Merge "libbinder: remove MemoryHeapBase::setDevice" am: 16794fd3fc am: 17800064e5 am: 9f4047c77a am: 9b3236fd0c am: 80245af0df
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1402750
Change-Id: I0ea1126e83ea93514d17d2485b6cef2f69de7ed1
diff --git a/.gitignore b/.gitignore
index 0d20b64..685e379 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
+*.iml
*.pyc
+.idea/
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 0473bb8..a686dfb 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,6 +6,7 @@
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/idlcli/
include/input/
+ include/powermanager/
libs/binder/fuzzer/
libs/binder/ndk/
libs/binderthreadstate/
@@ -18,7 +19,9 @@
opengl/libs/
services/bufferhub/
services/inputflinger/
+ services/powermanager/
services/surfaceflinger/
+ services/vibratorservice/
services/vr/
vulkan/
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 8173c89..5db44c7 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -58,6 +58,14 @@
},
{
"name": "libsurfaceflinger_unittest"
+ },
+ {
+ "name": "CtsGraphicsTestCases",
+ "options": [
+ {
+ "include-filter": "android.graphics.cts.VulkanPreTransformTest"
+ }
+ ]
}
]
}
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index ead491e..1f055f3 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -105,6 +105,7 @@
name: "dumpstate",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
"dumpstate.cpp",
"main.cpp",
],
@@ -132,6 +133,7 @@
name: "dumpstate_test",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
"dumpstate.cpp",
"tests/dumpstate_test.cpp",
],
@@ -148,10 +150,13 @@
name: "dumpstate_smoke_test",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
"dumpstate.cpp",
"tests/dumpstate_smoke_test.cpp",
],
static_libs: ["libgmock"],
+ test_config: "dumpstate_smoke_test.xml",
+ test_suites: ["device-tests"],
}
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
new file mode 100644
index 0000000..e174c8e
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 "dumpstate"
+
+#include "DumpPool.h"
+
+#include <array>
+#include <thread>
+
+#include <log/log.h>
+
+#include "dumpstate.h"
+#include "DumpstateInternal.h"
+#include "DumpstateUtil.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+const std::string DumpPool::PREFIX_TMPFILE_NAME = "dump-tmp.";
+
+DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false),
+ log_duration_(true) {
+ assert(!tmp_root.empty());
+ deleteTempFiles(tmp_root_);
+}
+
+DumpPool::~DumpPool() {
+ shutdown();
+}
+
+void DumpPool::start(int thread_counts) {
+ assert(thread_counts > 0);
+ assert(threads_.empty());
+ if (thread_counts > MAX_THREAD_COUNT) {
+ thread_counts = MAX_THREAD_COUNT;
+ }
+ MYLOGI("Start thread pool:%d", thread_counts);
+ shutdown_ = false;
+ for (int i = 0; i < thread_counts; i++) {
+ threads_.emplace_back(std::thread([=]() {
+ setThreadName(pthread_self(), i + 1);
+ loop();
+ }));
+ }
+}
+
+void DumpPool::shutdown() {
+ std::unique_lock lock(lock_);
+ if (shutdown_ || threads_.empty()) {
+ return;
+ }
+ while (!tasks_.empty()) tasks_.pop();
+ futures_map_.clear();
+
+ shutdown_ = true;
+ condition_variable_.notify_all();
+ lock.unlock();
+
+ for (auto& thread : threads_) {
+ thread.join();
+ }
+ threads_.clear();
+ deleteTempFiles(tmp_root_);
+ MYLOGI("shutdown thread pool");
+}
+
+void DumpPool::waitForTask(const std::string& task_name, const std::string& title,
+ int out_fd) {
+ DurationReporter duration_reporter("Wait for " + task_name, true);
+ auto iterator = futures_map_.find(task_name);
+ if (iterator == futures_map_.end()) {
+ MYLOGW("Task %s does not exist", task_name.c_str());
+ return;
+ }
+ Future future = iterator->second;
+ futures_map_.erase(iterator);
+
+ std::string result = future.get();
+ if (result.empty()) {
+ return;
+ }
+ DumpFileToFd(out_fd, title, result);
+ if (unlink(result.c_str())) {
+ MYLOGE("Failed to unlink (%s): %s\n", result.c_str(), strerror(errno));
+ }
+}
+
+void DumpPool::setLogDuration(bool log_duration) {
+ log_duration_ = log_duration;
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void()>>(std::function<void()> dump_func,
+ const std::string& duration_title, int out_fd) {
+ DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+ /*verbose =*/false, out_fd);
+ std::invoke(dump_func);
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void(int)>>(std::function<void(int)> dump_func,
+ const std::string& duration_title, int out_fd) {
+ DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+ /*verbose =*/false, out_fd);
+ std::invoke(dump_func, out_fd);
+}
+
+std::unique_ptr<DumpPool::TmpFile> DumpPool::createTempFile() {
+ auto tmp_file_ptr = std::make_unique<TmpFile>();
+ std::string file_name_format = "%s/" + PREFIX_TMPFILE_NAME + "XXXXXX";
+ snprintf(tmp_file_ptr->path, sizeof(tmp_file_ptr->path), file_name_format.c_str(),
+ tmp_root_.c_str());
+ tmp_file_ptr->fd.reset(TEMP_FAILURE_RETRY(
+ mkostemp(tmp_file_ptr->path, O_CLOEXEC)));
+ if (tmp_file_ptr->fd.get() == -1) {
+ MYLOGE("open(%s, %s)\n", tmp_file_ptr->path, strerror(errno));
+ tmp_file_ptr = nullptr;
+ return tmp_file_ptr;
+ }
+ return tmp_file_ptr;
+}
+
+void DumpPool::deleteTempFiles(const std::string& folder) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
+ &closedir);
+ if (!dir_ptr) {
+ MYLOGE("Failed to opendir (%s): %s\n", folder.c_str(), strerror(errno));
+ return;
+ }
+ int dir_fd = dirfd(dir_ptr.get());
+ if (dir_fd < 0) {
+ MYLOGE("Failed to get fd of dir (%s): %s\n", folder.c_str(),
+ strerror(errno));
+ return;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir_ptr.get()))) {
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+ std::string file_name(de->d_name);
+ if (file_name.find(PREFIX_TMPFILE_NAME) != 0) {
+ continue;
+ }
+ if (unlinkat(dir_fd, file_name.c_str(), 0)) {
+ MYLOGE("Failed to unlink (%s): %s\n", file_name.c_str(),
+ strerror(errno));
+ }
+ }
+}
+
+void DumpPool::setThreadName(const pthread_t thread, int id) {
+ std::array<char, 15> name;
+ snprintf(name.data(), name.size(), "dumpstate_%d", id);
+ pthread_setname_np(thread, name.data());
+}
+
+void DumpPool::loop() {
+ std::unique_lock lock(lock_);
+ while (!shutdown_) {
+ if (tasks_.empty()) {
+ condition_variable_.wait(lock);
+ continue;
+ } else {
+ std::packaged_task<std::string()> task = std::move(tasks_.front());
+ tasks_.pop();
+ lock.unlock();
+ std::invoke(task);
+ lock.lock();
+ }
+ }
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h
new file mode 100644
index 0000000..a4ea875
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.h
@@ -0,0 +1,201 @@
+/*
+ * 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_DUMPPOOL_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
+
+#include <future>
+#include <map>
+#include <queue>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+class DumpPoolTest;
+
+/*
+ * A thread pool with the fixed number of threads to execute multiple dump tasks
+ * simultaneously for the dumpstate. The dump task is a callable function. It
+ * could include a file descriptor as a parameter to redirect dump results, if
+ * it needs to output results to the bugreport. This can avoid messing up
+ * bugreport's results when multiple dump tasks are running at the same time.
+ * Takes an example below for the usage of the DumpPool:
+ *
+ * void DumpFoo(int out_fd) {
+ * dprintf(out_fd, "Dump result to out_fd ...");
+ * }
+ * ...
+ * DumpPool pool(tmp_root);
+ * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
+ * ...
+ * pool.waitForTask("TaskName");
+ *
+ * DumpFoo is a callable function included a out_fd parameter. Using the
+ * enqueueTaskWithFd method in DumpPool to enqueue the task to the pool. The
+ * std::placeholders::_1 is a placeholder for DumpPool to pass a fd argument.
+ */
+class DumpPool {
+ friend class android::os::dumpstate::DumpPoolTest;
+
+ public:
+ /*
+ * Creates a thread pool.
+ *
+ * |tmp_root| A path to a temporary folder for threads to create temporary
+ * files.
+ */
+ explicit DumpPool(const std::string& tmp_root);
+ ~DumpPool();
+
+ /*
+ * Starts the threads in the pool.
+ *
+ * |thread_counts| the number of threads to start.
+ */
+ void start(int thread_counts = MAX_THREAD_COUNT);
+
+ /*
+ * Requests to shutdown the pool and waits until all threads exit the loop.
+ */
+ void shutdown();
+
+ /*
+ * Adds a task into the queue of the thread pool.
+ *
+ * |task_name| The name of the task. It's also the title of the
+ * DurationReporter log.
+ * |f| Callable function to execute the task.
+ * |args| A list of arguments.
+ *
+ * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
+ */
+ template<class F, class... Args> void enqueueTask(const std::string& task_name, F&& f,
+ Args&&... args) {
+ std::function<void(void)> func = std::bind(std::forward<F>(f),
+ std::forward<Args>(args)...);
+ futures_map_[task_name] = post(task_name, func);
+ if (threads_.empty()) {
+ start();
+ }
+ }
+
+ /*
+ * Adds a task into the queue of the thread pool. The task takes a file
+ * descriptor as a parameter to redirect dump results to a temporary file.
+ *
+ * |task_name| The name of the task. It's also the title of the
+ * DurationReporter log.
+ * |f| Callable function to execute the task.
+ * |args| A list of arguments. A placeholder std::placeholders::_1 as a fd
+ * argument needs to be included here.
+ */
+ template<class F, class... Args> void enqueueTaskWithFd(const std::string& task_name, F&& f,
+ Args&&... args) {
+ std::function<void(int)> func = std::bind(std::forward<F>(f),
+ std::forward<Args>(args)...);
+ futures_map_[task_name] = post(task_name, func);
+ if (threads_.empty()) {
+ start();
+ }
+ }
+
+ /*
+ * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
+ */
+ void waitForTask(const std::string& task_name) {
+ waitForTask(task_name, "", STDOUT_FILENO);
+ }
+
+ /*
+ * Waits until the task is finished. Dumps the task results to the specified
+ * out_fd.
+ *
+ * |task_name| The name of the task.
+ * |title| Dump title string to the out_fd, an empty string for nothing.
+ * |out_fd| The target file to dump the result from the task.
+ */
+ void waitForTask(const std::string& task_name, const std::string& title, int out_fd);
+
+ static const std::string PREFIX_TMPFILE_NAME;
+
+ private:
+ using Task = std::packaged_task<std::string()>;
+ using Future = std::shared_future<std::string>;
+
+ template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
+
+ template<class T> Future post(const std::string& task_name, T dump_func) {
+ Task packaged_task([=]() {
+ std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
+ if (!tmp_file_ptr) {
+ return std::string("");
+ }
+ invokeTask(dump_func, task_name, tmp_file_ptr->fd.get());
+ fsync(tmp_file_ptr->fd.get());
+ return std::string(tmp_file_ptr->path);
+ });
+ std::unique_lock lock(lock_);
+ auto future = packaged_task.get_future().share();
+ tasks_.push(std::move(packaged_task));
+ condition_variable_.notify_one();
+ return future;
+ }
+
+ typedef struct {
+ android::base::unique_fd fd;
+ char path[1024];
+ } TmpFile;
+
+ std::unique_ptr<TmpFile> createTempFile();
+ void deleteTempFiles(const std::string& folder);
+ void setThreadName(const pthread_t thread, int id);
+ void loop();
+
+ /*
+ * For test purpose only. Enables or disables logging duration of the task.
+ *
+ * |log_duration| if true, DurationReporter is initiated to log duration of
+ * the task.
+ */
+ void setLogDuration(bool log_duration);
+
+ private:
+ static const int MAX_THREAD_COUNT = 4;
+
+ /* A path to a temporary folder for threads to create temporary files. */
+ std::string tmp_root_;
+ bool shutdown_;
+ bool log_duration_; // For test purpose only, the default value is true.
+ std::mutex lock_; // A lock for the tasks_.
+ std::condition_variable condition_variable_;
+
+ std::vector<std::thread> threads_;
+ std::queue<Task> tasks_;
+ std::map<std::string, Future> futures_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(DumpPool);
+};
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+#endif //FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index 4b69607..eeaa5a3 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -180,6 +180,7 @@
std::string PropertiesHelper::build_type_ = "";
int PropertiesHelper::dry_run_ = -1;
int PropertiesHelper::unroot_ = -1;
+int PropertiesHelper::parallel_run_ = -1;
bool PropertiesHelper::IsUserBuild() {
if (build_type_.empty()) {
@@ -202,6 +203,14 @@
return unroot_ == 1;
}
+bool PropertiesHelper::IsParallelRun() {
+ if (parallel_run_ == -1) {
+ parallel_run_ = android::base::GetBoolProperty("dumpstate.parallel_run",
+ /* default_value = */true) ? 1 : 0;
+ }
+ return parallel_run_ == 1;
+}
+
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
if (fd.get() < 0) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index b7ac25c..b099443 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -176,10 +176,18 @@
*/
static bool IsUnroot();
+ /*
+ * Whether or not the parallel run is enabled. Setting the system property
+ * 'dumpstate.parallel_run' to false to disable it, otherwise it returns
+ * true by default.
+ */
+ static bool IsParallelRun();
+
private:
static std::string build_type_;
static int dry_run_;
static int unroot_;
+ static int parallel_run_;
};
/*
diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING
index 083944f..839a2c3 100644
--- a/cmds/dumpstate/TEST_MAPPING
+++ b/cmds/dumpstate/TEST_MAPPING
@@ -1,7 +1,28 @@
{
"presubmit": [
{
+ "name": "BugreportManagerTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ },
+ {
+ "name": "dumpstate_smoke_test"
+ },
+ {
"name": "dumpstate_test"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "BugreportManagerTestCases"
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/packages/Shell"
+ }
]
-}
\ No newline at end of file
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index af41643..7d195b4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -95,6 +95,7 @@
using ::android::hardware::dumpstate::V1_1::toString;
using ::std::literals::chrono_literals::operator""ms;
using ::std::literals::chrono_literals::operator""s;
+using ::std::placeholders::_1;
// TODO: remove once moved to namespace
using android::defaultServiceManager;
@@ -113,6 +114,7 @@
using android::os::IDumpstateListener;
using android::os::dumpstate::CommandOptions;
using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::DumpPool;
using android::os::dumpstate::PropertiesHelper;
// Keep in sync with
@@ -196,8 +198,26 @@
func_ptr(__VA_ARGS__); \
RETURN_IF_USER_DENIED_CONSENT();
+// Runs func_ptr, and logs a duration report after it's finished.
+#define RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, ...) \
+ { \
+ DurationReporter duration_reporter_in_macro(log_title); \
+ func_ptr(__VA_ARGS__); \
+ }
+
+// Similar with RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK, an additional duration report
+// is output after a slow function is finished.
+#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(log_title, func_ptr, ...) \
+ RETURN_IF_USER_DENIED_CONSENT(); \
+ RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, __VA_ARGS__); \
+ 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";
+
namespace android {
namespace os {
namespace {
@@ -762,8 +782,9 @@
RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
CommandOptions::WithTimeout(1).Always().Build());
printf("Bugreport format version: %s\n", version_.c_str());
- printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s bugreport_mode=%s\n", id_, pid_,
- PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->bugreport_mode.c_str());
+ printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n",
+ id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
+ options_->args.c_str(), options_->bugreport_mode.c_str());
printf("\n");
}
@@ -1680,7 +1701,18 @@
time_t logcat_ts = time(nullptr);
/* collect stack traces from Dalvik and native processes (needs root) */
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path);
+ if (dump_pool_) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ // One thread is enough since we only need to enqueue DumpTraces here.
+ dump_pool_->start(/* thread_counts = */1);
+
+ // DumpTraces takes long time, post it to the another thread in the
+ // pool, if pool is available
+ dump_pool_->enqueueTask(DUMP_TRACES_TASK, &Dumpstate::DumpTraces, &ds, &dump_traces_path);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_TRACES_TASK, ds.DumpTraces,
+ &dump_traces_path);
+ }
/* Run some operations that require root. */
ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
@@ -1723,6 +1755,15 @@
DumpFile("PSI memory", "/proc/pressure/memory");
DumpFile("PSI io", "/proc/pressure/io");
+ if (dump_pool_) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ dump_pool_->waitForTask(DUMP_TRACES_TASK);
+
+ // Current running thread in the pool is the root user also. Shutdown
+ // the pool and restart later to ensure all threads in the pool could
+ // drop the root user.
+ dump_pool_->shutdown();
+ }
if (!DropRootUser()) {
return Dumpstate::RunStatus::ERROR;
}
@@ -1881,8 +1922,6 @@
}
Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
- DurationReporter duration_reporter("DUMP TRACES");
-
const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
const size_t buf_size = temp_file_pattern.length() + 1;
std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
@@ -2554,6 +2593,7 @@
*/
Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
const std::string& calling_package) {
+ DurationReporter duration_reporter("RUN INTERNAL", /* logcat_only = */true);
LogDumpOptions(*options_);
if (!options_->ValidateOptions()) {
MYLOGE("Invalid options specified\n");
@@ -2715,6 +2755,13 @@
// Don't buffer stdout
setvbuf(stdout, nullptr, _IONBF, 0);
+ // Enable the parallel run if the client requests to output to a file.
+ EnableParallelRunIfNeeded();
+ // Using scope guard to make sure the dump pool can be shut down correctly.
+ auto scope_guard_to_shutdown_pool = android::base::make_scope_guard([=]() {
+ ShutdownDumpPool();
+ });
+
// NOTE: there should be no stdout output until now, otherwise it would break the header.
// In particular, DurationReport objects should be created passing 'title, NULL', so their
// duration is logged into MYLOG instead.
@@ -2881,6 +2928,23 @@
android::os::UnlinkAndLogOnError(path_);
}
+void Dumpstate::EnableParallelRunIfNeeded() {
+ // The thread pool needs to create temporary files to receive dump results.
+ // That's why we only enable it when the bugreport client chooses to output
+ // to a file.
+ if (!PropertiesHelper::IsParallelRun() || !options_->OutputToFile()) {
+ return;
+ }
+ dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
+}
+
+void Dumpstate::ShutdownDumpPool() {
+ if (dump_pool_) {
+ dump_pool_->shutdown();
+ dump_pool_ = nullptr;
+ }
+}
+
Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
MYLOGD("User denied consent; deleting files and returning\n");
CleanupTmpFiles();
@@ -2999,8 +3063,9 @@
return singleton_;
}
-DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose)
- : title_(title), logcat_only_(logcat_only), verbose_(verbose) {
+DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose,
+ int duration_fd) : title_(title), logcat_only_(logcat_only), verbose_(verbose),
+ duration_fd_(duration_fd) {
if (!title_.empty()) {
started_ = Nanotime();
}
@@ -3014,7 +3079,8 @@
}
if (!logcat_only_) {
// Use "Yoda grammar" to make it easier to grep|sort sections.
- printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str());
+ dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n",
+ elapsed, title_.c_str());
}
}
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0d25d30..d400dc7 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -35,6 +35,7 @@
#include <ziparchive/zip_writer.h>
#include "DumpstateUtil.h"
+#include "DumpPool.h"
// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
// std::vector<std::string>
@@ -75,7 +76,7 @@
class DurationReporter {
public:
explicit DurationReporter(const std::string& title, bool logcat_only = false,
- bool verbose = false);
+ bool verbose = false, int duration_fd = STDOUT_FILENO);
~DurationReporter();
@@ -84,6 +85,7 @@
bool logcat_only_;
bool verbose_;
uint64_t started_;
+ int duration_fd_;
DISALLOW_COPY_AND_ASSIGN(DurationReporter);
};
@@ -193,7 +195,7 @@
* that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
*/
class Dumpstate {
- friend class DumpstateTest;
+ friend class android::os::dumpstate::DumpstateTest;
public:
enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
@@ -490,6 +492,9 @@
// List of open ANR dump files.
std::vector<DumpData> anr_data_;
+ // A thread pool to execute dump tasks simultaneously if the parallel run is enabled.
+ std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_;
+
// 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.
@@ -528,6 +533,10 @@
// but leaves the log file alone.
void CleanupTmpFiles();
+ // Create the thread pool to enable the parallel run function.
+ void EnableParallelRunIfNeeded();
+ void ShutdownDumpPool();
+
RunStatus HandleUserConsentDenied();
// Copies bugreport artifacts over to the caller's directories provided there is user consent or
diff --git a/cmds/dumpstate/dumpstate_smoke_test.xml b/cmds/dumpstate/dumpstate_smoke_test.xml
new file mode 100644
index 0000000..0aff200
--- /dev/null
+++ b/cmds/dumpstate/dumpstate_smoke_test.xml
@@ -0,0 +1,31 @@
+<?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 dumpstate_smoke_test">
+ <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="dumpstate_smoke_test->/data/local/tmp/dumpstate_smoke_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="dumpstate_smoke_test" />
+ <option name="native-test-timeout" value="600000" />
+ </test>
+</configuration>
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 6f2d754..bb0e5ad 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -209,13 +209,12 @@
static std::shared_ptr<std::vector<SectionInfo>> sections;
static Dumpstate& ds;
static std::chrono::milliseconds duration;
- static void SetUpTestCase() {
+ static void GenerateBugreport() {
// clang-format off
char* argv[] = {
(char*)"dumpstate",
(char*)"-d",
- (char*)"-z",
- (char*)"-B"
+ (char*)"-z"
};
// clang-format on
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
@@ -236,20 +235,20 @@
std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
+ GenerateBugreport();
EXPECT_EQ(access(getZipFilePath().c_str(), F_OK), 0);
}
-TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) {
+TEST_F(ZippedBugreportGenerationTest, Is3MBMBinSize) {
struct stat st;
EXPECT_EQ(stat(getZipFilePath().c_str(), &st), 0);
EXPECT_GE(st.st_size, 3000000 /* 3MB */);
- EXPECT_LE(st.st_size, 30000000 /* 30MB */);
}
-TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) {
+TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) {
EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
<< duration.count() << " s.";
- EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time "
+ EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
<< duration.count() << " s.";
}
@@ -266,7 +265,8 @@
CloseArchive(handle);
}
- void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
+ void FileExists(const char* filename, uint32_t minsize,
+ uint32_t maxsize = std::numeric_limits<uint32_t>::max()) {
ZipEntry entry;
GetEntry(handle, filename, &entry);
EXPECT_GT(entry.uncompressed_length, minsize);
@@ -285,7 +285,7 @@
main_entry.uncompressed_length);
// contains main entry file
- FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U);
+ FileExists(bugreport_txt_name.c_str(), 1000000U);
}
TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
@@ -301,8 +301,9 @@
}
TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
- FileExists("dumpstate_board.bin", 1000000U, 80000000U);
- FileExists("dumpstate_board.txt", 100000U, 1000000U);
+ // TODO(b/160109027): cf_x86_phone-userdebug does not dump them.
+ // FileExists("dumpstate_board.bin", 1000000U, 80000000U);
+ // FileExists("dumpstate_board.txt", 100000U, 1000000U);
}
TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index c7df1bb..b3cb434 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -21,6 +21,7 @@
#include "DumpstateService.h"
#include "android/os/BnDumpstate.h"
#include "dumpstate.h"
+#include "DumpPool.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -46,6 +47,7 @@
using ::android::hardware::dumpstate::V1_1::DumpstateMode;
using ::testing::EndsWith;
+using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::IsEmpty;
using ::testing::IsNull;
@@ -95,6 +97,10 @@
PropertiesHelper::unroot_ = unroot;
}
+ void SetParallelRun(bool parallel_run) const {
+ PropertiesHelper::parallel_run_ = parallel_run;
+ }
+
bool IsStandalone() const {
return calls_ == 1;
}
@@ -542,6 +548,10 @@
ds.options_.reset(new Dumpstate::DumpOptions());
}
+ void TearDown() {
+ ds.ShutdownDumpPool();
+ }
+
// Runs a command and capture `stdout` and `stderr`.
int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options = CommandOptions::DEFAULT) {
@@ -569,6 +579,10 @@
ds.progress_.reset(new Progress(initial_max, progress, 1.2));
}
+ void EnableParallelRunIfNeeded() {
+ ds.EnableParallelRunIfNeeded();
+ }
+
std::string GetProgressMessage(int progress, int max,
int old_max = 0, bool update_progress = true) {
EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
@@ -1007,6 +1021,27 @@
ds.listener_.clear();
}
+TEST_F(DumpstateTest, DumpPool_withOutputToFileAndParallelRunEnabled_notNull) {
+ ds.options_->use_socket = false;
+ SetParallelRun(true);
+ EnableParallelRunIfNeeded();
+ EXPECT_TRUE(ds.options_->OutputToFile());
+ EXPECT_TRUE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withNotOutputToFile_isNull) {
+ ds.options_->use_socket = true;
+ EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.options_->OutputToFile());
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) {
+ SetParallelRun(false);
+ EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
class DumpstateServiceTest : public DumpstateBaseTest {
public:
DumpstateService dss;
@@ -1618,6 +1653,103 @@
EXPECT_THAT(out, EndsWith("skipped on dry run\n"));
}
+class DumpPoolTest : public DumpstateBaseTest {
+ public:
+ void SetUp() {
+ dump_pool_ = std::make_unique<DumpPool>(kTestDataPath);
+ DumpstateBaseTest::SetUp();
+ CreateOutputFile();
+ }
+
+ void CreateOutputFile() {
+ out_path_ = kTestDataPath + "out.txt";
+ out_fd_.reset(TEMP_FAILURE_RETRY(open(out_path_.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+ ASSERT_GE(out_fd_.get(), 0) << "could not create FD for path "
+ << out_path_;
+ }
+
+ int getTempFileCounts(const std::string& folder) {
+ int count = 0;
+ std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
+ &closedir);
+ if (!dir_ptr) {
+ return -1;
+ }
+ int dir_fd = dirfd(dir_ptr.get());
+ if (dir_fd < 0) {
+ return -1;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir_ptr.get()))) {
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+ std::string file_name(de->d_name);
+ if (file_name.find(DumpPool::PREFIX_TMPFILE_NAME) != 0) {
+ continue;
+ }
+ count++;
+ }
+ return count;
+ }
+
+ void setLogDuration(bool log_duration) {
+ dump_pool_->setLogDuration(log_duration);
+ }
+
+ std::unique_ptr<DumpPool> dump_pool_;
+ android::base::unique_fd out_fd_;
+ std::string out_path_;
+};
+
+TEST_F(DumpPoolTest, EnqueueTaskWithFd) {
+ auto dump_func_1 = [](int out_fd) {
+ dprintf(out_fd, "A");
+ };
+ auto dump_func_2 = [](int out_fd) {
+ dprintf(out_fd, "B");
+ sleep(1);
+ };
+ auto dump_func_3 = [](int out_fd) {
+ dprintf(out_fd, "C");
+ };
+ setLogDuration(/* log_duration = */false);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"1", dump_func_1, std::placeholders::_1);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"2", dump_func_2, std::placeholders::_1);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"3", dump_func_3, std::placeholders::_1);
+
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->waitForTask("2", "", out_fd_.get());
+ dump_pool_->waitForTask("3", "", out_fd_.get());
+ dump_pool_->shutdown();
+
+ std::string result;
+ ReadFileToString(out_path_, &result);
+ EXPECT_THAT(result, StrEq("A\nB\nC\n"));
+ EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
+TEST_F(DumpPoolTest, EnqueueTask_withDurationLog) {
+ bool run_1 = false;
+ auto dump_func_1 = [&]() {
+ run_1 = true;
+ };
+
+ dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->shutdown();
+
+ std::string result;
+ ReadFileToString(out_path_, &result);
+ EXPECT_TRUE(run_1);
+ EXPECT_THAT(result, StrEq("------ 0.000s was the duration of '1' ------\n"));
+ EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
+
} // namespace dumpstate
} // namespace os
} // namespace android
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 402767a..64bfdf9 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -37,10 +37,16 @@
defaults: ["idlcli-defaults"],
srcs: [
"CommandVibrator.cpp",
+ "vibrator/CommandAlwaysOnDisable.cpp",
+ "vibrator/CommandAlwaysOnEnable.cpp",
"vibrator/CommandCompose.cpp",
"vibrator/CommandGetCapabilities.cpp",
"vibrator/CommandGetCompositionDelayMax.cpp",
"vibrator/CommandGetCompositionSizeMax.cpp",
+ "vibrator/CommandGetPrimitiveDuration.cpp",
+ "vibrator/CommandGetSupportedAlwaysOnEffects.cpp",
+ "vibrator/CommandGetSupportedEffects.cpp",
+ "vibrator/CommandGetSupportedPrimitives.cpp",
"vibrator/CommandOff.cpp",
"vibrator/CommandOn.cpp",
"vibrator/CommandPerform.cpp",
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
index a8e5954..b874455 100644
--- a/cmds/idlcli/utils.h
+++ b/cmds/idlcli/utils.h
@@ -17,6 +17,7 @@
#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
+#include <android/binder_enums.h>
#include <hidl/HidlSupport.h>
#include <iomanip>
@@ -66,7 +67,7 @@
} // namespace overrides
-template <typename T, typename R = hardware::hidl_enum_range<T>>
+template <typename T, typename R = ndk::enum_range<T>>
inline std::istream &operator>>(std::istream &stream, T &out) {
using overrides::operator>>;
auto validRange = R();
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index ca5142d..6c30a9e 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -16,8 +16,12 @@
#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#include <future>
+
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
#include "utils.h"
@@ -101,6 +105,18 @@
namespace V1_3 = ::android::hardware::vibrator::V1_3;
namespace aidl = ::aidl::android::hardware::vibrator;
+class VibratorCallback : public aidl::BnVibratorCallback {
+public:
+ ndk::ScopedAStatus onComplete() override {
+ mPromise.set_value();
+ return ndk::ScopedAStatus::ok();
+ }
+ void waitForComplete() { mPromise.get_future().wait(); }
+
+private:
+ std::promise<void> mPromise;
+};
+
} // namespace vibrator
} // namespace idlcli
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
new file mode 100644
index 0000000..9afa300
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandAlwaysOnDisable : public Command {
+ std::string getDescription() const override { return "Disarm always-on haptic source."; }
+
+ std::string getUsageSummary() const override { return "<id>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<id>", {"Source ID (device-specific)."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto id = args.pop<decltype(mId)>()) {
+ mId = *id;
+ std::cout << "Source ID: " << mId << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Source ID!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::alwaysOnDisable, mId);
+
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+
+ return ret;
+ }
+
+ int32_t mId;
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnDisable>("alwaysOnDisable");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
new file mode 100644
index 0000000..bb7f9f2
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+using aidl::EffectStrength;
+
+class CommandAlwaysOnEnable : public Command {
+ std::string getDescription() const override {
+ return "Arm always-on haptic source with an effect.";
+ }
+
+ std::string getUsageSummary() const override { return "<id> <effect> <strength>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<id>", {"Source ID (device-specific)."}},
+ {"<effect>", {"Effect ID."}},
+ {"<strength>", {"0-2."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto id = args.pop<decltype(mId)>()) {
+ mId = *id;
+ std::cout << "Source ID: " << mId << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Source ID!" << std::endl;
+ return USAGE;
+ }
+ if (auto effect = args.pop<decltype(mEffect)>()) {
+ mEffect = *effect;
+ std::cout << "Effect: " << toString(mEffect) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Effect!" << std::endl;
+ return USAGE;
+ }
+ if (auto strength = args.pop<decltype(mStrength)>()) {
+ mStrength = *strength;
+ std::cout << "Strength: " << toString(mStrength) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Strength!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::alwaysOnEnable, mId, mEffect, mStrength);
+
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+
+ return ret;
+ }
+
+ int32_t mId;
+ Effect mEffect;
+ EffectStrength mStrength;
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnEnable>("alwaysOnEnable");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 4721a5f..eb9008b 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -28,19 +28,33 @@
class CommandCompose : public Command {
std::string getDescription() const override { return "Compose vibration."; }
- std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; }
+ std::string getUsageSummary() const override {
+ return "[options] <delay> <primitive> <scale> ...";
+ }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<delay>", {"In milliseconds"}},
{"<primitive>", {"Primitive ID."}},
- {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}},
+ {"<scale>", {"0.0 (inclusive) - 1.0 (inclusive)."}},
{"...", {"May repeat multiple times."}},
};
return details;
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
while (!args.empty()) {
CompositeEffect effect;
if (auto delay = args.pop<decltype(effect.delayMs)>()) {
@@ -50,16 +64,15 @@
std::cerr << "Missing or Invalid Delay!" << std::endl;
return USAGE;
}
- // TODO: Use range validation when supported by AIDL
- if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) {
- effect.primitive = static_cast<decltype(effect.primitive)>(*primitive);
+ if (auto primitive = args.pop<decltype(effect.primitive)>()) {
+ effect.primitive = *primitive;
std::cout << "Primitive: " << toString(effect.primitive) << std::endl;
} else {
std::cerr << "Missing or Invalid Primitive!" << std::endl;
return USAGE;
}
if (auto scale = args.pop<decltype(effect.scale)>();
- scale && *scale > 0.0 && scale <= 1.0) {
+ scale && *scale >= 0.0 && scale <= 1.0) {
effect.scale = *scale;
std::cout << "Scale: " << effect.scale << std::endl;
} else {
@@ -76,21 +89,33 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- Status ret;
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ auto hal = getHal<aidl::IVibrator>();
+
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
- return ret;
+ std::shared_ptr<VibratorCallback> callback;
+
+ if (mBlocking) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback);
+
+ if (status.isOk() && callback) {
+ callback->waitForComplete();
+ }
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
+ bool mBlocking;
std::vector<CompositeEffect> mComposite;
};
diff --git a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
new file mode 100644
index 0000000..460d39e
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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 <future>
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePrimitive;
+
+class CommandGetPrimitiveDuration : public Command {
+ std::string getDescription() const override {
+ return "Retrieve effect primitive's duration in milliseconds.";
+ }
+
+ std::string getUsageSummary() const override { return "<primitive>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<primitive>", {"Primitive ID."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto primitive = args.pop<decltype(mPrimitive)>()) {
+ mPrimitive = *primitive;
+ std::cout << "Primitive: " << toString(mPrimitive) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Primitive!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t duration;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getPrimitiveDuration, mPrimitive, &duration);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Duration: " << duration << std::endl;
+
+ return ret;
+ }
+
+ CompositePrimitive mPrimitive;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetPrimitiveDuration>(
+ "getPrimitiveDuration");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
new file mode 100644
index 0000000..edfcd91
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+
+class CommandGetSupportedAlwaysOnEffects : public Command {
+ std::string getDescription() const override { return "List of supported always-on effects."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<Effect> effects;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedAlwaysOnEffects, &effects);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Effects:" << std::endl;
+ for (auto &e : effects) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetSupportedAlwaysOnEffects>(
+ "getSupportedAlwaysOnEffects");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
new file mode 100644
index 0000000..7658f22
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+
+class CommandGetSupportedEffects : public Command {
+ std::string getDescription() const override { return "List supported effects."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<Effect> effects;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedEffects, &effects);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Effects:" << std::endl;
+ for (auto &e : effects) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetSupportedEffects>(
+ "getSupportedEffects");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
new file mode 100644
index 0000000..d101681
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePrimitive;
+
+class CommandGetSupportedPrimitives : public Command {
+ std::string getDescription() const override { return "List of supported effect primitive."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<CompositePrimitive> primitives;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedPrimitives, &primitives);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Primitives:" << std::endl;
+ for (auto &e : primitives) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetSupportedPrimitives>(
+ "getSupportedPrimitives");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index 4e7e493..8212fc1 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -13,9 +13,14 @@
* limitations under the License.
*/
+#include <thread>
+
#include "utils.h"
#include "vibrator.h"
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
namespace android {
namespace idlcli {
@@ -26,16 +31,28 @@
class CommandOn : public Command {
std::string getDescription() const override { return "Turn on vibrator."; }
- std::string getUsageSummary() const override { return "<duration>"; }
+ std::string getUsageSummary() const override { return "[options] <duration>"; }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<duration>", {"In milliseconds."}},
};
return details;
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
if (auto duration = args.pop<decltype(mDuration)>()) {
mDuration = *duration;
} else {
@@ -52,9 +69,21 @@
Status doMain(Args && /*args*/) override {
std::string statusStr;
Status ret;
+ std::shared_ptr<VibratorCallback> callback;
if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ int32_t cap;
+ hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+ if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ auto status = hal->call(&aidl::IVibrator::on, mDuration, callback);
+
statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
@@ -65,11 +94,20 @@
return UNAVAILABLE;
}
+ if (ret == OK && mBlocking) {
+ if (callback) {
+ callback->waitForComplete();
+ } else {
+ sleep_for(milliseconds(mDuration));
+ }
+ }
+
std::cout << "Status: " << statusStr << std::endl;
return ret;
}
+ bool mBlocking;
uint32_t mDuration;
};
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 69c7e37..c897686 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -13,9 +13,14 @@
* limitations under the License.
*/
+#include <thread>
+
#include "utils.h"
#include "vibrator.h"
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
namespace android {
namespace idlcli {
@@ -51,16 +56,17 @@
static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
-using V1_0::EffectStrength;
-using V1_3::Effect;
+using aidl::Effect;
+using aidl::EffectStrength;
class CommandPerform : public Command {
std::string getDescription() const override { return "Perform vibration effect."; }
- std::string getUsageSummary() const override { return "<effect> <strength>"; }
+ std::string getUsageSummary() const override { return "[options] <effect> <strength>"; }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<effect>", {"Effect ID."}},
{"<strength>", {"0-2."}},
};
@@ -68,6 +74,17 @@
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
if (auto effect = args.pop<decltype(mEffect)>()) {
mEffect = *effect;
std::cout << "Effect: " << toString(mEffect) << std::endl;
@@ -93,12 +110,23 @@
std::string statusStr;
uint32_t lengthMs;
Status ret;
+ std::shared_ptr<VibratorCallback> callback;
if (auto hal = getHal<aidl::IVibrator>()) {
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ int32_t cap;
+ hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+ if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
int32_t aidlLengthMs;
- auto status =
- hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
- static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
+ auto status = hal->call(&aidl::IVibrator::perform, mEffect, mStrength, callback,
+ &aidlLengthMs);
+
statusStr = status.getDescription();
lengthMs = static_cast<uint32_t>(aidlLengthMs);
ret = status.isOk() ? OK : ERROR;
@@ -111,17 +139,20 @@
};
if (auto hal = getHal<V1_3::IVibrator>()) {
- hidlRet = hal->call(&V1_3::IVibrator::perform_1_3,
- static_cast<V1_3::Effect>(mEffect), mStrength, callback);
+ hidlRet =
+ hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else if (auto hal = getHal<V1_2::IVibrator>()) {
- hidlRet = hal->call(&V1_2::IVibrator::perform_1_2,
- static_cast<V1_2::Effect>(mEffect), mStrength, callback);
+ hidlRet =
+ hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else if (auto hal = getHal<V1_1::IVibrator>()) {
hidlRet = hal->call(&V1_1::IVibrator::perform_1_1,
- static_cast<V1_1::Effect_1_1>(mEffect), mStrength, callback);
+ static_cast<V1_1::Effect_1_1>(mEffect),
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else if (auto hal = getHal<V1_0::IVibrator>()) {
hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
- mStrength, callback);
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else {
return UNAVAILABLE;
}
@@ -130,12 +161,21 @@
ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR;
}
+ if (ret == OK && mBlocking) {
+ if (callback) {
+ callback->waitForComplete();
+ } else {
+ sleep_for(milliseconds(lengthMs));
+ }
+ }
+
std::cout << "Status: " << statusStr << std::endl;
std::cout << "Length: " << lengthMs << std::endl;
return ret;
}
+ bool mBlocking;
Effect mEffect;
EffectStrength mStrength;
};
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index be0c062..17015a4 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -189,8 +189,7 @@
filegroup {
name: "installd_aidl",
srcs: [
- "binder/android/os/IInstalld.aidl",
- "binder/android/os/storage/CrateMetadata.aidl",
+ "binder/**/*.aidl",
],
path: "binder",
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 0782b43..eb1bbd9 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -420,33 +420,6 @@
return true;
}
-binder::Status InstalldNativeService::createAppDataBatched(
- const std::optional<std::vector<std::optional<std::string>>>& uuids,
- const std::optional<std::vector<std::optional<std::string>>>& packageNames,
- int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
- const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
- int64_t* _aidl_return) {
- ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
- ATRACE_BEGIN("createAppDataBatched");
- binder::Status ret;
- for (size_t i = 0; i < uuids->size(); i++) {
- std::optional<std::string> packageName = packageNames->at(i);
- if (!packageName) {
- continue;
- }
- ret = createAppData(uuids->at(i), *packageName, userId, flags, appIds[i],
- seInfos[i], targetSdkVersions[i], _aidl_return);
- if (!ret.isOk()) {
- ATRACE_END();
- return ret;
- }
- }
- ATRACE_END();
- return ok();
-}
-
binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
@@ -528,6 +501,38 @@
return ok();
}
+
+binder::Status InstalldNativeService::createAppData(
+ const android::os::CreateAppDataArgs& args,
+ android::os::CreateAppDataResult* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ int64_t ceDataInode = -1;
+ auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId,
+ args.seInfo, args.targetSdkVersion, &ceDataInode);
+ _aidl_return->ceDataInode = ceDataInode;
+ _aidl_return->exceptionCode = status.exceptionCode();
+ _aidl_return->exceptionMessage = status.exceptionMessage();
+ return ok();
+}
+
+binder::Status InstalldNativeService::createAppDataBatched(
+ const std::vector<android::os::CreateAppDataArgs>& args,
+ std::vector<android::os::CreateAppDataResult>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ std::vector<android::os::CreateAppDataResult> results;
+ for (auto arg : args) {
+ android::os::CreateAppDataResult result;
+ createAppData(arg, &result);
+ results.push_back(result);
+ }
+ *_aidl_return = results;
+ return ok();
+}
+
binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
@@ -2246,7 +2251,7 @@
#if CRATE_DEBUG
LOG(WARNING) << "retVector.size() =" << retVector.size();
for (auto& item : retVector) {
- CrateManager::dump(item);
+ CrateManager::dump(*item);
}
#endif
@@ -2278,7 +2283,7 @@
if (cratedFolder == nullptr) {
return;
}
- retVector->push_back(std::move(crateMetadata));
+ retVector.push_back(std::move(crateMetadata));
};
std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void {
@@ -2290,7 +2295,7 @@
#if CRATE_DEBUG
LOG(DEBUG) << "retVector.size() =" << retVector.size();
for (auto& item : retVector) {
- CrateManager::dump(item);
+ CrateManager::dump(*item);
}
#endif
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 9819327..4966b96 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -44,15 +44,18 @@
int32_t userSerial, int32_t flags);
binder::Status destroyUserData(const std::optional<std::string>& uuid, int32_t userId,
int32_t flags);
- binder::Status createAppDataBatched(
- const std::optional<std::vector<std::optional<std::string>>>& uuids,
- const std::optional<std::vector<std::optional<std::string>>>& packageNames,
- int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
- const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
- int64_t* _aidl_return);
+
binder::Status createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+
+ binder::Status createAppData(
+ const android::os::CreateAppDataArgs& args,
+ android::os::CreateAppDataResult* _aidl_return);
+ binder::Status createAppDataBatched(
+ const std::vector<android::os::CreateAppDataArgs>& args,
+ std::vector<android::os::CreateAppDataResult>* _aidl_return);
+
binder::Status restoreconAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
copy to cmds/installd/binder/android/os/CreateAppDataArgs.aidl
index f9bacc8..96d7faa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
+++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#include "mock/MockEventControlThread.h"
+package android.os;
-namespace android {
-namespace mock {
-
-// Explicit default instantiation is recommended.
-EventControlThread::EventControlThread() = default;
-EventControlThread::~EventControlThread() = default;
-
-} // namespace mock
-} // namespace android
+/** {@hide} */
+parcelable CreateAppDataArgs {
+ @nullable @utf8InCpp String uuid;
+ @utf8InCpp String packageName;
+ int userId;
+ int flags;
+ int appId;
+ @utf8InCpp String seInfo;
+ int targetSdkVersion;
+}
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
similarity index 62%
rename from services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
rename to cmds/installd/binder/android/os/CreateAppDataResult.aidl
index f9bacc8..3b8fa6b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp
+++ b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-#include "mock/MockEventControlThread.h"
+package android.os;
-namespace android {
-namespace mock {
-
-// Explicit default instantiation is recommended.
-EventControlThread::EventControlThread() = default;
-EventControlThread::~EventControlThread() = default;
-
-} // namespace mock
-} // namespace android
+/** {@hide} */
+parcelable CreateAppDataResult {
+ long ceDataInode;
+ int exceptionCode;
+ @utf8InCpp String exceptionMessage;
+}
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 4ac70a4..2538e22 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -21,11 +21,9 @@
void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags);
void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags);
- long createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
- int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion);
- long createAppDataBatched(in @nullable @utf8InCpp String[] uuids,
- in @nullable @utf8InCpp String[] packageNames, in int userId, int flags, in int[] appIds,
- in @utf8InCpp String[] seInfos, in int[] targetSdkVersions);
+ android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args);
+ android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args);
+
void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, int flags, int appId, @utf8InCpp String seInfo);
void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
@@ -117,10 +115,11 @@
int userId, int snapshotId, int storageFlags);
void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags);
- void destroyCeSnapshotsNotSpecified(@nullable @utf8InCpp String uuid, int userId,
- in int[] retainSnapshotIds);
void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, long ceSnapshotInode, int snapshotId, int storageFlags);
+ void destroyCeSnapshotsNotSpecified(@nullable @utf8InCpp String uuid, int userId,
+ in int[] retainSnapshotIds);
+
void tryMountDataMirror(@nullable @utf8InCpp String volumeUuid);
void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid);
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index b574098..3ada05c 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -25,8 +25,9 @@
repeated SurfaceChange surface_change = 1;
repeated DisplayChange display_change = 2;
- required bool synchronous = 3;
- required bool animation = 4;
+ required bool synchronous = 3;
+ required bool animation = 4;
+ optional Origin origin = 5;
}
message SurfaceChange {
@@ -208,4 +209,9 @@
message ShadowRadiusChange {
required float radius = 1;
+}
+
+message Origin {
+ required int32 pid = 1;
+ required int32 uid = 2;
}
\ No newline at end of file
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
new file mode 100644
index 0000000..83b6aa0
--- /dev/null
+++ b/data/etc/Android.bp
@@ -0,0 +1,7 @@
+prebuilt_etc {
+ name: "android.hardware.biometrics.face.xml",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "android.hardware.biometrics.face.xml",
+ filename_from_src: true,
+}
\ No newline at end of file
diff --git a/include/android/input.h b/include/android/input.h
index dbfd61e..7c39234 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -55,6 +55,7 @@
#include <sys/types.h>
#include <android/keycodes.h>
#include <android/looper.h>
+#include <jni.h>
#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
@@ -931,6 +932,15 @@
/** Get the input event source. */
int32_t AInputEvent_getSource(const AInputEvent* event);
+/**
+ * Releases interface objects created by {@link AKeyEvent_fromJava()}
+ * and {@link AMotionEvent_fromJava()}.
+ * After returning, the specified AInputEvent* object becomes invalid and should no longer be used.
+ * The underlying Java object remains valid and does not change its state.
+ */
+
+void AInputEvent_release(const AInputEvent* event);
+
/*** Accessors for key events only. ***/
/** Get the key event action. */
@@ -977,6 +987,15 @@
*/
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*.
+ */
+const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent);
+
/*** Accessors for motion events only. ***/
/** Get the combined motion event action code and pointer index. */
@@ -1292,6 +1311,14 @@
float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
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*.
+ */
+const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent);
struct AInputQueue;
/**
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 2427a07..334fe34 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -17,12 +17,12 @@
#ifndef _LIBINPUT_DISPLAY_VIEWPORT_H
#define _LIBINPUT_DISPLAY_VIEWPORT_H
-#include <cinttypes>
-#include <optional>
-
#include <android-base/stringprintf.h>
#include <input/Input.h>
+#include <cinttypes>
+#include <optional>
+
using android::base::StringPrintf;
namespace android {
@@ -39,22 +39,21 @@
* Keep in sync with values in InputManagerService.java.
*/
enum class ViewportType : int32_t {
- VIEWPORT_INTERNAL = 1,
- VIEWPORT_EXTERNAL = 2,
- VIEWPORT_VIRTUAL = 3,
+ INTERNAL = 1,
+ EXTERNAL = 2,
+ VIRTUAL = 3,
};
static const char* viewportTypeToString(ViewportType type) {
- switch(type) {
- case ViewportType::VIEWPORT_INTERNAL:
+ switch (type) {
+ case ViewportType::INTERNAL:
return "INTERNAL";
- case ViewportType::VIEWPORT_EXTERNAL:
+ case ViewportType::EXTERNAL:
return "EXTERNAL";
- case ViewportType::VIEWPORT_VIRTUAL:
+ case ViewportType::VIRTUAL:
return "VIRTUAL";
- default:
- return "UNKNOWN";
}
+ return "UNKNOWN";
}
/*
@@ -97,7 +96,7 @@
isActive(false),
uniqueId(),
physicalPort(std::nullopt),
- type(ViewportType::VIEWPORT_INTERNAL) {}
+ type(ViewportType::INTERNAL) {}
bool operator==(const DisplayViewport& other) const {
return displayId == other.displayId && orientation == other.orientation &&
@@ -134,7 +133,7 @@
isActive = false;
uniqueId.clear();
physicalPort = std::nullopt;
- type = ViewportType::VIEWPORT_INTERNAL;
+ type = ViewportType::INTERNAL;
}
std::string toString() const {
diff --git a/include/input/Flags.h b/include/input/Flags.h
new file mode 100644
index 0000000..4ad9056
--- /dev/null
+++ b/include/input/Flags.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <type_traits>
+
+#include "utils/BitSet.h"
+
+#ifndef __UI_INPUT_FLAGS_H
+#define __UI_INPUT_FLAGS_H
+
+namespace android {
+
+namespace details {
+template <typename F, F V>
+constexpr std::optional<std::string_view> enum_value_name() {
+ // Should look something like (but all on one line):
+ // std::optional<std::string_view>
+ // android::details::enum_value_name()
+ // [F = android::test::TestFlags, V = android::test::TestFlags::ONE]
+ std::string_view view = __PRETTY_FUNCTION__;
+ size_t templateStart = view.rfind("[");
+ size_t templateEnd = view.rfind("]");
+ if (templateStart == std::string::npos || templateEnd == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Extract the template parameters without the enclosing braces.
+ // Example (cont'd): F = android::test::TestFlags, V = android::test::TestFlags::ONE
+ view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
+ size_t valStart = view.rfind("V = ");
+ if (valStart == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Example (cont'd): V = android::test::TestFlags::ONE
+ view = view.substr(valStart);
+ size_t nameStart = view.rfind("::");
+ if (nameStart == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Chop off the initial "::"
+ nameStart += 2;
+ return view.substr(nameStart);
+}
+
+template <typename F>
+inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;
+
+template <typename F, typename T, T... I>
+constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) {
+ constexpr size_t count = seq.size();
+
+ std::array<F, count> values{};
+ for (size_t i = 0, v = 0; v < count; ++i) {
+ values[v++] = static_cast<F>(T{1} << i);
+ }
+
+ return values;
+}
+
+template <typename F>
+inline constexpr auto flag_values = generate_flag_values<F>(
+ std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{});
+
+template <typename F, std::size_t... I>
+constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept {
+ return std::array<std::optional<std::string_view>, sizeof...(I)>{
+ {enum_value_name<F, flag_values<F>[I]>()...}};
+}
+
+template <typename F>
+inline constexpr auto flag_names =
+ generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{});
+
+// A trait for determining whether a type is specifically an enum class or not.
+template <typename T, bool = std::is_enum_v<T>>
+struct is_enum_class : std::false_type {};
+
+// By definition, an enum class is an enum that is not implicitly convertible to its underlying
+// type.
+template <typename T>
+struct is_enum_class<T, true>
+ : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {};
+
+template <typename T>
+inline constexpr bool is_enum_class_v = is_enum_class<T>::value;
+} // namespace details
+
+template <auto V>
+constexpr auto flag_name() {
+ using F = decltype(V);
+ return details::enum_value_name<F, V>();
+}
+
+template <typename F>
+constexpr std::optional<std::string_view> flag_name(F flag) {
+ using U = std::underlying_type_t<F>;
+ auto idx = __builtin_ctzl(static_cast<U>(flag));
+ return details::flag_names<F>[idx];
+}
+
+/* A class for handling flags defined by an enum or enum class in a type-safe way. */
+template <typename F>
+class Flags {
+ // F must be an enum or its underlying type is undefined. Theoretically we could specialize this
+ // further to avoid this restriction but in general we want to encourage the use of enums
+ // anyways.
+ static_assert(std::is_enum_v<F>, "Flags type must be an enum");
+ using U = typename std::underlying_type_t<F>;
+
+public:
+ constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
+ constexpr Flags() : mFlags(0) {}
+ constexpr Flags(const Flags<F>& f) : mFlags(f.mFlags) {}
+
+ // Provide a non-explicit construct for non-enum classes since they easily convert to their
+ // underlying types (e.g. when used with bitwise operators). For enum classes, however, we
+ // should force them to be explicitly constructed from their underlying types to make full use
+ // of the type checker.
+ template <typename T = U>
+ constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr)
+ : mFlags(t) {}
+ template <typename T = U>
+ explicit constexpr Flags(T t,
+ typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr)
+ : mFlags(t) {}
+
+ class Iterator {
+ // The type can't be larger than 64-bits otherwise it won't fit in BitSet64.
+ static_assert(sizeof(U) <= sizeof(uint64_t));
+
+ public:
+ Iterator(Flags<F> flags) : mRemainingFlags(flags.mFlags) { (*this)++; }
+ Iterator() : mRemainingFlags(0), mCurrFlag(static_cast<F>(0)) {}
+
+ // Pre-fix ++
+ Iterator& operator++() {
+ if (mRemainingFlags.isEmpty()) {
+ mCurrFlag = static_cast<F>(0);
+ } else {
+ uint64_t bit = mRemainingFlags.clearLastMarkedBit(); // counts from left
+ const U flag = 1 << (64 - bit - 1);
+ mCurrFlag = static_cast<F>(flag);
+ }
+ return *this;
+ }
+
+ // Post-fix ++
+ Iterator operator++(int) {
+ Iterator iter = *this;
+ ++*this;
+ return iter;
+ }
+
+ bool operator==(Iterator other) const {
+ return mCurrFlag == other.mCurrFlag && mRemainingFlags == other.mRemainingFlags;
+ }
+
+ bool operator!=(Iterator other) const { return !(*this == other); }
+
+ F operator*() { return mCurrFlag; }
+
+ // iterator traits
+
+ // In the future we could make this a bidirectional const iterator instead of a forward
+ // iterator but it doesn't seem worth the added complexity at this point. This could not,
+ // however, be made a non-const iterator as assigning one flag to another is a non-sensical
+ // operation.
+ using iterator_category = std::input_iterator_tag;
+ using value_type = F;
+ // Per the C++ spec, because input iterators are not assignable the iterator's reference
+ // type does not actually need to be a reference. In fact, making it a reference would imply
+ // that modifying it would change the underlying Flags object, which is obviously wrong for
+ // the same reason this can't be a non-const iterator.
+ using reference = F;
+ using difference_type = void;
+ using pointer = void;
+
+ private:
+ BitSet64 mRemainingFlags;
+ F mCurrFlag;
+ };
+
+ /*
+ * Tests whether the given flag is set.
+ */
+ bool test(F flag) const {
+ U f = static_cast<U>(flag);
+ return (f & mFlags) == f;
+ }
+
+ /* Tests whether any of the given flags are set */
+ bool any(Flags<F> f) { return (mFlags & f.mFlags) != 0; }
+
+ /* Tests whether all of the given flags are set */
+ bool all(Flags<F> f) { return (mFlags & f.mFlags) == f.mFlags; }
+
+ Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); }
+ Flags<F>& operator|=(Flags<F> rhs) {
+ mFlags = mFlags | rhs.mFlags;
+ return *this;
+ }
+
+ Flags<F> operator&(Flags<F> rhs) const { return static_cast<F>(mFlags & rhs.mFlags); }
+ Flags<F>& operator&=(Flags<F> rhs) {
+ mFlags = mFlags & rhs.mFlags;
+ return *this;
+ }
+
+ Flags<F> operator^(Flags<F> rhs) const { return static_cast<F>(mFlags ^ rhs.mFlags); }
+ Flags<F>& operator^=(Flags<F> rhs) {
+ mFlags = mFlags ^ rhs.mFlags;
+ return *this;
+ }
+
+ Flags<F> operator~() { return static_cast<F>(~mFlags); }
+
+ bool operator==(Flags<F> rhs) const { return mFlags == rhs.mFlags; }
+ bool operator!=(Flags<F> rhs) const { return !operator==(rhs); }
+
+ Flags<F>& operator=(const Flags<F>& rhs) {
+ mFlags = rhs.mFlags;
+ return *this;
+ }
+
+ Iterator begin() const { return Iterator(*this); }
+
+ Iterator end() const { return Iterator(); }
+
+ /*
+ * Returns the stored set of flags.
+ *
+ * Note that this returns the underlying type rather than the base enum class. This is because
+ * the value is no longer necessarily a strict member of the enum since the returned value could
+ * be multiple enum variants OR'd together.
+ */
+ U get() const { return mFlags; }
+
+ std::string string() const {
+ std::string result;
+ bool first = true;
+ U unstringified = 0;
+ for (const F f : *this) {
+ std::optional<std::string_view> flagString = flag_name(f);
+ if (flagString) {
+ appendFlag(result, flagString.value(), first);
+ } else {
+ unstringified |= static_cast<U>(f);
+ }
+ }
+
+ if (unstringified != 0) {
+ appendFlag(result, base::StringPrintf("0x%08x", unstringified), first);
+ }
+
+ if (first) {
+ result += "0x0";
+ }
+
+ return result;
+ }
+
+private:
+ U mFlags;
+
+ static void appendFlag(std::string& str, const std::string_view& flag, bool& first) {
+ if (first) {
+ first = false;
+ } else {
+ str += " | ";
+ }
+ str += flag;
+ }
+};
+
+// This namespace provides operator overloads for enum classes to make it easier to work with them
+// as flags. In order to use these, add them via a `using namespace` declaration.
+namespace flag_operators {
+
+template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+inline Flags<F> operator~(F f) {
+ using U = typename std::underlying_type_t<F>;
+ return static_cast<F>(~static_cast<U>(f));
+}
+template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+Flags<F> operator|(F lhs, F rhs) {
+ using U = typename std::underlying_type_t<F>;
+ return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs));
+}
+
+} // namespace flag_operators
+} // namespace android
+
+#endif // __UI_INPUT_FLAGS_H
\ No newline at end of file
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
deleted file mode 100644
index d23e3b7..0000000
--- a/include/input/IInputFlinger.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 _LIBINPUT_IINPUT_FLINGER_H
-#define _LIBINPUT_IINPUT_FLINGER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IInterface.h>
-
-#include <input/InputWindow.h>
-#include <input/ISetInputWindowsListener.h>
-
-namespace android {
-
-/*
- * This class defines the Binder IPC interface for accessing various
- * InputFlinger features.
- */
-class IInputFlinger : public IInterface {
-public:
- DECLARE_META_INTERFACE(InputFlinger)
-
- virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles,
- const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0;
- virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
- virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
-};
-
-
-/**
- * Binder implementation.
- */
-class BnInputFlinger : public BnInterface<IInputFlinger> {
-public:
- enum {
- SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
- REGISTER_INPUT_CHANNEL_TRANSACTION,
- UNREGISTER_INPUT_CHANNEL_TRANSACTION
- };
-
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0);
-};
-
-} // namespace android
-
-#endif // _LIBINPUT_IINPUT_FLINGER_H
diff --git a/include/input/ISetInputWindowsListener.h b/include/input/ISetInputWindowsListener.h
deleted file mode 100644
index 15d31b2..0000000
--- a/include/input/ISetInputWindowsListener.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class ISetInputWindowsListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(SetInputWindowsListener)
- virtual void onSetInputWindowsFinished() = 0;
-};
-
-class BnSetInputWindowsListener: public BnInterface<ISetInputWindowsListener> {
-public:
- enum SetInputWindowsTag : uint32_t {
- ON_SET_INPUT_WINDOWS_FINISHED
- };
-
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
-}; // namespace android
diff --git a/include/input/Input.h b/include/input/Input.h
index 54b4e5a..d40ba43 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -26,6 +26,7 @@
#include <android/input.h>
#include <math.h>
#include <stdint.h>
+#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
@@ -86,6 +87,13 @@
constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS =
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+/**
+ * This flag indicates that the point up event has been canceled.
+ * Typically this is used for palm event when the user has accidental touches.
+ * TODO: Adjust flag to public api
+ */
+constexpr int32_t AMOTION_EVENT_FLAG_CANCELED = 0x20;
+
enum {
/* Used when a motion event is not associated with any display.
* Typically used for non-pointer events. */
@@ -280,9 +288,9 @@
public:
// Used to divide integer space to ensure no conflict among these sources./
enum class Source : int32_t {
- INPUT_READER = 0x0 << SOURCE_SHIFT,
- INPUT_DISPATCHER = 0x1 << SOURCE_SHIFT,
- OTHER = 0x3 << SOURCE_SHIFT, // E.g. app injected events
+ INPUT_READER = static_cast<int32_t>(0x0u << SOURCE_SHIFT),
+ INPUT_DISPATCHER = static_cast<int32_t>(0x1u << SOURCE_SHIFT),
+ OTHER = static_cast<int32_t>(0x3u << SOURCE_SHIFT), // E.g. app injected events
};
IdGenerator(Source source);
@@ -294,7 +302,7 @@
private:
const Source mSource;
- static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT;
+ static constexpr int32_t SOURCE_MASK = static_cast<int32_t>(0x3u << SOURCE_SHIFT);
};
/**
@@ -341,6 +349,8 @@
void scale(float globalScale, float windowXScale, float windowYScale);
void applyOffset(float xOffset, float yOffset);
+ void transform(const ui::Transform& transform);
+
inline float getX() const {
return getAxisValue(AMOTION_EVENT_AXIS_X);
}
@@ -517,13 +527,11 @@
inline void setActionButton(int32_t button) { mActionButton = button; }
- inline float getXScale() const { return mXScale; }
+ inline float getXOffset() const { return mTransform.tx(); }
- inline float getYScale() const { return mYScale; }
+ inline float getYOffset() const { return mTransform.ty(); }
- inline float getXOffset() const { return mXOffset; }
-
- inline float getYOffset() const { return mYOffset; }
+ inline ui::Transform getTransform() const { return mTransform; }
inline float getXPrecision() const { return mXPrecision; }
@@ -685,8 +693,8 @@
void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xScale, float yScale, float xOffset,
- float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition,
+ MotionClassification classification, const ui::Transform& transform,
+ float xPrecision, float yPrecision, float rawXCursorPosition,
float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
size_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
@@ -703,7 +711,7 @@
// Apply 3x3 perspective matrix transformation.
// Matrix is in row-major form and compatible with SkMatrix.
- void transform(const float matrix[9]);
+ void transform(const std::array<float, 9>& matrix);
#ifdef __ANDROID__
status_t readFromParcel(Parcel* parcel);
@@ -737,10 +745,7 @@
int32_t mMetaState;
int32_t mButtonState;
MotionClassification mClassification;
- float mXScale;
- float mYScale;
- float mXOffset;
- float mYOffset;
+ ui::Transform mTransform;
float mXPrecision;
float mYPrecision;
float mRawXCursorPosition;
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 86de394..8e4fe79 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -19,35 +19,24 @@
#include <string>
+#include <android/InputApplicationInfo.h>
+
#include <binder/IBinder.h>
#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
#include <input/Input.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
namespace android {
-
-/*
- * Describes the properties of an application that can receive input.
- */
-struct InputApplicationInfo {
- sp<IBinder> token;
- std::string name;
- nsecs_t dispatchingTimeout;
-
- status_t write(Parcel& output) const;
- static InputApplicationInfo read(const Parcel& from);
-};
-
-
/*
* Handle for an application that can receive input.
*
* Used by the native input dispatcher as a handle for the window manager objects
* that describe an application.
*/
-class InputApplicationHandle : public RefBase {
+class InputApplicationHandle {
public:
inline const InputApplicationInfo* getInfo() const {
return &mInfo;
@@ -57,19 +46,22 @@
return !mInfo.name.empty() ? mInfo.name : "<invalid>";
}
- inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
- }
-
inline std::chrono::nanoseconds getDispatchingTimeout(
std::chrono::nanoseconds defaultValue) const {
- return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue;
+ return mInfo.token ? std::chrono::milliseconds(mInfo.dispatchingTimeoutMillis)
+ : defaultValue;
}
inline sp<IBinder> getApplicationToken() const {
return mInfo.token;
}
+ bool operator==(const InputApplicationHandle& other) const {
+ return getName() == other.getName() && getApplicationToken() == other.getApplicationToken();
+ }
+
+ bool operator!=(const InputApplicationHandle& other) const { return !(*this == other); }
+
/**
* Requests that the state of this object be updated to reflect
* the most current available information about the application.
@@ -80,9 +72,10 @@
* Returns true on success, or false if the handle is no longer valid.
*/
virtual bool updateInfo() = 0;
+
protected:
- InputApplicationHandle();
- virtual ~InputApplicationHandle();
+ InputApplicationHandle() = default;
+ virtual ~InputApplicationHandle() = default;
InputApplicationInfo mInfo;
};
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 20a17e3..c7685b7 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -144,10 +144,10 @@
};
/* Types of input device configuration files. */
-enum InputDeviceConfigurationFileType {
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
+enum class InputDeviceConfigurationFileType : int32_t {
+ CONFIGURATION = 0, /* .idc file */
+ KEY_LAYOUT = 1, /* .kl file */
+ KEY_CHARACTER_MAP = 2, /* .kcm file */
};
/*
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index b327d76..ec2de61 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -19,6 +19,7 @@
#include <input/Input.h>
#include <android/keycodes.h>
+#include <unordered_map>
#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
@@ -35,428 +36,435 @@
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)
-static const InputEventLabel KEYCODES[] = {
- // 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_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), // *Camera* 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),
-
- { nullptr, 0 }
+static const std::unordered_map<std::string, int> KEYCODES = {
+ KEYCODES_SEQUENCE
};
-static const InputEventLabel AXES[] = {
- 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 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.
- { nullptr, 0 }
+static const std::vector<InputEventLabel> KEY_NAMES = {
+ KEYCODES_SEQUENCE
};
-static const InputEventLabel LEDS[] = {
- 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),
+// 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
- { nullptr, 0 }
+static const std::unordered_map<std::string, int> AXES = {
+ AXES_SEQUENCE
};
-static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL),
- DEFINE_FLAG(FUNCTION),
- DEFINE_FLAG(GESTURE),
- DEFINE_FLAG(WAKE),
+static const std::vector<InputEventLabel> AXES_NAMES = {
+ AXES_SEQUENCE
+};
- {nullptr, 0}};
+// 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 int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
- while (list->literal) {
- if (strcmp(literal, list->literal) == 0) {
- return list->value;
- }
- list++;
- }
- return list->value;
+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(int value, const InputEventLabel* list) {
- while (list->literal) {
- if (list->value == value) {
- return list->literal;
- }
- list++;
+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(label, KEYCODES));
+ return int32_t(lookupValueByLabel(KEYCODES, label));
}
static inline const char* getLabelByKeyCode(int32_t keyCode) {
- if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) {
- return KEYCODES[keyCode].literal;
+ 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(label, FLAGS));
+ return uint32_t(lookupValueByLabel(FLAGS, label));
}
static inline int32_t getAxisByLabel(const char* label) {
- return int32_t(lookupValueByLabel(label, AXES));
+ return int32_t(lookupValueByLabel(AXES, label));
}
static inline const char* getAxisLabel(int32_t axisId) {
- return lookupLabelByValue(axisId, AXES);
+ return lookupLabelByValue(AXES_NAMES, axisId);
}
static inline int32_t getLedByLabel(const char* label) {
- return int32_t(lookupValueByLabel(label, LEDS));
+ return int32_t(lookupValueByLabel(LEDS, label));
}
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 7ca9031..8e00969 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -34,12 +34,14 @@
#include <android-base/chrono_utils.h>
#include <binder/IBinder.h>
+#include <binder/Parcelable.h>
#include <input/Input.h>
+#include <sys/stat.h>
+#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include <utils/Vector.h>
#include <android-base/unique_fd.h>
@@ -69,10 +71,7 @@
struct Header {
Type type; // 4 bytes
- // We don't need this field in order to align the body below but we
- // leave it here because InputMessage::size() and other functions
- // compute the size of this structure as sizeof(Header) + sizeof(Body).
- uint32_t padding;
+ uint32_t seq;
} header;
// Body *must* be 8 byte aligned.
@@ -81,8 +80,8 @@
static_assert(sizeof(std::array<uint8_t, 32>) == 32);
union Body {
struct Key {
- uint32_t seq;
int32_t eventId;
+ uint32_t empty1;
nsecs_t eventTime __attribute__((aligned(8)));
int32_t deviceId;
int32_t source;
@@ -101,8 +100,8 @@
} key;
struct Motion {
- uint32_t seq;
int32_t eventId;
+ uint32_t empty1;
nsecs_t eventTime __attribute__((aligned(8)));
int32_t deviceId;
int32_t source;
@@ -117,10 +116,12 @@
uint8_t empty2[3]; // 3 bytes to fill gap created by classification
int32_t edgeFlags;
nsecs_t downTime __attribute__((aligned(8)));
- float xScale;
- float yScale;
- float xOffset;
- float yOffset;
+ float dsdx;
+ float dtdx;
+ float dtdy;
+ float dsdy;
+ float tx;
+ float ty;
float xPrecision;
float yPrecision;
float xCursorPosition;
@@ -151,16 +152,14 @@
} motion;
struct Finished {
- uint32_t seq;
+ uint32_t empty1;
uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
inline size_t size() const { return sizeof(Finished); }
} finished;
struct Focus {
- uint32_t seq;
int32_t eventId;
- uint32_t empty1;
// The following two fields take up 4 bytes total
uint16_t hasFocus; // actually a bool
uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment
@@ -172,6 +171,19 @@
bool isValid(size_t actualSize) const;
size_t size() const;
void getSanitizedCopy(InputMessage* msg) const;
+
+ static const char* typeToString(Type type) {
+ switch (type) {
+ case Type::KEY:
+ return "KEY";
+ case Type::MOTION:
+ return "MOTION";
+ case Type::FINISHED:
+ return "FINISHED";
+ case Type::FOCUS:
+ return "FOCUS";
+ }
+ }
};
/*
@@ -182,14 +194,15 @@
*
* The input channel is closed when all references to it are released.
*/
-class InputChannel : public RefBase {
-protected:
- virtual ~InputChannel();
-
+class InputChannel : public Parcelable {
public:
- static sp<InputChannel> create(const std::string& name, android::base::unique_fd fd,
- sp<IBinder> token);
-
+ static std::unique_ptr<InputChannel> create(const std::string& name,
+ android::base::unique_fd fd, sp<IBinder> token);
+ InputChannel() = default;
+ InputChannel(const InputChannel& other)
+ : mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){};
+ InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
+ virtual ~InputChannel();
/**
* Create a pair of input channels.
* The two returned input channels are equivalent, and are labeled as "server" and "client"
@@ -198,10 +211,12 @@
* Return OK on success.
*/
static status_t openInputChannelPair(const std::string& name,
- sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
+ std::unique_ptr<InputChannel>& outServerChannel,
+ std::unique_ptr<InputChannel>& outClientChannel);
inline std::string getName() const { return mName; }
- inline int getFd() const { return mFd.get(); }
+ inline const android::base::unique_fd& getFd() const { return mFd; }
+ inline sp<IBinder> getToken() const { return mToken; }
/* Send a message to the other endpoint.
*
@@ -229,10 +244,10 @@
status_t receiveMessage(InputMessage* msg);
/* Return a new object that has a duplicate of this channel's fd. */
- sp<InputChannel> dup() const;
+ std::unique_ptr<InputChannel> dup() const;
- status_t write(Parcel& out) const;
- static sp<InputChannel> read(const Parcel& from);
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
/**
* The connection token is used to identify the input connection, i.e.
@@ -248,8 +263,20 @@
*/
sp<IBinder> getConnectionToken() const;
+ bool operator==(const InputChannel& inputChannel) const {
+ struct stat lhs, rhs;
+ if (fstat(mFd.get(), &lhs) != 0) {
+ return false;
+ }
+ if (fstat(inputChannel.getFd(), &rhs) != 0) {
+ return false;
+ }
+ // If file descriptors are pointing to same inode they are duplicated fds.
+ return inputChannel.getName() == getName() && inputChannel.getConnectionToken() == mToken &&
+ lhs.st_ino == rhs.st_ino;
+ }
+
private:
- InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token);
std::string mName;
android::base::unique_fd mFd;
@@ -262,13 +289,13 @@
class InputPublisher {
public:
/* Creates a publisher associated with an input channel. */
- explicit InputPublisher(const sp<InputChannel>& channel);
+ explicit InputPublisher(const std::shared_ptr<InputChannel>& channel);
/* Destroys the publisher and releases its input channel. */
~InputPublisher();
/* Gets the underlying input channel. */
- inline sp<InputChannel> getChannel() { return mChannel; }
+ inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
/* Publishes a key event to the input channel.
*
@@ -295,11 +322,10 @@
int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
int32_t actionButton, int32_t flags, int32_t edgeFlags,
int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xScale, float yScale,
- float xOffset, float yOffset, float xPrecision, float yPrecision,
- float xCursorPosition, float yCursorPosition, nsecs_t downTime,
- nsecs_t eventTime, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
+ MotionClassification classification, const ui::Transform& transform,
+ float xPrecision, float yPrecision, float xCursorPosition,
+ float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
+ uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
/* Publishes a focus event to the input channel.
@@ -325,8 +351,7 @@
status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
private:
-
- sp<InputChannel> mChannel;
+ std::shared_ptr<InputChannel> mChannel;
};
/*
@@ -335,13 +360,13 @@
class InputConsumer {
public:
/* Creates a consumer associated with an input channel. */
- explicit InputConsumer(const sp<InputChannel>& channel);
+ explicit InputConsumer(const std::shared_ptr<InputChannel>& channel);
/* Destroys the consumer and releases its input channel. */
~InputConsumer();
/* Gets the underlying input channel. */
- inline sp<InputChannel> getChannel() { return mChannel; }
+ inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
/* Consumes an input event from the input channel and copies its contents into
* an InputEvent object created using the specified factory.
@@ -411,12 +436,13 @@
*/
int32_t getPendingBatchSource() const;
+ std::string dump() const;
+
private:
// True if touch resampling is enabled.
const bool mResampleTouch;
- // The input channel.
- sp<InputChannel> mChannel;
+ std::shared_ptr<InputChannel> mChannel;
// The current input message.
InputMessage mMsg;
@@ -427,9 +453,9 @@
// Batched motion events per device and source.
struct Batch {
- Vector<InputMessage> samples;
+ std::vector<InputMessage> samples;
};
- Vector<Batch> mBatches;
+ std::vector<Batch> mBatches;
// Touch state per device and source, only for sources of class pointer.
struct History {
@@ -516,7 +542,7 @@
return false;
}
};
- Vector<TouchState> mTouchStates;
+ std::vector<TouchState> mTouchStates;
// Chain of batched sequence numbers. When multiple input messages are combined into
// a batch, we append a record here that associates the last sequence number in the
@@ -526,7 +552,7 @@
uint32_t seq; // sequence number of batched input message
uint32_t chain; // sequence number of previous batched input message
};
- Vector<SeqChain> mSeqChains;
+ std::vector<SeqChain> mSeqChains;
status_t consumeBatch(InputEventFactoryInterface* factory,
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 2dac5b6..8a752c1 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -17,105 +17,114 @@
#ifndef _UI_INPUT_WINDOW_H
#define _UI_INPUT_WINDOW_H
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <input/Flags.h>
#include <input/Input.h>
#include <input/InputTransport.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/Transform.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include "InputApplication.h"
namespace android {
-class Parcel;
/*
* Describes the properties of a window that can receive input.
*/
-struct InputWindowInfo {
+struct InputWindowInfo : public Parcelable {
InputWindowInfo() = default;
- InputWindowInfo(const Parcel& from);
// Window flags from WindowManager.LayoutParams
- enum {
- FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
- FLAG_DIM_BEHIND = 0x00000002,
- FLAG_BLUR_BEHIND = 0x00000004,
- FLAG_NOT_FOCUSABLE = 0x00000008,
- FLAG_NOT_TOUCHABLE = 0x00000010,
- FLAG_NOT_TOUCH_MODAL = 0x00000020,
- FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
- FLAG_KEEP_SCREEN_ON = 0x00000080,
- FLAG_LAYOUT_IN_SCREEN = 0x00000100,
- FLAG_LAYOUT_NO_LIMITS = 0x00000200,
- FLAG_FULLSCREEN = 0x00000400,
- FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
- FLAG_DITHER = 0x00001000,
- FLAG_SECURE = 0x00002000,
- FLAG_SCALED = 0x00004000,
- FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
- FLAG_LAYOUT_INSET_DECOR = 0x00010000,
- FLAG_ALT_FOCUSABLE_IM = 0x00020000,
- FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
- FLAG_SHOW_WHEN_LOCKED = 0x00080000,
- FLAG_SHOW_WALLPAPER = 0x00100000,
- FLAG_TURN_SCREEN_ON = 0x00200000,
- FLAG_DISMISS_KEYGUARD = 0x00400000,
- FLAG_SPLIT_TOUCH = 0x00800000,
- FLAG_SLIPPERY = 0x20000000,
- };
+ enum class Flag : uint32_t {
+ ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+ DIM_BEHIND = 0x00000002,
+ BLUR_BEHIND = 0x00000004,
+ NOT_FOCUSABLE = 0x00000008,
+ NOT_TOUCHABLE = 0x00000010,
+ NOT_TOUCH_MODAL = 0x00000020,
+ TOUCHABLE_WHEN_WAKING = 0x00000040,
+ KEEP_SCREEN_ON = 0x00000080,
+ LAYOUT_IN_SCREEN = 0x00000100,
+ LAYOUT_NO_LIMITS = 0x00000200,
+ FULLSCREEN = 0x00000400,
+ FORCE_NOT_FULLSCREEN = 0x00000800,
+ DITHER = 0x00001000,
+ SECURE = 0x00002000,
+ SCALED = 0x00004000,
+ IGNORE_CHEEK_PRESSES = 0x00008000,
+ LAYOUT_INSET_DECOR = 0x00010000,
+ ALT_FOCUSABLE_IM = 0x00020000,
+ WATCH_OUTSIDE_TOUCH = 0x00040000,
+ SHOW_WHEN_LOCKED = 0x00080000,
+ SHOW_WALLPAPER = 0x00100000,
+ TURN_SCREEN_ON = 0x00200000,
+ DISMISS_KEYGUARD = 0x00400000,
+ SPLIT_TOUCH = 0x00800000,
+ HARDWARE_ACCELERATED = 0x01000000,
+ LAYOUT_IN_OVERSCAN = 0x02000000,
+ TRANSLUCENT_STATUS = 0x04000000,
+ TRANSLUCENT_NAVIGATION = 0x08000000,
+ LOCAL_FOCUS_MODE = 0x10000000,
+ SLIPPERY = 0x20000000,
+ LAYOUT_ATTACHED_IN_DECOR = 0x40000000,
+ DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000,
+ }; // Window types from WindowManager.LayoutParams
- // Window types from WindowManager.LayoutParams
- enum {
+ enum class Type : int32_t {
+ UNKNOWN = 0,
FIRST_APPLICATION_WINDOW = 1,
- TYPE_BASE_APPLICATION = 1,
- TYPE_APPLICATION = 2,
- TYPE_APPLICATION_STARTING = 3,
+ BASE_APPLICATION = 1,
+ APPLICATION = 2,
+ APPLICATION_STARTING = 3,
LAST_APPLICATION_WINDOW = 99,
FIRST_SUB_WINDOW = 1000,
- TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
- TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1,
- TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2,
- TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
- TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
+ APPLICATION_PANEL = FIRST_SUB_WINDOW,
+ APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1,
+ APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2,
+ APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
+ APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
LAST_SUB_WINDOW = 1999,
FIRST_SYSTEM_WINDOW = 2000,
- TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
- TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
- TYPE_PHONE = FIRST_SYSTEM_WINDOW + 2,
- TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
- TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
- TYPE_TOAST = FIRST_SYSTEM_WINDOW + 5,
- TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
- TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
- TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
- TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
- TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
- TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
- TYPE_INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
- TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
- TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
- TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
- TYPE_DRAG = FIRST_SYSTEM_WINDOW + 16,
- TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
- TYPE_POINTER = FIRST_SYSTEM_WINDOW + 18,
- TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
- TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
- TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
- TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
- TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
- TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
- TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
- TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
- TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
- TYPE_TRUSTED_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 42,
+ STATUS_BAR = FIRST_SYSTEM_WINDOW,
+ SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
+ PHONE = FIRST_SYSTEM_WINDOW + 2,
+ SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
+ KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
+ TOAST = FIRST_SYSTEM_WINDOW + 5,
+ SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
+ PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
+ SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
+ KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
+ SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
+ INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
+ INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
+ WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
+ STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
+ SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
+ DRAG = FIRST_SYSTEM_WINDOW + 16,
+ STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
+ POINTER = FIRST_SYSTEM_WINDOW + 18,
+ NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
+ VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
+ BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
+ INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
+ NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
+ MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
+ ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
+ DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
+ ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
+ NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
LAST_SYSTEM_WINDOW = 2999,
};
- enum {
- INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
- INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
- INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
+ enum class Feature {
+ DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
+ NO_INPUT_CHANNEL = 0x00000002,
+ DISABLE_USER_ACTIVITY = 0x00000004,
};
/* These values are filled in by the WM and passed through SurfaceFlinger
@@ -127,9 +136,9 @@
// This uniquely identifies the input window.
int32_t id = -1;
std::string name;
- int32_t layoutParamsFlags = 0;
- int32_t layoutParamsType = 0;
- nsecs_t dispatchingTimeout = -1;
+ Flags<Flag> flags;
+ Type type = Type::UNKNOWN;
+ std::chrono::nanoseconds dispatchingTimeout = std::chrono::seconds(5);
/* These values are filled in by SurfaceFlinger. */
int32_t frameLeft = -1;
@@ -149,9 +158,8 @@
// in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
float globalScaleFactor = 1.0f;
- // Scaling factors applied to individual windows.
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
+ // Transform applied to individual windows.
+ ui::Transform transform;
/*
* This is filled in by the WM relative to the frame and then translated
@@ -163,9 +171,15 @@
bool hasFocus = false;
bool hasWallpaper = false;
bool paused = false;
+ /* This flag is set when the window is of a trusted type that is allowed to silently
+ * overlay other windows for the purpose of implementing the secure views feature.
+ * Trusted overlays, such as IME windows, can partly obscure other windows without causing
+ * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
+ */
+ bool trustedOverlay = false;
int32_t ownerPid = -1;
int32_t ownerUid = -1;
- int32_t inputFeatures = 0;
+ Flags<Feature> inputFeatures;
int32_t displayId = ADISPLAY_ID_NONE;
int32_t portalToDisplayId = ADISPLAY_ID_NONE;
InputApplicationInfo applicationInfo;
@@ -175,23 +189,19 @@
void addTouchableRegion(const Rect& region);
bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
- bool frameContainsPoint(int32_t x, int32_t y) const;
- /* Returns true if the window is of a trusted type that is allowed to silently
- * overlay other windows for the purpose of implementing the secure views feature.
- * Trusted overlays, such as IME windows, can partly obscure other windows without causing
- * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
- */
- bool isTrustedOverlay() const;
+ bool frameContainsPoint(int32_t x, int32_t y) const;
bool supportsSplitTouch() const;
bool overlaps(const InputWindowInfo* other) const;
- status_t write(Parcel& output) const;
- static InputWindowInfo read(const Parcel& from);
-};
+ bool operator==(const InputWindowInfo& inputChannel) const;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+};
/*
* Handle for a window that can receive input.
@@ -201,26 +211,19 @@
*/
class InputWindowHandle : public RefBase {
public:
+ explicit InputWindowHandle();
+ InputWindowHandle(const InputWindowHandle& other);
+ InputWindowHandle(const InputWindowInfo& other);
- inline const InputWindowInfo* getInfo() const {
- return &mInfo;
- }
+ inline const InputWindowInfo* getInfo() const { return &mInfo; }
sp<IBinder> getToken() const;
int32_t getId() const { return mInfo.id; }
- sp<IBinder> getApplicationToken() {
- return mInfo.applicationInfo.token;
- }
+ sp<IBinder> getApplicationToken() { return mInfo.applicationInfo.token; }
- inline std::string getName() const {
- return !mInfo.name.empty() ? mInfo.name : "<invalid>";
- }
-
- inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
- }
+ inline std::string getName() const { return !mInfo.name.empty() ? mInfo.name : "<invalid>"; }
inline std::chrono::nanoseconds getDispatchingTimeout(
std::chrono::nanoseconds defaultValue) const {
@@ -230,13 +233,14 @@
/**
* Requests that the state of this object be updated to reflect
* the most current available information about the application.
+ * As this class is created as RefBase object, no pure virtual function is allowed.
*
* This method should only be called from within the input dispatcher's
* critical section.
*
* Returns true on success, or false if the handle is no longer valid.
*/
- virtual bool updateInfo() = 0;
+ virtual bool updateInfo() { return false; }
/**
* Updates from another input window handle.
@@ -249,13 +253,15 @@
*/
void releaseChannel();
+ // Not override since this class is not derrived from Parcelable.
+ status_t readFromParcel(const android::Parcel* parcel);
+ status_t writeToParcel(android::Parcel* parcel) const;
+
protected:
- explicit InputWindowHandle();
virtual ~InputWindowHandle();
InputWindowInfo mInfo;
};
-
} // namespace android
#endif // _UI_INPUT_WINDOW_H
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 727865a..ee010a3 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -18,8 +18,8 @@
#define _LIBINPUT_VELOCITY_TRACKER_H
#include <input/Input.h>
-#include <utils/Timers.h>
#include <utils/BitSet.h>
+#include <utils/Timers.h>
namespace android {
@@ -30,6 +30,22 @@
*/
class VelocityTracker {
public:
+ enum class Strategy : int32_t {
+ DEFAULT = -1,
+ MIN = 0,
+ IMPULSE = 0,
+ LSQ1 = 1,
+ LSQ2 = 2,
+ LSQ3 = 3,
+ WLSQ2_DELTA = 4,
+ WLSQ2_CENTRAL = 5,
+ WLSQ2_RECENT = 6,
+ INT1 = 7,
+ INT2 = 8,
+ LEGACY = 9,
+ MAX = LEGACY,
+ };
+
struct Position {
float x, y;
};
@@ -62,8 +78,8 @@
};
// Creates a velocity tracker using the specified strategy.
- // If strategy is NULL, uses the default strategy for the platform.
- VelocityTracker(const char* strategy = nullptr);
+ // If strategy is not provided, uses the default strategy for the platform.
+ VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
~VelocityTracker();
@@ -102,16 +118,21 @@
inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
private:
- static const char* DEFAULT_STRATEGY;
+ // The default velocity tracker strategy.
+ // Although other strategies are available for testing and comparison purposes,
+ // this is the strategy that applications will actually use. Be very careful
+ // when adjusting the default strategy because it can dramatically affect
+ // (often in a bad way) the user experience.
+ static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2;
nsecs_t mLastEventTime;
BitSet32 mCurrentPointerIdBits;
int32_t mActivePointerId;
- VelocityTrackerStrategy* mStrategy;
+ std::unique_ptr<VelocityTrackerStrategy> mStrategy;
- bool configureStrategy(const char* strategy);
+ bool configureStrategy(const Strategy strategy);
- static VelocityTrackerStrategy* createStrategy(const char* strategy);
+ static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy);
};
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
deleted file mode 100644
index 964e318..0000000
--- a/include/powermanager/IPowerManager.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_IPOWERMANAGER_H
-#define ANDROID_IPOWERMANAGER_H
-
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <hardware/power.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IPowerManager : public IInterface
-{
-public:
- // These transaction IDs must be kept in sync with the method order from
- // IPowerManager.aidl.
- enum {
- ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION,
- ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1,
- RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2,
- UPDATE_WAKE_LOCK_UIDS = IBinder::FIRST_CALL_TRANSACTION + 3,
- POWER_HINT = IBinder::FIRST_CALL_TRANSACTION + 4,
- UPDATE_WAKE_LOCK_SOURCE = IBinder::FIRST_CALL_TRANSACTION + 5,
- IS_WAKE_LOCK_LEVEL_SUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 6,
- USER_ACTIVITY = IBinder::FIRST_CALL_TRANSACTION + 7,
- WAKE_UP = IBinder::FIRST_CALL_TRANSACTION + 8,
- GO_TO_SLEEP = IBinder::FIRST_CALL_TRANSACTION + 9,
- NAP = IBinder::FIRST_CALL_TRANSACTION + 10,
- IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11,
- IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12,
- GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13,
- SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14,
- REBOOT = IBinder::FIRST_CALL_TRANSACTION + 21,
- REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 22,
- SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 23,
- CRASH = IBinder::FIRST_CALL_TRANSACTION + 24,
- };
-
- DECLARE_META_INTERFACE(PowerManager)
-
- // The parcels created by these methods must be kept in sync with the
- // corresponding methods from IPowerManager.aidl.
- // FIXME remove the bool isOneWay parameters as they are not oneway in the .aidl
- virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, bool isOneWay = false) = 0;
- virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, int uid, bool isOneWay = false) = 0;
- virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay = false) = 0;
- virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids,
- bool isOneWay = false) = 0;
- virtual status_t powerHint(int hintId, int data) = 0;
- virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) = 0;
- virtual status_t reboot(bool confirm, const String16& reason, bool wait) = 0;
- virtual status_t shutdown(bool confirm, const String16& reason, bool wait) = 0;
- virtual status_t crash(const String16& message) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IPOWERMANAGER_H
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
new file mode 100644
index 0000000..dd34c0a
--- /dev/null
+++ b/include/powermanager/PowerHalController.h
@@ -0,0 +1,79 @@
+/*
+ * 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_POWERHALCONTROLLER_H
+#define ANDROID_POWERHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalWrapper.h>
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+// Connects to underlying Power HAL handles.
+class HalConnector {
+public:
+ HalConnector() = default;
+ virtual ~HalConnector() = default;
+
+ virtual std::unique_ptr<HalWrapper> connect();
+ virtual void reset();
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Controller for Power HAL handle.
+// This relies on HalConnector to connect to the underlying Power HAL
+// service and reconnects to it after each failed api call. This also ensures
+// connecting to the service is thread-safe.
+class PowerHalController : public HalWrapper {
+public:
+ PowerHalController() : PowerHalController(std::make_unique<HalConnector>()) {}
+ explicit PowerHalController(std::unique_ptr<HalConnector> connector)
+ : mHalConnector(std::move(connector)) {}
+ virtual ~PowerHalController() = default;
+
+ void init();
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+
+private:
+ std::mutex mConnectedHalMutex;
+ std::unique_ptr<HalConnector> mHalConnector;
+
+ // Shared pointers to keep global pointer and allow local copies to be used in
+ // different threads
+ std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr;
+ const std::shared_ptr<HalWrapper> mDefaultHal = std::make_shared<EmptyHalWrapper>();
+
+ std::shared_ptr<HalWrapper> initHal();
+ HalResult processHalResult(HalResult result, const char* functionName);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace power
+
+}; // namespace android
+
+#endif // ANDROID_POWERHALCONTROLLER_H
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
new file mode 100644
index 0000000..ed6f6f3
--- /dev/null
+++ b/include/powermanager/PowerHalLoader.h
@@ -0,0 +1,53 @@
+/*
+ * 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_POWERHALLOADER_H
+#define ANDROID_POWERHALLOADER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+
+namespace android {
+
+namespace power {
+
+// Loads available Power HAL services.
+class PowerHalLoader {
+public:
+ static void unloadAll();
+ static sp<hardware::power::IPower> loadAidl();
+ static sp<hardware::power::V1_0::IPower> loadHidlV1_0();
+ static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
+
+private:
+ static std::mutex gHalMutex;
+ static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex);
+
+ static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
+ EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
+
+ PowerHalLoader() = delete;
+ ~PowerHalLoader() = delete;
+};
+
+}; // namespace power
+
+} // namespace android
+
+#endif // ANDROID_POWERHALLOADER_H
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
new file mode 100644
index 0000000..c3e7601
--- /dev/null
+++ b/include/powermanager/PowerHalWrapper.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_POWERHALWRAPPER_H
+#define ANDROID_POWERHALWRAPPER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+
+namespace android {
+
+namespace power {
+
+// State of Power HAL support for individual apis.
+enum class HalSupport {
+ UNKNOWN = 0,
+ ON = 1,
+ OFF = 2,
+};
+
+// State of the Power HAL api call result.
+enum class HalResult {
+ SUCCESSFUL = 0,
+ FAILED = 1,
+ UNSUPPORTED = 2,
+};
+
+// Wrapper for Power HAL handlers.
+class HalWrapper {
+public:
+ virtual ~HalWrapper() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) = 0;
+};
+
+// Empty Power HAL wrapper that ignores all api calls.
+class EmptyHalWrapper : public HalWrapper {
+public:
+ EmptyHalWrapper() = default;
+ ~EmptyHalWrapper() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+};
+
+// Wrapper for the HIDL Power HAL v1.0.
+class HidlHalWrapperV1_0 : public HalWrapper {
+public:
+ explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal)
+ : mHandleV1_0(std::move(Hal)) {}
+ virtual ~HidlHalWrapperV1_0() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+
+protected:
+ virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
+
+private:
+ sp<hardware::power::V1_0::IPower> mHandleV1_0;
+ HalResult setInteractive(bool enabled);
+ HalResult setFeature(hardware::power::V1_0::Feature feature, bool enabled);
+};
+
+// Wrapper for the HIDL Power HAL v1.1.
+class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
+public:
+ HidlHalWrapperV1_1(sp<hardware::power::V1_0::IPower> handleV1_0,
+ sp<hardware::power::V1_1::IPower> handleV1_1)
+ : HidlHalWrapperV1_0(std::move(handleV1_0)), mHandleV1_1(std::move(handleV1_1)) {}
+ virtual ~HidlHalWrapperV1_1() = default;
+
+protected:
+ virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId,
+ uint32_t data) override;
+
+private:
+ sp<hardware::power::V1_1::IPower> mHandleV1_1;
+};
+
+// Wrapper for the AIDL Power HAL.
+class AidlHalWrapper : public HalWrapper {
+public:
+ explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
+ virtual ~AidlHalWrapper() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+
+private:
+ // Control access to the boost and mode supported arrays.
+ std::mutex mBoostMutex;
+ std::mutex mModeMutex;
+ sp<hardware::power::IPower> mHandle;
+ // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
+ // Need to increase the array size if more boost supported.
+ std::array<std::atomic<HalSupport>,
+ static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+ mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
+ // Android framework only sends mode upto DISPLAY_INACTIVE.
+ // Need to increase the array if more mode supported.
+ std::array<std::atomic<HalSupport>,
+ static_cast<int32_t>(hardware::power::Mode::DISPLAY_INACTIVE) + 1>
+ mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
+};
+
+}; // namespace power
+
+}; // namespace android
+
+#endif // ANDROID_POWERHALWRAPPER_H
diff --git a/include/ui/Rotation.h b/include/ui/Rotation.h
new file mode 120000
index 0000000..095d2ce
--- /dev/null
+++ b/include/ui/Rotation.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Rotation.h
\ No newline at end of file
diff --git a/include/ui/Transform.h b/include/ui/Transform.h
new file mode 120000
index 0000000..323f1fd
--- /dev/null
+++ b/include/ui/Transform.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Transform.h
\ No newline at end of file
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..d005058 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -98,6 +98,15 @@
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/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 1c6b491..de42f36 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -22,6 +22,7 @@
#include <utils/SystemClock.h>
#include <sys/types.h>
+#include <private/android_filesystem_config.h>
#ifdef LOG_TAG
#undef LOG_TAG
@@ -100,7 +101,7 @@
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
? service->noteOperation(op, uid, callingPackage, attributionTag,
- shouldCollectNotes(op), message)
+ shouldCollectNotes(op), message, uid == AID_SYSTEM)
: AppOpsManager::MODE_IGNORED;
return mode;
@@ -118,7 +119,8 @@
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
? service->startOperation(getClientId(), op, uid, callingPackage,
- attributionTag, startIfModeDefault, shouldCollectNotes(op), message)
+ attributionTag, startIfModeDefault, shouldCollectNotes(op), message,
+ uid == AID_SYSTEM)
: AppOpsManager::MODE_IGNORED;
return mode;
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..a3021122 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -104,6 +104,17 @@
}
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/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index cd78866..ee0cd62 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -50,15 +50,16 @@
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
- const String16& message) {
+ const String16& message, bool shouldCollectMessage) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
data.writeString16(attributionTag);
- data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+ data.writeBool(shouldCollectAsyncNotedOp);
data.writeString16(message);
+ data.writeBool(shouldCollectMessage);
remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -67,7 +68,8 @@
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
const String16& packageName, const std::optional<String16>& attributionTag,
- bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
+ bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
+ bool shouldCollectMessage) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
@@ -75,9 +77,10 @@
data.writeInt32(uid);
data.writeString16(packageName);
data.writeString16(attributionTag);
- data.writeInt32(startIfModeDefault ? 1 : 0);
- data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+ data.writeBool(startIfModeDefault);
+ data.writeBool(shouldCollectAsyncNotedOp);
data.writeString16(message);
+ data.writeBool(shouldCollectMessage);
remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -186,10 +189,11 @@
String16 packageName = data.readString16();
std::optional<String16> attributionTag;
data.readString16(&attributionTag);
- bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+ bool shouldCollectAsyncNotedOp = data.readBool();
String16 message = data.readString16();
+ bool shouldCollectMessage = data.readBool();
int32_t res = noteOperation(code, uid, packageName, attributionTag,
- shouldCollectAsyncNotedOp, message);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
@@ -202,11 +206,12 @@
String16 packageName = data.readString16();
std::optional<String16> attributionTag;
data.readString16(&attributionTag);
- bool startIfModeDefault = data.readInt32() == 1;
- bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+ bool startIfModeDefault = data.readBool();
+ bool shouldCollectAsyncNotedOp = data.readBool();
String16 message = data.readString16();
+ bool shouldCollectMessage = data.readBool();
int32_t res = startOperation(token, code, uid, packageName, attributionTag,
- startIfModeDefault, shouldCollectAsyncNotedOp, message);
+ startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp
index 52c730c..8c0495c 100644
--- a/libs/binder/fuzzer/binder.cpp
+++ b/libs/binder/fuzzer/binder.cpp
@@ -135,6 +135,7 @@
PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16),
+ PARCEL_READ_WITH_STATUS(std::optional<std::string>, readUtf8FromUtf16),
[] (const ::android::Parcel& p, uint8_t /*data*/) {
FUZZ_LOG() << "about to read c-str";
const char* str = p.readCString();
@@ -143,6 +144,7 @@
PARCEL_READ_OPT_STATUS(android::String8, readString8),
PARCEL_READ_OPT_STATUS(android::String16, readString16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16),
+ PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16),
[] (const ::android::Parcel& p, uint8_t /*data*/) {
FUZZ_LOG() << "about to readString16Inplace";
size_t outLen = 0;
@@ -156,17 +158,22 @@
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<ByteEnum>>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<IntEnum>>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<LongEnum>>, readEnumVector),
// only reading one parcelable type for now
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<ExampleParcelable>>>, readParcelableVector),
// PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable),
PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable),
+ PARCEL_READ_WITH_STATUS(std::optional<ExampleParcelable>, readParcelable),
// only reading one binder type for now
PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
@@ -174,30 +181,42 @@
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+ // PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
// PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int8_t>>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int32_t>>, readInt32Vector),
// PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int64_t>>, readInt64Vector),
// PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint64_t>>, readUint64Vector),
// PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<float>>, readFloatVector),
// PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<double>>, readDoubleVector),
// PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<bool>>, readBoolVector),
// PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<char16_t>>, readCharVector),
// PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<android::String16>>>, readString16Vector),
// PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
// PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
[] (const android::Parcel& p, uint8_t /*len*/) {
@@ -234,6 +253,7 @@
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
// PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
[] (const android::Parcel& p, uint8_t len) {
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..7043b17 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/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..fe58a41 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,13 +39,15 @@
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
+ GET_UID_PROCESS_STATE_TRANSACTION,
+ SET_SCHED_POLICY_CGROUP_TRANSACTION
};
};
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index a4a20c8..de7d12f 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -39,10 +39,11 @@
virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
- const String16& message) = 0;
+ const String16& message, bool shouldCollectMessage) = 0;
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
const String16& packageName, const std::optional<String16>& attributionTag,
- bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
+ bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
+ bool shouldCollectMessage) = 0;
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
const String16& packageName, const std::optional<String16>& attributionTag) = 0;
virtual void startWatchingMode(int32_t op, const String16& packageName,
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 7116154..33ee680 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -247,8 +247,6 @@
"android.hardware.ISoundTriggerHwService",
"android.hardware.IStreamListener",
"android.hardware.IStreamSource",
- "android.input.IInputFlinger",
- "android.input.ISetInputWindowsListener",
"android.media.IAudioFlinger",
"android.media.IAudioFlingerClient",
"android.media.IAudioPolicyService",
@@ -282,7 +280,6 @@
"android.os.IComplexTypeInterface",
"android.os.IPermissionController",
"android.os.IPingResponder",
- "android.os.IPowerManager",
"android.os.IProcessInfoService",
"android.os.ISchedulingPolicyService",
"android.os.IStringConstants",
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index d287290..7d9fd51 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -168,7 +168,7 @@
binder_status_t status = getClass()->onTransact(this, code, &in, &out);
return PruneStatusT(status);
- } else if (code == SHELL_COMMAND_TRANSACTION) {
+ } else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) {
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
int err = data.readFileDescriptor();
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 5779427..902fe79 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -110,13 +110,13 @@
const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; }
// required to be non-null, implemented for every class
- const AIBinder_Class_onCreate onCreate;
- const AIBinder_Class_onDestroy onDestroy;
- const AIBinder_Class_onTransact onTransact;
+ const AIBinder_Class_onCreate onCreate = nullptr;
+ const AIBinder_Class_onDestroy onDestroy = nullptr;
+ const AIBinder_Class_onTransact onTransact = nullptr;
// optional methods for a class
- AIBinder_onDump onDump;
- AIBinder_handleShellCommand handleShellCommand;
+ AIBinder_onDump onDump = nullptr;
+ AIBinder_handleShellCommand handleShellCommand = nullptr;
private:
// This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index 64832f3..a588985 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -118,7 +118,7 @@
AIBinder_Weak_delete(mWeakBinder);
}
-binder_status_t IFoo::addService(const char* instance) {
+AIBinder* IFoo::getBinder() {
AIBinder* binder = nullptr;
if (mWeakBinder != nullptr) {
@@ -132,8 +132,18 @@
AIBinder_Weak_delete(mWeakBinder);
}
mWeakBinder = AIBinder_Weak_new(binder);
+
+ // WARNING: it is important that this class does not implement debug or
+ // shell functions because it does not use special C++ wrapper
+ // functions, and so this is how we test those functions.
}
+ return binder;
+}
+
+binder_status_t IFoo::addService(const char* instance) {
+ AIBinder* binder = getBinder();
+
binder_status_t status = AServiceManager_addService(binder, instance);
// Strong references we care about kept by remote process
AIBinder_decStrong(binder);
diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
index cdf5493..d9dd64b 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -30,6 +30,9 @@
static AIBinder_Class* kClass;
+ // binder representing this interface with one reference count
+ AIBinder* getBinder();
+
// Takes ownership of IFoo
binder_status_t addService(const char* instance);
static ::android::sp<IFoo> getService(const char* instance, AIBinder** outBinder = nullptr);
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index b2c412d..1424b6c 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -183,6 +183,26 @@
AIBinder_decStrong(binder);
}
+TEST(NdkBinder, UnimplementedDump) {
+ sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
+ ASSERT_NE(foo, nullptr);
+ AIBinder* binder = foo->getBinder();
+ EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0));
+ AIBinder_decStrong(binder);
+}
+
+TEST(NdkBinder, UnimplementedShell) {
+ // libbinder_ndk doesn't support calling shell, so we are calling from the
+ // libbinder across processes to the NDK service which doesn't implement
+ // shell
+ static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+ sp<IBinder> testService = sm->getService(String16(IFoo::kSomeInstanceName));
+
+ Vector<String16> argsVec;
+ EXPECT_EQ(OK, IBinder::shellCommand(testService, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO,
+ argsVec, nullptr, nullptr));
+}
+
TEST(NdkBinder, DoubleNumber) {
sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
ASSERT_NE(foo, nullptr);
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 2e14408..3ec4b3a 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -86,7 +86,7 @@
static void read_extra_hals_to_dump_from_property() {
// extra hals to dump are already filled
- if (extra_hal_interfaces_to_dump.size() > 0) {
+ if (!extra_hal_interfaces_to_dump.empty()) {
return;
}
std::string value = android::base::GetProperty("ro.dump.hals.extra", "");
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 3d0f8bb..119b3e0 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -29,7 +29,6 @@
#include <android-base/strings.h>
#include <android/dlext.h>
#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
#include <graphicsenv/IGpuService.h>
#include <log/log.h>
#include <nativeloader/dlext_namespaces.h>
@@ -74,7 +73,7 @@
static std::string vndkVersionStr() {
#ifdef __BIONIC__
- return android::base::GetProperty("ro.vndk.version", "");
+ return base::GetProperty("ro.vndk.version", "");
#endif
return "";
}
@@ -345,10 +344,8 @@
}
bool GraphicsEnv::checkAngleRules(void* so) {
- char manufacturer[PROPERTY_VALUE_MAX];
- char model[PROPERTY_VALUE_MAX];
- property_get("ro.product.manufacturer", manufacturer, "UNSET");
- property_get("ro.product.model", model, "UNSET");
+ auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET");
+ auto model = base::GetProperty("ro.product.model", "UNSET");
auto ANGLEGetFeatureSupportUtilAPIVersion =
(fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
@@ -401,7 +398,8 @@
ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
break;
}
- if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) {
+ if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(),
+ systemInfoHandle)) {
ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
break;
}
@@ -653,8 +651,7 @@
mAngleNamespace = android_create_namespace("ANGLE",
nullptr, // ld_library_path
mAnglePath.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_SHARED |
- ANDROID_NAMESPACE_TYPE_ISOLATED,
+ ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED,
nullptr, // permitted_when_isolated_path
nullptr);
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 4a4510e..686e274 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -92,6 +92,7 @@
export_shared_lib_headers: [
"libbinder",
+ "libinput",
],
// bufferhub is not used when building libgui for vendors
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 682fe91..2cc7c34 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -117,9 +117,8 @@
PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
- ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
- ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
- this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
+ ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%s, count=%d", this,
+ ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount);
mWaitingForVsync = false;
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
}
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index e62a61f..4a12035 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -111,81 +111,64 @@
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
}
- virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, bool captureSecureLayers) {
+ virtual status_t captureDisplay(const DisplayCaptureArgs& args,
+ ScreenCaptureResults& captureResults) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeStrongBinder(display);
- data.writeInt32(static_cast<int32_t>(reqDataspace));
- data.writeInt32(static_cast<int32_t>(reqPixelFormat));
- data.write(sourceCrop);
- data.writeUint32(reqWidth);
- data.writeUint32(reqHeight);
- data.writeInt32(static_cast<int32_t>(useIdentityTransform));
- data.writeInt32(static_cast<int32_t>(rotation));
- data.writeInt32(static_cast<int32_t>(captureSecureLayers));
- status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
+
+ status_t result = args.write(data);
if (result != NO_ERROR) {
- ALOGE("captureScreen failed to transact: %d", result);
+ ALOGE("captureDisplay failed to parcel args: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("captureDisplay failed to transact: %d", result);
return result;
}
result = reply.readInt32();
if (result != NO_ERROR) {
- ALOGE("captureScreen failed to readInt32: %d", result);
+ ALOGE("captureDisplay failed to readInt32: %d", result);
return result;
}
- *outBuffer = new GraphicBuffer();
- reply.read(**outBuffer);
- outCapturedSecureLayers = reply.readBool();
-
+ captureResults.read(reply);
return result;
}
- virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer) {
+ virtual status_t captureDisplay(uint64_t displayOrLayerStack,
+ ScreenCaptureResults& captureResults) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeUint64(displayOrLayerStack);
- status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN_BY_ID, data, &reply);
+ status_t result =
+ remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
if (result != NO_ERROR) {
- ALOGE("captureScreen failed to transact: %d", result);
+ ALOGE("captureDisplay failed to transact: %d", result);
return result;
}
result = reply.readInt32();
if (result != NO_ERROR) {
- ALOGE("captureScreen failed to readInt32: %d", result);
+ ALOGE("captureDisplay failed to readInt32: %d", result);
return result;
}
- *outDataspace = static_cast<ui::Dataspace>(reply.readInt32());
- *outBuffer = new GraphicBuffer();
- reply.read(**outBuffer);
+ captureResults.read(reply);
return result;
}
- virtual status_t captureLayers(
- const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
- const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
- const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeLayers, float frameScale,
- bool childrenOnly) {
+ virtual status_t captureLayers(const LayerCaptureArgs& args,
+ ScreenCaptureResults& captureResults) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeStrongBinder(layerHandleBinder);
- data.writeInt32(static_cast<int32_t>(reqDataspace));
- data.writeInt32(static_cast<int32_t>(reqPixelFormat));
- data.write(sourceCrop);
- data.writeInt32(excludeLayers.size());
- for (auto el : excludeLayers) {
- data.writeStrongBinder(el);
+
+ status_t result = args.write(data);
+ if (result != NO_ERROR) {
+ ALOGE("captureLayers failed to parcel args: %d", result);
+ return result;
}
- data.writeFloat(frameScale);
- data.writeBool(childrenOnly);
- status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
+
+ result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
if (result != NO_ERROR) {
ALOGE("captureLayers failed to transact: %d", result);
return result;
@@ -196,9 +179,7 @@
return result;
}
- *outBuffer = new GraphicBuffer();
- reply.read(**outBuffer);
-
+ captureResults.read(reply);
return result;
}
@@ -327,8 +308,11 @@
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, data, &reply) ==
NO_ERROR) {
- std::vector<PhysicalDisplayId> displayIds;
- if (reply.readUint64Vector(&displayIds) == NO_ERROR) {
+ std::vector<uint64_t> rawIds;
+ if (reply.readUint64Vector(&rawIds) == NO_ERROR) {
+ std::vector<PhysicalDisplayId> displayIds(rawIds.size());
+ std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(),
+ [](uint64_t rawId) { return PhysicalDisplayId(rawId); });
return displayIds;
}
}
@@ -339,7 +323,7 @@
virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeUint64(displayId);
+ data.writeUint64(displayId.value);
remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, data, &reply);
return reply.readStrongBinder();
}
@@ -371,10 +355,8 @@
data.writeStrongBinder(display);
remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply);
const status_t result = reply.readInt32();
- if (result == NO_ERROR) {
- memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo));
- }
- return result;
+ if (result != NO_ERROR) return result;
+ return reply.read(*info);
}
virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) {
@@ -1093,22 +1075,22 @@
return NO_ERROR;
}
- virtual status_t notifyPowerHint(int32_t hintId) {
+ virtual status_t notifyPowerBoost(int32_t boostId) {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to write interface token: %d", error);
+ ALOGE("notifyPowerBoost: failed to write interface token: %d", error);
return error;
}
- error = data.writeInt32(hintId);
+ error = data.writeInt32(boostId);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to write hintId: %d", error);
+ ALOGE("notifyPowerBoost: failed to write boostId: %d", error);
return error;
}
- error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply,
+ error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_BOOST, data, &reply,
IBinder::FLAG_ONEWAY);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to transact: %d", error);
+ ALOGE("notifyPowerBoost: failed to transact: %d", error);
return error;
}
return NO_ERROR;
@@ -1295,74 +1277,51 @@
bootFinished();
return NO_ERROR;
}
- case CAPTURE_SCREEN: {
+ case CAPTURE_DISPLAY: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> display = data.readStrongBinder();
- ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
- ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop(Rect::EMPTY_RECT);
- data.read(sourceCrop);
- uint32_t reqWidth = data.readUint32();
- uint32_t reqHeight = data.readUint32();
- bool useIdentityTransform = static_cast<bool>(data.readInt32());
- int32_t rotation = data.readInt32();
- bool captureSecureLayers = static_cast<bool>(data.readInt32());
+ DisplayCaptureArgs args;
+ ScreenCaptureResults captureResults;
- bool capturedSecureLayers = false;
- status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace,
- reqPixelFormat, sourceCrop, reqWidth, reqHeight,
- useIdentityTransform, ui::toRotation(rotation),
- captureSecureLayers);
+ status_t res = args.read(data);
+ if (res != NO_ERROR) {
+ reply->writeInt32(res);
+ return NO_ERROR;
+ }
+
+ res = captureDisplay(args, captureResults);
reply->writeInt32(res);
if (res == NO_ERROR) {
- reply->write(*outBuffer);
- reply->writeBool(capturedSecureLayers);
+ captureResults.write(*reply);
}
return NO_ERROR;
}
- case CAPTURE_SCREEN_BY_ID: {
+ case CAPTURE_DISPLAY_BY_ID: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
uint64_t displayOrLayerStack = data.readUint64();
- ui::Dataspace outDataspace = ui::Dataspace::V0_SRGB;
- sp<GraphicBuffer> outBuffer;
- status_t res = captureScreen(displayOrLayerStack, &outDataspace, &outBuffer);
+ ScreenCaptureResults captureResults;
+ status_t res = captureDisplay(displayOrLayerStack, captureResults);
reply->writeInt32(res);
if (res == NO_ERROR) {
- reply->writeInt32(static_cast<int32_t>(outDataspace));
- reply->write(*outBuffer);
+ captureResults.write(*reply);
}
return NO_ERROR;
}
case CAPTURE_LAYERS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> layerHandleBinder = data.readStrongBinder();
- ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
- ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop(Rect::EMPTY_RECT);
- data.read(sourceCrop);
+ LayerCaptureArgs args;
+ ScreenCaptureResults captureResults;
- std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
- int numExcludeHandles = data.readInt32();
- if (numExcludeHandles >= static_cast<int>(MAX_LAYERS)) {
- return BAD_VALUE;
- }
- excludeHandles.reserve(numExcludeHandles);
- for (int i = 0; i < numExcludeHandles; i++) {
- excludeHandles.emplace(data.readStrongBinder());
+ status_t res = args.read(data);
+ if (res != NO_ERROR) {
+ reply->writeInt32(res);
+ return NO_ERROR;
}
- float frameScale = data.readFloat();
- bool childrenOnly = data.readBool();
-
- status_t res =
- captureLayers(layerHandleBinder, &outBuffer, reqDataspace, reqPixelFormat,
- sourceCrop, excludeHandles, frameScale, childrenOnly);
+ res = captureLayers(args, captureResults);
reply->writeInt32(res);
if (res == NO_ERROR) {
- reply->write(*outBuffer);
+ captureResults.write(*reply);
}
return NO_ERROR;
}
@@ -1419,7 +1378,7 @@
}
case GET_PHYSICAL_DISPLAY_TOKEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- PhysicalDisplayId displayId = data.readUint64();
+ PhysicalDisplayId displayId(data.readUint64());
sp<IBinder> display = getPhysicalDisplayToken(displayId);
reply->writeStrongBinder(display);
return NO_ERROR;
@@ -1442,10 +1401,8 @@
const sp<IBinder> display = data.readStrongBinder();
const status_t result = getDisplayInfo(display, &info);
reply->writeInt32(result);
- if (result == NO_ERROR) {
- memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo));
- }
- return NO_ERROR;
+ if (result != NO_ERROR) return result;
+ return reply->write(info);
}
case GET_DISPLAY_CONFIGS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1823,7 +1780,11 @@
}
case GET_PHYSICAL_DISPLAY_IDS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- return reply->writeUint64Vector(getPhysicalDisplayIds());
+ std::vector<PhysicalDisplayId> ids = getPhysicalDisplayIds();
+ std::vector<uint64_t> rawIds(ids.size());
+ std::transform(ids.begin(), ids.end(), rawIds.begin(),
+ [](PhysicalDisplayId id) { return id.value; });
+ return reply->writeUint64Vector(rawIds);
}
case ADD_REGION_SAMPLING_LISTENER: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1989,15 +1950,15 @@
}
return setDisplayBrightness(displayToken, brightness);
}
- case NOTIFY_POWER_HINT: {
+ case NOTIFY_POWER_BOOST: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- int32_t hintId;
- status_t error = data.readInt32(&hintId);
+ int32_t boostId;
+ status_t error = data.readInt32(&boostId);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to read hintId: %d", error);
+ ALOGE("notifyPowerBoost: failed to read boostId: %d", error);
return error;
}
- return notifyPowerHint(hintId);
+ return notifyPowerBoost(boostId);
}
case SET_GLOBAL_SHADOW_SETTINGS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 0281279..725d3cd 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -55,13 +55,13 @@
output.writeFloat(color.g);
output.writeFloat(color.b);
#ifndef NO_INPUT
- inputInfo.write(output);
+ inputHandle->writeToParcel(&output);
#endif
output.write(transparentRegion);
output.writeUint32(transform);
output.writeBool(transformToDisplayInverse);
output.write(crop);
- output.write(frame);
+ output.write(orientedDisplaySpaceRect);
if (buffer) {
output.writeBool(true);
output.write(*buffer);
@@ -152,14 +152,14 @@
color.b = input.readFloat();
#ifndef NO_INPUT
- inputInfo = InputWindowInfo::read(input);
+ inputHandle->readFromParcel(&input);
#endif
input.read(transparentRegion);
transform = input.readUint32();
transformToDisplayInverse = input.readBool();
input.read(crop);
- input.read(frame);
+ input.read(orientedDisplaySpaceRect);
buffer = new GraphicBuffer();
if (input.readBool()) {
input.read(*buffer);
@@ -216,15 +216,13 @@
return state.read(input);
}
-
-DisplayState::DisplayState() :
- what(0),
- layerStack(0),
- viewport(Rect::EMPTY_RECT),
- frame(Rect::EMPTY_RECT),
- width(0),
- height(0) {
-}
+DisplayState::DisplayState()
+ : what(0),
+ layerStack(0),
+ layerStackSpaceRect(Rect::EMPTY_RECT),
+ orientedDisplaySpaceRect(Rect::EMPTY_RECT),
+ width(0),
+ height(0) {}
status_t DisplayState::write(Parcel& output) const {
output.writeStrongBinder(token);
@@ -232,8 +230,8 @@
output.writeUint32(what);
output.writeUint32(layerStack);
output.writeUint32(toRotationInt(orientation));
- output.write(viewport);
- output.write(frame);
+ output.write(layerStackSpaceRect);
+ output.write(orientedDisplaySpaceRect);
output.writeUint32(width);
output.writeUint32(height);
return NO_ERROR;
@@ -245,8 +243,8 @@
what = input.readUint32();
layerStack = input.readUint32();
orientation = ui::toRotation(input.readUint32());
- input.read(viewport);
- input.read(frame);
+ input.read(layerStackSpaceRect);
+ input.read(orientedDisplaySpaceRect);
width = input.readUint32();
height = input.readUint32();
return NO_ERROR;
@@ -264,8 +262,8 @@
if (other.what & eDisplayProjectionChanged) {
what |= eDisplayProjectionChanged;
orientation = other.orientation;
- viewport = other.viewport;
- frame = other.frame;
+ layerStackSpaceRect = other.layerStackSpaceRect;
+ orientedDisplaySpaceRect = other.orientedDisplaySpaceRect;
}
if (other.what & eDisplaySizeChanged) {
what |= eDisplaySizeChanged;
@@ -368,7 +366,7 @@
}
if (other.what & eFrameChanged) {
what |= eFrameChanged;
- frame = other.frame;
+ orientedDisplaySpaceRect = other.orientedDisplaySpaceRect;
}
if (other.what & eBufferChanged) {
what |= eBufferChanged;
@@ -409,7 +407,7 @@
#ifndef NO_INPUT
if (other.what & eInputInfoChanged) {
what |= eInputInfoChanged;
- inputInfo = other.inputInfo;
+ inputHandle = new InputWindowHandle(*other.inputHandle);
}
#endif
@@ -453,19 +451,36 @@
// ------------------------------- InputWindowCommands ----------------------------------------
-void InputWindowCommands::merge(const InputWindowCommands& other) {
+bool InputWindowCommands::merge(const InputWindowCommands& other) {
+ bool changes = false;
+#ifndef NO_INPUT
+ changes |= !other.focusRequests.empty();
+ focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()),
+ std::make_move_iterator(other.focusRequests.end()));
+#endif
+ changes |= other.syncInputWindows && !syncInputWindows;
syncInputWindows |= other.syncInputWindows;
+ return changes;
}
void InputWindowCommands::clear() {
+#ifndef NO_INPUT
+ focusRequests.clear();
+#endif
syncInputWindows = false;
}
void InputWindowCommands::write(Parcel& output) const {
+#ifndef NO_INPUT
+ output.writeParcelableVector(focusRequests);
+#endif
output.writeBool(syncInputWindows);
}
void InputWindowCommands::read(const Parcel& input) {
+#ifndef NO_INPUT
+ input.readParcelableVector(&focusRequests);
+#endif
syncInputWindows = input.readBool();
}
@@ -486,4 +501,96 @@
return true;
}
+// ----------------------------------------------------------------------------
+
+status_t CaptureArgs::write(Parcel& output) const {
+ status_t status = output.writeInt32(static_cast<int32_t>(pixelFormat)) ?:
+ output.write(sourceCrop) ?:
+ output.writeFloat(frameScale) ?:
+ output.writeBool(captureSecureLayers) ?:
+ output.writeInt32(uid);
+ return status;
+}
+
+status_t CaptureArgs::read(const Parcel& input) {
+ int32_t format = 0;
+ status_t status = input.readInt32(&format) ?:
+ input.read(sourceCrop) ?:
+ input.readFloat(&frameScale) ?:
+ input.readBool(&captureSecureLayers) ?:
+ input.readInt32(&uid);
+
+ pixelFormat = static_cast<ui::PixelFormat>(format);
+ return status;
+}
+
+status_t DisplayCaptureArgs::write(Parcel& output) const {
+ status_t status = CaptureArgs::write(output);
+
+ status |= output.writeStrongBinder(displayToken) ?:
+ output.writeUint32(width) ?:
+ output.writeUint32(height) ?:
+ output.writeBool(useIdentityTransform);
+ return status;
+}
+
+status_t DisplayCaptureArgs::read(const Parcel& input) {
+ status_t status = CaptureArgs::read(input);
+
+ status |= input.readStrongBinder(&displayToken) ?:
+ input.readUint32(&width) ?:
+ input.readUint32(&height) ?:
+ input.readBool(&useIdentityTransform);
+ return status;
+}
+
+status_t LayerCaptureArgs::write(Parcel& output) const {
+ status_t status = CaptureArgs::write(output);
+
+ status |= output.writeStrongBinder(layerHandle);
+ status |= output.writeInt32(excludeHandles.size());
+ for (auto el : excludeHandles) {
+ status |= output.writeStrongBinder(el);
+ }
+ status |= output.writeBool(childrenOnly);
+ return status;
+}
+
+status_t LayerCaptureArgs::read(const Parcel& input) {
+ status_t status = CaptureArgs::read(input);
+
+ status |= input.readStrongBinder(&layerHandle);
+
+ int32_t numExcludeHandles = 0;
+ status |= input.readInt32(&numExcludeHandles);
+ excludeHandles.reserve(numExcludeHandles);
+ for (int i = 0; i < numExcludeHandles; i++) {
+ sp<IBinder> binder;
+ status |= input.readStrongBinder(&binder);
+ excludeHandles.emplace(binder);
+ }
+
+ status |= input.readBool(&childrenOnly);
+ return status;
+}
+
+status_t ScreenCaptureResults::write(Parcel& output) const {
+ status_t status = output.write(*buffer) ?:
+ output.writeBool(capturedSecureLayers) ?:
+ output.writeUint32(static_cast<uint32_t>(capturedDataspace));
+ return status;
+}
+
+status_t ScreenCaptureResults::read(const Parcel& input) {
+ buffer = new GraphicBuffer();
+ uint32_t dataspace = 0;
+ status_t status = input.read(*buffer) ?:
+ input.readBool(&capturedSecureLayers) ?:
+ input.readUint32(&dataspace);
+
+ capturedDataspace = static_cast<ui::Dataspace>(dataspace);
+
+ return status;
+}
+
}; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index d6f9e63..e45b3d1 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1499,7 +1499,7 @@
int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix);
if (graphicBuffer != nullptr) {
- *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get());
+ *buffer = graphicBuffer->toAHardwareBuffer();
AHardwareBuffer_acquire(*buffer);
} else {
*buffer = nullptr;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 83bc069..62a3c45 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1137,7 +1137,7 @@
return *this;
}
s->what |= layer_state_t::eFrameChanged;
- s->frame = frame;
+ s->orientedDisplaySpaceRect = frame;
registerSurfaceControlForCallback(sc);
return *this;
@@ -1359,11 +1359,26 @@
mStatus = BAD_INDEX;
return *this;
}
- s->inputInfo = info;
+ s->inputHandle = new InputWindowHandle(info);
s->what |= layer_state_t::eInputInfoChanged;
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
+ const sp<IBinder>& token, const sp<IBinder>& focusedToken, nsecs_t timestampNanos) {
+ FocusRequest request;
+ request.token = token;
+ request.focusedToken = focusedToken;
+ request.timestamp = timestampNanos;
+ return setFocusedWindow(request);
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
+ const FocusRequest& request) {
+ mInputWindowCommands.focusRequests.push_back(request);
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() {
mInputWindowCommands.syncInputWindows = true;
return *this;
@@ -1530,8 +1545,8 @@
const Rect& displayRect) {
DisplayState& s(getDisplayState(token));
s.orientation = orientation;
- s.viewport = layerStackRect;
- s.frame = displayRect;
+ s.layerStackSpaceRect = layerStackRect;
+ s.orientedDisplaySpaceRect = displayRect;
s.what |= DisplayState::eDisplayProjectionChanged;
mForceSynchronous = true; // TODO: do we actually still need this?
}
@@ -1893,8 +1908,8 @@
return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
}
-status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) {
- return ComposerService::getComposerService()->notifyPowerHint(hintId);
+status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) {
+ return ComposerService::getComposerService()->notifyPowerBoost(boostId);
}
status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor,
@@ -1907,59 +1922,25 @@
// ----------------------------------------------------------------------------
-status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, bool captureSecureLayers,
- sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers) {
+status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace,
- reqPixelFormat, sourceCrop, reqWidth, reqHeight,
- useIdentityTransform, rotation, captureSecureLayers);
- if (ret != NO_ERROR) {
- return ret;
- }
- return ret;
+ return s->captureDisplay(captureArgs, captureResults);
}
-status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, sp<GraphicBuffer>* outBuffer) {
- bool ignored;
- return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, reqHeight,
- useIdentityTransform, rotation, false, outBuffer, ignored);
-}
-
-status_t ScreenshotClient::capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer) {
+status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack,
+ ScreenCaptureResults& captureResults) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureScreen(displayOrLayerStack, outDataspace, outBuffer);
+ return s->captureDisplay(displayOrLayerStack, captureResults);
}
-status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- float frameScale, sp<GraphicBuffer>* outBuffer) {
+status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat,
- sourceCrop, {}, frameScale, false /* childrenOnly */);
- return ret;
-}
-
-status_t ScreenshotClient::captureChildLayers(
- const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, ui::PixelFormat reqPixelFormat,
- const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles,
- float frameScale, sp<GraphicBuffer>* outBuffer) {
- sp<ISurfaceComposer> s(ComposerService::getComposerService());
- if (s == nullptr) return NO_INIT;
- status_t ret =
- s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop,
- excludeHandles, frameScale, true /* childrenOnly */);
- return ret;
+ return s->captureLayers(captureArgs, captureResults);
}
} // namespace android
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index a332a1f..8dcb71b 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -178,17 +178,28 @@
}
sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) {
- sp<IBinder> client = parcel->readStrongBinder();
- sp<IBinder> handle = parcel->readStrongBinder();
- if (client == nullptr || handle == nullptr)
- {
- ALOGE("Invalid parcel");
- return nullptr;
+ bool invalidParcel = false;
+ status_t status;
+ sp<IBinder> client;
+ if ((status = parcel->readStrongBinder(&client)) != OK) {
+ ALOGE("Failed to read client: %s", statusToString(status).c_str());
+ invalidParcel = true;
+ }
+ sp<IBinder> handle;
+ if ((status = parcel->readStrongBinder(&handle)) != OK) {
+ ALOGE("Failed to read handle: %s", statusToString(status).c_str());
+ invalidParcel = true;
}
sp<IBinder> gbp;
- parcel->readNullableStrongBinder(&gbp);
-
+ if ((status = parcel->readNullableStrongBinder(&gbp)) != OK) {
+ ALOGE("Failed to read gbp: %s", statusToString(status).c_str());
+ invalidParcel = true;
+ }
uint32_t transformHint = parcel->readUint32();
+
+ if (invalidParcel) {
+ return nullptr;
+ }
// We aren't the original owner of the surface.
return new SurfaceControl(new SurfaceComposerClient(
interface_cast<ISurfaceComposerClient>(client)),
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp
index fcae05c..8df6e81 100644
--- a/libs/gui/SyncFeatures.cpp
+++ b/libs/gui/SyncFeatures.cpp
@@ -27,8 +27,6 @@
#include <private/gui/SyncFeatures.h>
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
namespace android {
ANDROID_SINGLETON_STATIC_INSTANCE(SyncFeatures);
@@ -40,8 +38,8 @@
EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
// This can only be called after EGL has been initialized; otherwise the
// check below will abort.
- const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
- LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed");
+ const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+ LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryString failed");
if (strstr(exts, "EGL_ANDROID_native_fence_sync")) {
// This makes GLConsumer use the EGL_ANDROID_native_fence_sync
// extension to create Android native fences to signal when all
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 0e10d1a..7974a06 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -57,16 +57,21 @@
};
struct Event {
+ // We add __attribute__((aligned(8))) for nsecs_t fields because
+ // we need to make sure all fields are aligned the same with x86
+ // and x64 (long long has different default alignment):
+ //
+ // https://en.wikipedia.org/wiki/Data_structure_alignment
struct Header {
uint32_t type;
- PhysicalDisplayId displayId;
+ PhysicalDisplayId displayId __attribute__((aligned(8)));
nsecs_t timestamp __attribute__((aligned(8)));
};
struct VSync {
uint32_t count;
- nsecs_t expectedVSyncTimestamp;
+ nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
};
struct Hotplug {
@@ -75,7 +80,7 @@
struct Config {
int32_t configId;
- nsecs_t vsyncPeriod;
+ nsecs_t vsyncPeriod __attribute__((aligned(8)));
};
Header header;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 8d3160a..926a66f 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -27,11 +27,11 @@
#include <math/vec4.h>
#include <ui/ConfigStoreTypes.h>
+#include <ui/DisplayId.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
-#include <ui/PhysicalDisplayId.h>
#include <ui/PixelFormat.h>
#include <ui/Rotation.h>
@@ -48,11 +48,14 @@
struct client_cache_t;
struct ComposerState;
+struct DisplayCaptureArgs;
struct DisplayConfig;
struct DisplayInfo;
struct DisplayStatInfo;
struct DisplayState;
struct InputWindowCommands;
+struct LayerCaptureArgs;
+struct ScreenCaptureResults;
class LayerDebugInfo;
class HdrCapabilities;
class IDisplayEventConnection;
@@ -246,65 +249,17 @@
/**
* Capture the specified screen. This requires READ_FRAME_BUFFER
* permission. This function will fail if there is a secure window on
- * screen.
+ * screen and DisplayCaptureArgs.captureSecureLayers is false.
*
* This function can capture a subregion (the source crop) of the screen.
* The subregion can be optionally rotated. It will also be scaled to
* match the size of the output buffer.
- *
- * reqDataspace and reqPixelFormat specify the data space and pixel format
- * of the buffer. The caller should pick the data space and pixel format
- * that it can consume.
- *
- * sourceCrop is the crop on the logical display.
- *
- * reqWidth and reqHeight specifies the size of the buffer. When either
- * of them is 0, they are set to the size of the logical display viewport.
- *
- * When useIdentityTransform is true, layer transformations are disabled.
- *
- * rotation specifies the rotation of the source crop (and the pixels in
- * it) around its center.
*/
- virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation = ui::ROTATION_0,
- bool captureSecureLayers = false) = 0;
- /**
- * Capture the specified screen. This requires READ_FRAME_BUFFER
- * permission. This function will fail if there is a secure window on
- * screen.
- *
- * This function can capture a subregion (the source crop) of the screen
- * into an sRGB buffer with RGBA_8888 pixel format.
- * The subregion can be optionally rotated. It will also be scaled to
- * match the size of the output buffer.
- *
- * At the moment, sourceCrop is ignored and is always set to the visible
- * region (projected display viewport) of the screen.
- *
- * reqWidth and reqHeight specifies the size of the buffer. When either
- * of them is 0, they are set to the size of the logical display viewport.
- *
- * When useIdentityTransform is true, layer transformations are disabled.
- *
- * rotation specifies the rotation of the source crop (and the pixels in
- * it) around its center.
- */
- virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- bool useIdentityTransform,
- ui::Rotation rotation = ui::ROTATION_0) {
- bool outIgnored;
- return captureScreen(display, outBuffer, outIgnored, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, sourceCrop, reqWidth, reqHeight,
- useIdentityTransform, rotation);
- }
+ virtual status_t captureDisplay(const DisplayCaptureArgs& args,
+ ScreenCaptureResults& captureResults) = 0;
- virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer) = 0;
+ virtual status_t captureDisplay(uint64_t displayOrLayerStack,
+ ScreenCaptureResults& captureResults) = 0;
template <class AA>
struct SpHash {
@@ -313,27 +268,11 @@
/**
* Capture a subtree of the layer hierarchy, potentially ignoring the root node.
- *
- * reqDataspace and reqPixelFormat specify the data space and pixel format
- * of the buffer. The caller should pick the data space and pixel format
- * that it can consume.
+ * This requires READ_FRAME_BUFFER permission. This function will fail if there
+ * is a secure window on screen
*/
- virtual status_t captureLayers(
- const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
- ui::Dataspace reqDataspace, ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeHandles,
- float frameScale = 1.0, bool childrenOnly = false) = 0;
-
- /**
- * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format,
- * potentially ignoring the root node.
- */
- status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
- const Rect& sourceCrop, float frameScale = 1.0,
- bool childrenOnly = false) {
- return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, sourceCrop, {}, frameScale, childrenOnly);
- }
+ virtual status_t captureLayers(const LayerCaptureArgs& args,
+ ScreenCaptureResults& captureResults) = 0;
/* Clears the frame statistics for animations.
*
@@ -496,14 +435,14 @@
virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0;
/*
- * Sends a power hint to the composer. This function is asynchronous.
+ * Sends a power boost to the composer. This function is asynchronous.
*
- * hintId
- * hint id according to android::hardware::power::V1_0::PowerHint
+ * boostId
+ * boost id according to android::hardware::power::Boost
*
* Returns NO_ERROR upon success.
*/
- virtual status_t notifyPowerHint(int32_t hintId) = 0;
+ virtual status_t notifyPowerBoost(int32_t boostId) = 0;
/*
* Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -562,7 +501,7 @@
GET_DISPLAY_CONFIGS,
GET_ACTIVE_CONFIG,
GET_DISPLAY_STATE,
- CAPTURE_SCREEN,
+ CAPTURE_DISPLAY,
CAPTURE_LAYERS,
CLEAR_ANIMATION_FRAME_STATS,
GET_ANIMATION_FRAME_STATS,
@@ -590,8 +529,8 @@
GET_DESIRED_DISPLAY_CONFIG_SPECS,
GET_DISPLAY_BRIGHTNESS_SUPPORT,
SET_DISPLAY_BRIGHTNESS,
- CAPTURE_SCREEN_BY_ID,
- NOTIFY_POWER_HINT,
+ CAPTURE_DISPLAY_BY_ID,
+ NOTIFY_POWER_BOOST,
SET_GLOBAL_SHADOW_SETTINGS,
GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
SET_AUTO_LOW_LATENCY_MODE,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index e60f677..187e478 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -26,9 +26,11 @@
#include <math/mat4.h>
#ifndef NO_INPUT
+#include <android/FocusRequest.h>
#include <input/InputWindow.h>
#endif
+#include <gui/ISurfaceComposer.h>
#include <gui/LayerMetadata.h>
#include <math/vec3.h>
#include <ui/GraphicTypes.h>
@@ -127,7 +129,7 @@
transform(0),
transformToDisplayInverse(false),
crop(Rect::INVALID_RECT),
- frame(Rect::INVALID_RECT),
+ orientedDisplaySpaceRect(Rect::INVALID_RECT),
dataspace(ui::Dataspace::UNKNOWN),
surfaceDamageRegion(),
api(-1),
@@ -190,7 +192,7 @@
uint32_t transform;
bool transformToDisplayInverse;
Rect crop;
- Rect frame;
+ Rect orientedDisplaySpaceRect;
sp<GraphicBuffer> buffer;
sp<Fence> acquireFence;
ui::Dataspace dataspace;
@@ -201,7 +203,7 @@
mat4 colorTransform;
#ifndef NO_INPUT
- InputWindowInfo inputInfo;
+ sp<InputWindowHandle> inputHandle = new InputWindowHandle();
#endif
client_cache_t cachedBuffer;
@@ -263,18 +265,18 @@
// These states define how layers are projected onto the physical display.
//
- // Layers are first clipped to `viewport'. They are then translated and
- // scaled from `viewport' to `frame'. Finally, they are rotated according
- // to `orientation', `width', and `height'.
+ // Layers are first clipped to `layerStackSpaceRect'. They are then translated and
+ // scaled from `layerStackSpaceRect' to `orientedDisplaySpaceRect'. Finally, they are rotated
+ // according to `orientation', `width', and `height'.
//
- // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20,
- // 10, 420, 210), and the size of the display is WxH. When orientation is
- // 0, layers will be scaled by a factor of 2 and translated by (20, 10).
- // When orientation is 1, layers will be additionally rotated by 90
- // degrees around the origin clockwise and translated by (W, 0).
+ // For example, assume layerStackSpaceRect is Rect(0, 0, 200, 100), orientedDisplaySpaceRect is
+ // Rect(20, 10, 420, 210), and the size of the display is WxH. When orientation is 0, layers
+ // will be scaled by a factor of 2 and translated by (20, 10). When orientation is 1, layers
+ // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W,
+ // 0).
ui::Rotation orientation = ui::ROTATION_0;
- Rect viewport;
- Rect frame;
+ Rect layerStackSpaceRect;
+ Rect orientedDisplaySpaceRect;
uint32_t width, height;
@@ -283,9 +285,13 @@
};
struct InputWindowCommands {
+#ifndef NO_INPUT
+ std::vector<FocusRequest> focusRequests;
+#endif
bool syncInputWindows{false};
- void merge(const InputWindowCommands& other);
+ // Merges the passed in commands and returns true if there were any changes.
+ bool merge(const InputWindowCommands& other);
void clear();
void write(Parcel& output) const;
void read(const Parcel& input);
@@ -307,6 +313,48 @@
// functionName can be null.
bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName);
+struct CaptureArgs {
+ const static int32_t UNSET_UID = -1;
+ virtual ~CaptureArgs() = default;
+
+ ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
+ Rect sourceCrop;
+ float frameScale{1};
+ bool captureSecureLayers{false};
+ int32_t uid{UNSET_UID};
+
+ virtual status_t write(Parcel& output) const;
+ virtual status_t read(const Parcel& input);
+};
+
+struct DisplayCaptureArgs : CaptureArgs {
+ sp<IBinder> displayToken;
+ uint32_t width{0};
+ uint32_t height{0};
+ bool useIdentityTransform{false};
+
+ status_t write(Parcel& output) const override;
+ status_t read(const Parcel& input) override;
+};
+
+struct LayerCaptureArgs : CaptureArgs {
+ sp<IBinder> layerHandle;
+ std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeHandles;
+ bool childrenOnly{false};
+
+ status_t write(Parcel& output) const override;
+ status_t read(const Parcel& input) override;
+};
+
+struct ScreenCaptureResults {
+ sp<GraphicBuffer> buffer;
+ bool capturedSecureLayers{false};
+ ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
+
+ status_t write(Parcel& output) const;
+ status_t read(const Parcel& input);
+};
+
}; // namespace android
#endif // ANDROID_SF_LAYER_STATE_H
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index adcb898..1a9710a 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -217,14 +217,14 @@
static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness);
/*
- * Sends a power hint to the composer. This function is asynchronous.
+ * Sends a power boost to the composer. This function is asynchronous.
*
- * hintId
- * hint id according to android::hardware::power::V1_0::PowerHint
+ * boostId
+ * boost id according to android::hardware::power::Boost
*
* Returns NO_ERROR upon success.
*/
- static status_t notifyPowerHint(int32_t hintId);
+ static status_t notifyPowerBoost(int32_t boostId);
/*
* Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -507,6 +507,9 @@
#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);
+ Transaction& setFocusedWindow(const FocusRequest& request);
Transaction& syncInputWindows();
#endif
@@ -594,26 +597,12 @@
public:
// if cropping isn't required, callers may pass in a default Rect, e.g.:
// capture(display, producer, Rect(), reqWidth, ...);
- static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, bool captureSecureLayers,
- sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers);
- static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, sp<GraphicBuffer>* outBuffer);
- static status_t capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer);
- static status_t captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- float frameScale, sp<GraphicBuffer>* outBuffer);
- static status_t captureChildLayers(
- const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
- excludeHandles,
- float frameScale, sp<GraphicBuffer>* outBuffer);
+ static status_t captureDisplay(const DisplayCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults);
+ static status_t captureDisplay(uint64_t displayOrLayerStack,
+ ScreenCaptureResults& captureResults);
+ static status_t captureLayers(const LayerCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults);
};
// ---------------------------------------------------------------------------
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index a6bcd10..53c13c8 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -14,7 +14,7 @@
srcs: [
"BLASTBufferQueue_test.cpp",
- "BufferItemConsumer_test.cpp",
+ "BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
@@ -58,6 +58,30 @@
header_libs: ["libsurfaceflinger_headers"],
}
+// Build the tests that need to run with both 32bit and 64bit.
+cc_test {
+ name: "libgui_multilib_test",
+ test_suites: ["device-tests"],
+
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: [
+ "DisplayEventStructLayout_test.cpp",
+ ],
+
+ shared_libs: [
+ "libgui",
+ ],
+
+ compile_multilib: "both",
+
+ header_libs: ["libsurfaceflinger_headers"],
+}
+
// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
// This test has a main method, and requires a separate binary to be built.
// To add move tests like this, just add additional cc_test statements,
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index da5bbdd..d88c477 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -120,6 +120,8 @@
.show(mSurfaceControl)
.setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
.apply();
+
+ mCaptureArgs.displayToken = mDisplayToken;
}
void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
@@ -165,14 +167,15 @@
void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region, int32_t border = 0,
bool outsideRegion = false) {
+ sp<GraphicBuffer>& captureBuf = mCaptureResults.buffer;
const auto epsilon = 3;
- const auto width = mScreenCaptureBuf->getWidth();
- const auto height = mScreenCaptureBuf->getHeight();
- const auto stride = mScreenCaptureBuf->getStride();
+ const auto width = captureBuf->getWidth();
+ const auto height = captureBuf->getHeight();
+ const auto stride = captureBuf->getStride();
uint32_t* bufData;
- mScreenCaptureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN),
- reinterpret_cast<void**>(&bufData));
+ captureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN),
+ reinterpret_cast<void**>(&bufData));
for (uint32_t row = 0; row < height; row++) {
for (uint32_t col = 0; col < width; col++) {
@@ -196,7 +199,7 @@
}
}
}
- mScreenCaptureBuf->unlock();
+ captureBuf->unlock();
ASSERT_EQ(false, ::testing::Test::HasFailure());
}
@@ -206,10 +209,12 @@
sp<IBinder> mDisplayToken;
sp<SurfaceControl> mSurfaceControl;
- sp<GraphicBuffer> mScreenCaptureBuf;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
+
+ DisplayCaptureArgs mCaptureArgs;
+ ScreenCaptureResults mCaptureResults;
};
TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
@@ -301,12 +306,7 @@
adapter.waitForCallbacks();
// capture screen and verify that it is red
- bool capturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
- mDisplayWidth, mDisplayHeight,
- /*useIdentityTransform*/ false));
+ ASSERT_EQ(NO_ERROR, mComposer->captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -383,12 +383,8 @@
adapter.waitForCallbacks();
// capture screen and verify that it is red
- bool capturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
- mDisplayWidth, mDisplayHeight,
- /*useIdentityTransform*/ false));
+ ASSERT_EQ(NO_ERROR, mComposer->captureDisplay(mCaptureArgs, mCaptureResults));
+
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -444,12 +440,8 @@
adapter.waitForCallbacks();
// capture screen and verify that it is red
- bool capturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
- mDisplayWidth, mDisplayHeight,
- /*useIdentityTransform*/ false));
+ ASSERT_EQ(NO_ERROR, mComposer->captureDisplay(mCaptureArgs, mCaptureResults));
+
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b,
{0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
@@ -489,12 +481,8 @@
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
adapter.waitForCallbacks();
- bool capturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
- Rect(), mDisplayWidth, mDisplayHeight,
- /*useIdentityTransform*/ false));
+ ASSERT_EQ(NO_ERROR, mComposer->captureDisplay(mCaptureArgs, mCaptureResults));
+
switch (tr) {
case ui::Transform::ROT_0:
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
new file mode 100644
index 0000000..4bcb795
--- /dev/null
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <gui/DisplayEventReceiver.h>
+
+namespace android::test {
+
+#define CHECK_OFFSET(type, member, expected_offset) \
+ static_assert((offsetof(type, member) == (expected_offset)), "")
+
+TEST(DisplayEventStructLayoutTest, TestEventAlignment) {
+ CHECK_OFFSET(DisplayEventReceiver::Event, vsync, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event, hotplug, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event, config, 24);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::Header, type, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::Header, displayId, 8);
+ CHECK_OFFSET(DisplayEventReceiver::Event::Header, timestamp, 16);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, expectedVSyncTimestamp, 8);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::Config, configId, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::Config, vsyncPeriod, 8);
+}
+
+} // namespace android::test
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index b1d3ecb..a68ec29 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -36,15 +36,16 @@
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
-#include <input/InputWindow.h>
-#include <input/IInputFlinger.h>
-#include <input/InputTransport.h>
+#include <android/os/IInputFlinger.h>
#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
#include <ui/DisplayConfig.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+using android::os::IInputFlinger;
namespace android {
namespace test {
@@ -62,16 +63,18 @@
// We use the top 10 layers as a way to haphazardly place ourselves above anything else.
static const int LAYER_BASE = INT32_MAX - 10;
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
class InputSurface {
public:
InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
mSurfaceControl = sc;
-
- InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
+ std::unique_ptr<InputChannel> clientChannel;
+ InputChannel::openInputChannelPair("testchannels", mServerChannel, clientChannel);
+ mClientChannel = std::move(clientChannel);
mInputFlinger = getInputFlinger();
- mInputFlinger->registerInputChannel(mServerChannel);
+ mInputFlinger->registerInputChannel(*mServerChannel);
populateInputInfo(width, height);
@@ -153,9 +156,7 @@
EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
- ~InputSurface() {
- mInputFlinger->unregisterInputChannel(mServerChannel);
- }
+ ~InputSurface() { mInputFlinger->unregisterInputChannel(*mServerChannel); }
void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
const sp<SurfaceControl>&)> transactionBody) {
@@ -187,9 +188,9 @@
void populateInputInfo(int width, int height) {
mInputInfo.token = mServerChannel->getConnectionToken();
mInputInfo.name = "Test info";
- mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
- mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
- mInputInfo.dispatchingTimeout = seconds_to_nanoseconds(5);
+ mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
+ mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION;
+ mInputInfo.dispatchingTimeout = 5s;
mInputInfo.globalScaleFactor = 1.0;
mInputInfo.canReceiveKeys = true;
mInputInfo.hasFocus = true;
@@ -201,19 +202,20 @@
// TODO: Fill in from SF?
mInputInfo.ownerPid = 11111;
mInputInfo.ownerUid = 11111;
- mInputInfo.inputFeatures = 0;
mInputInfo.displayId = 0;
InputApplicationInfo aInfo;
aInfo.token = new BBinder();
aInfo.name = "Test app info";
- aInfo.dispatchingTimeout = seconds_to_nanoseconds(5);
+ aInfo.dispatchingTimeoutMillis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
mInputInfo.applicationInfo = aInfo;
}
public:
sp<SurfaceControl> mSurfaceControl;
- sp<InputChannel> mServerChannel, mClientChannel;
+ std::unique_ptr<InputChannel> mServerChannel;
+ std::shared_ptr<InputChannel> mClientChannel;
sp<IInputFlinger> mInputFlinger;
InputWindowInfo mInputInfo;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 592913c..2f8e412 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -244,11 +244,13 @@
const sp<IBinder> display = sf->getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
- sp<GraphicBuffer> outBuffer;
- bool ignored;
- ASSERT_EQ(NO_ERROR,
- sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = display;
+ captureArgs.width = 64;
+ captureArgs.height = 64;
+
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, sf->captureDisplay(captureArgs, captureResults));
ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
NATIVE_WINDOW_API_CPU));
@@ -278,9 +280,7 @@
&buf));
ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
}
- ASSERT_EQ(NO_ERROR,
- sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
+ ASSERT_EQ(NO_ERROR, sf->captureDisplay(captureArgs, captureResults));
}
TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
@@ -742,12 +742,8 @@
}
status_t setActiveColorMode(const sp<IBinder>& /*display*/,
ColorMode /*colorMode*/) override { return NO_ERROR; }
- status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/,
- bool& /*outCapturedSecureLayers*/, ui::Dataspace /*reqDataspace*/,
- ui::PixelFormat /*reqPixelFormat*/, const Rect& /*sourceCrop*/,
- uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
- bool /*useIdentityTransform*/, ui::Rotation,
- bool /*captureSecureLayers*/) override {
+ status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */,
+ ScreenCaptureResults& /* captureResults */) override {
return NO_ERROR;
}
status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
@@ -760,17 +756,12 @@
return NO_ERROR;
}
void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
- status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/,
- sp<GraphicBuffer>* /*outBuffer*/) override {
+ status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
+ ScreenCaptureResults& /* captureResults */) override {
return NO_ERROR;
}
- virtual status_t captureLayers(
- const sp<IBinder>& /*parentHandle*/, sp<GraphicBuffer>* /*outBuffer*/,
- ui::Dataspace /*reqDataspace*/, ui::PixelFormat /*reqPixelFormat*/,
- const Rect& /*sourceCrop*/,
- const std::unordered_set<sp<IBinder>,
- ISurfaceComposer::SpHash<IBinder>>& /*excludeHandles*/,
- float /*frameScale*/, bool /*childrenOnly*/) override {
+ virtual status_t captureLayers(const LayerCaptureArgs& /* captureArgs */,
+ ScreenCaptureResults& /* captureResults */) override {
return NO_ERROR;
}
status_t clearAnimationFrameStats() override { return NO_ERROR; }
@@ -849,7 +840,7 @@
float* /*outAppRequestRefreshRateMax*/) override {
return NO_ERROR;
};
- status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
+ status_t notifyPowerBoost(int32_t /*boostId*/) override { return NO_ERROR; }
status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
float /*lightPosY*/, float /*lightPosZ*/,
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 7037680..0c3c1f0 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -14,6 +14,13 @@
// libinput is partially built for the host (used by build time keymap validation tool)
+filegroup {
+ name: "inputconstants_aidl",
+ srcs: [
+ "android/os/IInputConstants.aidl",
+ ],
+}
+
cc_library {
name: "libinput",
host_supported: true,
@@ -43,17 +50,27 @@
"libcutils",
],
+ static_libs: [
+ "libui-types",
+ ],
+
+ export_static_lib_headers: [
+ "libui-types",
+ ],
+
target: {
android: {
srcs: [
- "IInputFlinger.cpp",
- "InputApplication.cpp",
"InputTransport.cpp",
"InputWindow.cpp",
- "ISetInputWindowsListener.cpp",
"LatencyStatistics.cpp",
"VelocityControl.cpp",
"VelocityTracker.cpp",
+ "android/FocusRequest.aidl",
+ "android/InputApplicationInfo.aidl",
+ "android/os/IInputConstants.aidl",
+ "android/os/IInputFlinger.aidl",
+ "android/os/ISetInputWindowsListener.aidl",
],
shared_libs: [
@@ -72,6 +89,11 @@
},
},
},
+
+ aidl: {
+ local_include_dirs: ["."],
+ export_aidl_headers: true
+ },
}
subdirs = ["tests"]
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
deleted file mode 100644
index 8ec5165..0000000
--- a/libs/input/IInputFlinger.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <input/IInputFlinger.h>
-
-namespace android {
-
-class BpInputFlinger : public BpInterface<IInputFlinger> {
-public:
- explicit BpInputFlinger(const sp<IBinder>& impl) :
- BpInterface<IInputFlinger>(impl) { }
-
- virtual void setInputWindows(const std::vector<InputWindowInfo>& inputInfo,
- const sp<ISetInputWindowsListener>& setInputWindowsListener) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
-
- data.writeUint32(static_cast<uint32_t>(inputInfo.size()));
- for (const auto& info : inputInfo) {
- info.write(data);
- }
- data.writeStrongBinder(IInterface::asBinder(setInputWindowsListener));
-
- remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply,
- IBinder::FLAG_ONEWAY);
- }
-
- virtual void registerInputChannel(const sp<InputChannel>& channel) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
- channel->write(data);
- remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
- }
-
- virtual void unregisterInputChannel(const sp<InputChannel>& channel) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
- channel->write(data);
- remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
- }
-};
-
-IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger");
-
-status_t BnInputFlinger::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- switch(code) {
- case SET_INPUT_WINDOWS_TRANSACTION: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- size_t count = data.readUint32();
- if (count > data.dataSize()) {
- return BAD_VALUE;
- }
- std::vector<InputWindowInfo> handles;
- for (size_t i = 0; i < count; i++) {
- handles.push_back(InputWindowInfo::read(data));
- }
- const sp<ISetInputWindowsListener> setInputWindowsListener =
- ISetInputWindowsListener::asInterface(data.readStrongBinder());
- setInputWindows(handles, setInputWindowsListener);
- break;
- }
- case REGISTER_INPUT_CHANNEL_TRANSACTION: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- sp<InputChannel> channel = InputChannel::read(data);
- registerInputChannel(channel);
- break;
- }
- case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- sp<InputChannel> channel = InputChannel::read(data);
- unregisterInputChannel(channel);
- break;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
- return NO_ERROR;
-}
-
-};
diff --git a/libs/input/ISetInputWindowsListener.cpp b/libs/input/ISetInputWindowsListener.cpp
deleted file mode 100644
index a0330da..0000000
--- a/libs/input/ISetInputWindowsListener.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <input/ISetInputWindowsListener.h>
-
-namespace android {
-
-class BpSetInputWindowsListener : public BpInterface<ISetInputWindowsListener> {
-public:
- explicit BpSetInputWindowsListener(const sp<IBinder>& impl)
- : BpInterface<ISetInputWindowsListener>(impl) {
- }
-
- virtual ~BpSetInputWindowsListener() = default;
-
- virtual void onSetInputWindowsFinished() {
- Parcel data, reply;
- data.writeInterfaceToken(ISetInputWindowsListener::getInterfaceDescriptor());
- remote()->transact(BnSetInputWindowsListener::ON_SET_INPUT_WINDOWS_FINISHED, data, &reply,
- IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(SetInputWindowsListener, "android.input.ISetInputWindowsListener");
-
-status_t BnSetInputWindowsListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- switch(code) {
- case ON_SET_INPUT_WINDOWS_FINISHED: {
- CHECK_INTERFACE(ISetInputWindowsListener, data, reply);
- onSetInputWindowsFinished();
- return NO_ERROR;
- }
- default: {
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
-}
-
-} // namespace android
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 31aa685..fc73de3 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -301,6 +301,11 @@
}
}
+void PointerCoords::transform(const ui::Transform& transform) {
+ vec2 newCoords = transform.transform(getX(), getY());
+ setAxisValue(AMOTION_EVENT_AXIS_X, newCoords.x);
+ setAxisValue(AMOTION_EVENT_AXIS_Y, newCoords.y);
+}
// --- PointerProperties ---
@@ -320,10 +325,10 @@
void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
int32_t flags, int32_t edgeFlags, int32_t metaState,
- int32_t buttonState, MotionClassification classification, float xScale,
- float yScale, float xOffset, float yOffset, float xPrecision,
- float yPrecision, float rawXCursorPosition, float rawYCursorPosition,
- nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ int32_t buttonState, MotionClassification classification,
+ const ui::Transform& transform, float xPrecision, float yPrecision,
+ float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime,
+ nsecs_t eventTime, size_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
InputEvent::initialize(id, deviceId, source, displayId, hmac);
@@ -334,10 +339,7 @@
mMetaState = metaState;
mButtonState = buttonState;
mClassification = classification;
- mXScale = xScale;
- mYScale = yScale;
- mXOffset = xOffset;
- mYOffset = yOffset;
+ mTransform = transform;
mXPrecision = xPrecision;
mYPrecision = yPrecision;
mRawXCursorPosition = rawXCursorPosition;
@@ -360,10 +362,7 @@
mMetaState = other->mMetaState;
mButtonState = other->mButtonState;
mClassification = other->mClassification;
- mXScale = other->mXScale;
- mYScale = other->mYScale;
- mXOffset = other->mXOffset;
- mYOffset = other->mYOffset;
+ mTransform = other->mTransform;
mXPrecision = other->mXPrecision;
mYPrecision = other->mYPrecision;
mRawXCursorPosition = other->mRawXCursorPosition;
@@ -393,18 +392,20 @@
}
float MotionEvent::getXCursorPosition() const {
- const float rawX = getRawXCursorPosition();
- return rawX * mXScale + mXOffset;
+ vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
+ return vals.x;
}
float MotionEvent::getYCursorPosition() const {
- const float rawY = getRawYCursorPosition();
- return rawY * mYScale + mYOffset;
+ vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
+ return vals.y;
}
void MotionEvent::setCursorPosition(float x, float y) {
- mRawXCursorPosition = (x - mXOffset) / mXScale;
- mRawYCursorPosition = (y - mYOffset) / mYScale;
+ ui::Transform inverse = mTransform.inverse();
+ vec2 vals = inverse.transform(x, y);
+ mRawXCursorPosition = vals.x;
+ mRawYCursorPosition = vals.y;
}
const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
@@ -416,14 +417,7 @@
}
float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
- float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
- switch (axis) {
- case AMOTION_EVENT_AXIS_X:
- return value * mXScale + mXOffset;
- case AMOTION_EVENT_AXIS_Y:
- return value * mYScale + mYOffset;
- }
- return value;
+ return getHistoricalAxisValue(axis, pointerIndex, getHistorySize());
}
const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
@@ -438,14 +432,23 @@
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) {
+ return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ }
+
+ float rawX = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getX();
+ float rawY = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getY();
+ vec2 vals = mTransform.transform(rawX, rawY);
+
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value * mXScale + mXOffset;
+ return vals.x;
case AMOTION_EVENT_AXIS_Y:
- return value * mYScale + mYOffset;
+ return vals.y;
}
- return value;
+
+ // This should never happen
+ return 0;
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -459,23 +462,24 @@
}
void MotionEvent::offsetLocation(float xOffset, float yOffset) {
- mXOffset += xOffset;
- mYOffset += yOffset;
+ float currXOffset = mTransform.tx();
+ float currYOffset = mTransform.ty();
+ mTransform.set(currXOffset + xOffset, currYOffset + yOffset);
}
void MotionEvent::scale(float globalScaleFactor) {
- mXOffset *= globalScaleFactor;
- mYOffset *= globalScaleFactor;
+ mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
mXPrecision *= globalScaleFactor;
mYPrecision *= globalScaleFactor;
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
- mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor);
+ mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor, globalScaleFactor,
+ globalScaleFactor);
}
}
-static void transformPoint(const float matrix[9], float x, float y, float *outX, float *outY) {
+static vec2 transformPoint(const std::array<float, 9>& matrix, float x, float y) {
// Apply perspective transform like Skia.
float newX = matrix[0] * x + matrix[1] * y + matrix[2];
float newY = matrix[3] * x + matrix[4] * y + matrix[5];
@@ -483,22 +487,25 @@
if (newZ) {
newZ = 1.0f / newZ;
}
- *outX = newX * newZ;
- *outY = newY * newZ;
+ vec2 transformedPoint;
+ transformedPoint.x = newX * newZ;
+ transformedPoint.y = newY * newZ;
+ return transformedPoint;
}
-static float transformAngle(const float matrix[9], float angleRadians,
- float originX, float originY) {
+static float transformAngle(const std::array<float, 9>& matrix, float angleRadians, float originX,
+ float originY) {
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
// Coordinate system: down is increasing Y, right is increasing X.
float x = sinf(angleRadians);
float y = -cosf(angleRadians);
- transformPoint(matrix, x, y, &x, &y);
- x -= originX;
- y -= originY;
+ vec2 transformedPoint = transformPoint(matrix, x, y);
+
+ transformedPoint.x -= originX;
+ transformedPoint.y -= originY;
// Derive the transformed vector's clockwise angle from vertical.
- float result = atan2f(x, -y);
+ float result = atan2f(transformedPoint.x, -transformedPoint.y);
if (result < - M_PI_2) {
result += M_PI;
} else if (result > M_PI_2) {
@@ -507,51 +514,51 @@
return result;
}
-void MotionEvent::transform(const float matrix[9]) {
- // The tricky part of this implementation is to preserve the value of
- // rawX and rawY. So we apply the transformation to the first point
- // then derive an appropriate new X/Y offset that will preserve rawX
- // and rawY for that point.
- float oldXOffset = mXOffset;
- float oldYOffset = mYOffset;
- float newX, newY;
- float scaledRawX = getRawX(0) * mXScale;
- float scaledRawY = getRawY(0) * mYScale;
- transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY);
- mXOffset = newX - scaledRawX;
- mYOffset = newY - scaledRawY;
+void MotionEvent::transform(const std::array<float, 9>& matrix) {
+ // We want to preserve the rawX and rawY so we just update the transform
+ // using the values of the transform passed in
+ ui::Transform newTransform;
+ newTransform.set(matrix);
+ mTransform = newTransform * mTransform;
// Determine how the origin is transformed by the matrix so that we
// can transform orientation vectors.
- float originX, originY;
- transformPoint(matrix, 0, 0, &originX, &originY);
-
- // Apply the transformation to cursor position.
- if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- float x = mRawXCursorPosition * mXScale + oldXOffset;
- float y = mRawYCursorPosition * mYScale + oldYOffset;
- transformPoint(matrix, x, y, &x, &y);
- mRawXCursorPosition = (x - mXOffset) / mXScale;
- mRawYCursorPosition = (y - mYOffset) / mYScale;
- }
+ vec2 origin = transformPoint(matrix, 0, 0);
// Apply the transformation to all samples.
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
PointerCoords& c = mSamplePointerCoords.editItemAt(i);
- float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset;
- float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset;
- transformPoint(matrix, x, y, &x, &y);
- c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale);
- c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale);
-
float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
- transformAngle(matrix, orientation, originX, originY));
+ transformAngle(matrix, orientation, origin.x, origin.y));
}
}
#ifdef __ANDROID__
+static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ status_t status = parcel.readFloat(&dsdx);
+ status |= parcel.readFloat(&dtdx);
+ status |= parcel.readFloat(&tx);
+ status |= parcel.readFloat(&dtdy);
+ status |= parcel.readFloat(&dsdy);
+ status |= parcel.readFloat(&ty);
+
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+ return status;
+}
+
+static status_t writeToParcel(const ui::Transform& transform, Parcel& parcel) {
+ status_t status = parcel.writeFloat(transform.dsdx());
+ status |= parcel.writeFloat(transform.dtdx());
+ status |= parcel.writeFloat(transform.tx());
+ status |= parcel.writeFloat(transform.dtdy());
+ status |= parcel.writeFloat(transform.dsdy());
+ status |= parcel.writeFloat(transform.ty());
+ return status;
+}
+
status_t MotionEvent::readFromParcel(Parcel* parcel) {
size_t pointerCount = parcel->readInt32();
size_t sampleCount = parcel->readInt32();
@@ -577,10 +584,11 @@
mMetaState = parcel->readInt32();
mButtonState = parcel->readInt32();
mClassification = static_cast<MotionClassification>(parcel->readByte());
- mXScale = parcel->readFloat();
- mYScale = parcel->readFloat();
- mXOffset = parcel->readFloat();
- mYOffset = parcel->readFloat();
+
+ result = android::readFromParcel(mTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
mXPrecision = parcel->readFloat();
mYPrecision = parcel->readFloat();
mRawXCursorPosition = parcel->readFloat();
@@ -635,10 +643,11 @@
parcel->writeInt32(mMetaState);
parcel->writeInt32(mButtonState);
parcel->writeByte(static_cast<int8_t>(mClassification));
- parcel->writeFloat(mXScale);
- parcel->writeFloat(mYScale);
- parcel->writeFloat(mXOffset);
- parcel->writeFloat(mYOffset);
+
+ status_t result = android::writeToParcel(mTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
parcel->writeFloat(mXPrecision);
parcel->writeFloat(mYPrecision);
parcel->writeFloat(mRawXCursorPosition);
diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp
deleted file mode 100644
index 1d9f8a7..0000000
--- a/libs/input/InputApplication.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "InputApplication"
-
-#include <input/InputApplication.h>
-
-#include <android/log.h>
-
-namespace android {
-
-// --- InputApplicationHandle ---
-
-InputApplicationHandle::InputApplicationHandle() {
-}
-
-InputApplicationHandle::~InputApplicationHandle() {
-}
-
-InputApplicationInfo InputApplicationInfo::read(const Parcel& from) {
- InputApplicationInfo ret;
- ret.token = from.readStrongBinder();
- ret.name = from.readString8().c_str();
- ret.dispatchingTimeout = from.readInt64();
-
- return ret;
-}
-
-status_t InputApplicationInfo::write(Parcel& output) const {
- output.writeStrongBinder(token);
- output.writeString8(String8(name.c_str()));
- output.writeInt64(dispatchingTimeout);
-
- return OK;
-}
-
-} // namespace android
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 4db9e06..dbd6293 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -46,9 +46,9 @@
static void appendInputDeviceConfigurationFileRelativePath(std::string& path,
const std::string& name, InputDeviceConfigurationFileType type) {
- path += CONFIGURATION_FILE_DIR[type];
+ path += CONFIGURATION_FILE_DIR[static_cast<int32_t>(type)];
path += name;
- path += CONFIGURATION_FILE_EXTENSION[type];
+ path += CONFIGURATION_FILE_EXTENSION[static_cast<int32_t>(type)];
}
std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 11af23e..79e15c1 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -133,12 +133,11 @@
// Write the header
msg->header.type = header.type;
+ msg->header.seq = header.seq;
// Write the body
switch(header.type) {
case InputMessage::Type::KEY: {
- // uint32_t seq
- msg->body.key.seq = body.key.seq;
// int32_t eventId
msg->body.key.eventId = body.key.eventId;
// nsecs_t eventTime
@@ -168,8 +167,6 @@
break;
}
case InputMessage::Type::MOTION: {
- // uint32_t seq
- msg->body.motion.seq = body.motion.seq;
// int32_t eventId
msg->body.motion.eventId = body.motion.eventId;
// nsecs_t eventTime
@@ -198,14 +195,14 @@
msg->body.motion.edgeFlags = body.motion.edgeFlags;
// nsecs_t downTime
msg->body.motion.downTime = body.motion.downTime;
- // float xScale
- msg->body.motion.xScale = body.motion.xScale;
- // float yScale
- msg->body.motion.yScale = body.motion.yScale;
- // float xOffset
- msg->body.motion.xOffset = body.motion.xOffset;
- // float yOffset
- msg->body.motion.yOffset = body.motion.yOffset;
+
+ msg->body.motion.dsdx = body.motion.dsdx;
+ msg->body.motion.dtdx = body.motion.dtdx;
+ msg->body.motion.dtdy = body.motion.dtdy;
+ msg->body.motion.dsdy = body.motion.dsdy;
+ msg->body.motion.tx = body.motion.tx;
+ msg->body.motion.ty = body.motion.ty;
+
// float xPrecision
msg->body.motion.xPrecision = body.motion.xPrecision;
// float yPrecision
@@ -232,12 +229,10 @@
break;
}
case InputMessage::Type::FINISHED: {
- msg->body.finished.seq = body.finished.seq;
msg->body.finished.handled = body.finished.handled;
break;
}
case InputMessage::Type::FOCUS: {
- msg->body.focus.seq = body.focus.seq;
msg->body.focus.eventId = body.focus.eventId;
msg->body.focus.hasFocus = body.focus.hasFocus;
msg->body.focus.inTouchMode = body.focus.inTouchMode;
@@ -248,39 +243,40 @@
// --- InputChannel ---
-sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd,
- sp<IBinder> token) {
+std::unique_ptr<InputChannel> InputChannel::create(const std::string& name,
+ android::base::unique_fd fd, sp<IBinder> token) {
const int result = fcntl(fd, F_SETFL, O_NONBLOCK);
if (result != 0) {
LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(),
strerror(errno));
return nullptr;
}
- return new InputChannel(name, std::move(fd), token);
+ // using 'new' to access a non-public constructor
+ return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token));
}
-InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token)
- : mName(name), mFd(std::move(fd)), mToken(token) {
+InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token)
+ : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) {
if (DEBUG_CHANNEL_LIFECYCLE) {
- ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get());
+ ALOGD("Input channel constructed: name='%s', fd=%d", getName().c_str(), getFd().get());
}
}
InputChannel::~InputChannel() {
if (DEBUG_CHANNEL_LIFECYCLE) {
- ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get());
+ ALOGD("Input channel destroyed: name='%s', fd=%d", getName().c_str(), getFd().get());
}
}
status_t InputChannel::openInputChannelPair(const std::string& name,
- sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
+ std::unique_ptr<InputChannel>& outServerChannel,
+ std::unique_ptr<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
- ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
- name.c_str(), errno);
- outServerChannel.clear();
- outClientChannel.clear();
+ ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", name.c_str(), errno);
+ outServerChannel.reset();
+ outClientChannel.reset();
return result;
}
@@ -308,7 +304,7 @@
msg->getSanitizedCopy(&cleanMsg);
ssize_t nWrite;
do {
- nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
+ nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
@@ -343,7 +339,7 @@
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
- nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT);
+ nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
if (nRead < 0) {
@@ -380,10 +376,10 @@
return OK;
}
-sp<InputChannel> InputChannel::dup() const {
+std::unique_ptr<InputChannel> InputChannel::dup() const {
android::base::unique_fd newFd(::dup(getFd()));
if (!newFd.ok()) {
- ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(),
+ ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
strerror(errno));
const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
// If this process is out of file descriptors, then throwing that might end up exploding
@@ -394,34 +390,25 @@
getName().c_str());
return nullptr;
}
- return InputChannel::create(mName, std::move(newFd), mToken);
+ return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
}
-status_t InputChannel::write(Parcel& out) const {
- status_t s = out.writeCString(getName().c_str());
- if (s != OK) {
- return s;
+status_t InputChannel::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
}
-
- s = out.writeStrongBinder(mToken);
- if (s != OK) {
- return s;
- }
-
- s = out.writeUniqueFileDescriptor(mFd);
- return s;
+ return parcel->writeStrongBinder(mToken)
+ ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd);
}
-sp<InputChannel> InputChannel::read(const Parcel& from) {
- std::string name = from.readCString();
- sp<IBinder> token = from.readStrongBinder();
- android::base::unique_fd rawFd;
- status_t fdResult = from.readUniqueFileDescriptor(&rawFd);
- if (fdResult != OK) {
- return nullptr;
+status_t InputChannel::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
}
-
- return InputChannel::create(name, std::move(rawFd), token);
+ mToken = parcel->readStrongBinder();
+ return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd);
}
sp<IBinder> InputChannel::getConnectionToken() const {
@@ -430,9 +417,7 @@
// --- InputPublisher ---
-InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
- mChannel(channel) {
-}
+InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {}
InputPublisher::~InputPublisher() {
}
@@ -463,7 +448,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::KEY;
- msg.body.key.seq = seq;
+ msg.header.seq = seq;
msg.body.key.eventId = eventId;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
@@ -484,10 +469,10 @@
uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
int32_t edgeFlags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xScale, float yScale, float xOffset,
- float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
+ MotionClassification classification, const ui::Transform& transform, float xPrecision,
+ float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+ nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf(
"publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
@@ -495,17 +480,18 @@
ATRACE_NAME(message.c_str());
}
if (DEBUG_TRANSPORT_ACTIONS) {
+ std::string transformString;
+ transform.dump(transformString, "transform", " ");
ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
"displayId=%" PRId32 ", "
"action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
- "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, "
- "xOffset=%.1f, yOffset=%.1f, "
+ "metaState=0x%x, buttonState=0x%x, classification=%s,"
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
- "pointerCount=%" PRIu32,
+ "pointerCount=%" PRIu32 " \n%s",
mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
flags, edgeFlags, metaState, buttonState,
- motionClassificationToString(classification), xScale, yScale, xOffset, yOffset,
- xPrecision, yPrecision, downTime, eventTime, pointerCount);
+ motionClassificationToString(classification), xPrecision, yPrecision, downTime,
+ eventTime, pointerCount, transformString.c_str());
}
if (!seq) {
@@ -521,7 +507,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::MOTION;
- msg.body.motion.seq = seq;
+ msg.header.seq = seq;
msg.body.motion.eventId = eventId;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
@@ -534,10 +520,12 @@
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
msg.body.motion.classification = classification;
- msg.body.motion.xScale = xScale;
- msg.body.motion.yScale = yScale;
- msg.body.motion.xOffset = xOffset;
- msg.body.motion.yOffset = yOffset;
+ msg.body.motion.dsdx = transform.dsdx();
+ msg.body.motion.dtdx = transform.dtdx();
+ msg.body.motion.dtdy = transform.dtdy();
+ msg.body.motion.dsdy = transform.dsdy();
+ msg.body.motion.tx = transform.tx();
+ msg.body.motion.ty = transform.ty();
msg.body.motion.xPrecision = xPrecision;
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.xCursorPosition = xCursorPosition;
@@ -565,7 +553,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::FOCUS;
- msg.body.focus.seq = seq;
+ msg.header.seq = seq;
msg.body.focus.eventId = eventId;
msg.body.focus.hasFocus = hasFocus ? 1 : 0;
msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
@@ -589,17 +577,15 @@
mChannel->getName().c_str(), msg.header.type);
return UNKNOWN_ERROR;
}
- *outSeq = msg.body.finished.seq;
+ *outSeq = msg.header.seq;
*outHandled = msg.body.finished.handled == 1;
return OK;
}
// --- InputConsumer ---
-InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
- mResampleTouch(isTouchResamplingEnabled()),
- mChannel(channel), mMsgDeferred(false) {
-}
+InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel)
+ : mResampleTouch(isTouchResamplingEnabled()), mChannel(channel), mMsgDeferred(false) {}
InputConsumer::~InputConsumer() {
}
@@ -650,7 +636,7 @@
if (!keyEvent) return NO_MEMORY;
initializeKeyEvent(keyEvent, &mMsg);
- *outSeq = mMsg.body.key.seq;
+ *outSeq = mMsg.header.seq;
*outEvent = keyEvent;
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
@@ -662,9 +648,9 @@
case InputMessage::Type::MOTION: {
ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
if (batchIndex >= 0) {
- Batch& batch = mBatches.editItemAt(batchIndex);
+ Batch& batch = mBatches[batchIndex];
if (canAddSample(batch, &mMsg)) {
- batch.samples.push(mMsg);
+ batch.samples.push_back(mMsg);
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' consumer ~ appended to batch event",
mChannel->getName().c_str());
@@ -675,18 +661,18 @@
// No need to process events that we are going to cancel anyways
const size_t count = batch.samples.size();
for (size_t i = 0; i < count; i++) {
- const InputMessage& msg = batch.samples.itemAt(i);
- sendFinishedSignal(msg.body.motion.seq, false);
+ const InputMessage& msg = batch.samples[i];
+ sendFinishedSignal(msg.header.seq, false);
}
- batch.samples.removeItemsAt(0, count);
- mBatches.removeAt(batchIndex);
+ batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
+ mBatches.erase(mBatches.begin() + batchIndex);
} else {
// We cannot append to the batch in progress, so we need to consume
// the previous batch right now and defer the new message until later.
mMsgDeferred = true;
status_t result = consumeSamples(factory, batch, batch.samples.size(),
outSeq, outEvent);
- mBatches.removeAt(batchIndex);
+ mBatches.erase(mBatches.begin() + batchIndex);
if (result) {
return result;
}
@@ -702,9 +688,9 @@
// Start a new batch if needed.
if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mBatches.push();
- Batch& batch = mBatches.editTop();
- batch.samples.push(mMsg);
+ Batch batch;
+ batch.samples.push_back(mMsg);
+ mBatches.push_back(batch);
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' consumer ~ started batch event",
mChannel->getName().c_str());
@@ -717,7 +703,7 @@
updateTouchState(mMsg);
initializeMotionEvent(motionEvent, &mMsg);
- *outSeq = mMsg.body.motion.seq;
+ *outSeq = mMsg.header.seq;
*outEvent = motionEvent;
if (DEBUG_TRANSPORT_ACTIONS) {
@@ -738,7 +724,7 @@
if (!focusEvent) return NO_MEMORY;
initializeFocusEvent(focusEvent, &mMsg);
- *outSeq = mMsg.body.focus.seq;
+ *outSeq = mMsg.header.seq;
*outEvent = focusEvent;
break;
}
@@ -752,10 +738,10 @@
status_t result;
for (size_t i = mBatches.size(); i > 0; ) {
i--;
- Batch& batch = mBatches.editItemAt(i);
+ Batch& batch = mBatches[i];
if (frameTime < 0) {
result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent);
- mBatches.removeAt(i);
+ mBatches.erase(mBatches.begin() + i);
return result;
}
@@ -770,11 +756,11 @@
result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
const InputMessage* next;
- if (batch.samples.isEmpty()) {
- mBatches.removeAt(i);
+ if (batch.samples.empty()) {
+ mBatches.erase(mBatches.begin() + i);
next = nullptr;
} else {
- next = &batch.samples.itemAt(0);
+ next = &batch.samples[0];
}
if (!result && mResampleTouch) {
resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
@@ -792,20 +778,20 @@
uint32_t chain = 0;
for (size_t i = 0; i < count; i++) {
- InputMessage& msg = batch.samples.editItemAt(i);
+ InputMessage& msg = batch.samples[i];
updateTouchState(msg);
if (i) {
SeqChain seqChain;
- seqChain.seq = msg.body.motion.seq;
+ seqChain.seq = msg.header.seq;
seqChain.chain = chain;
- mSeqChains.push(seqChain);
+ mSeqChains.push_back(seqChain);
addSample(motionEvent, &msg);
} else {
initializeMotionEvent(motionEvent, &msg);
}
- chain = msg.body.motion.seq;
+ chain = msg.header.seq;
}
- batch.samples.removeItemsAt(0, count);
+ batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
*outSeq = chain;
*outEvent = motionEvent;
@@ -827,10 +813,10 @@
case AMOTION_EVENT_ACTION_DOWN: {
ssize_t index = findTouchState(deviceId, source);
if (index < 0) {
- mTouchStates.push();
+ mTouchStates.push_back({});
index = mTouchStates.size() - 1;
}
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
touchState.initialize(deviceId, source);
touchState.addHistory(msg);
break;
@@ -839,7 +825,7 @@
case AMOTION_EVENT_ACTION_MOVE: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
touchState.addHistory(msg);
rewriteMessage(touchState, msg);
}
@@ -849,7 +835,7 @@
case AMOTION_EVENT_ACTION_POINTER_DOWN: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
rewriteMessage(touchState, msg);
}
@@ -859,7 +845,7 @@
case AMOTION_EVENT_ACTION_POINTER_UP: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
rewriteMessage(touchState, msg);
touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
}
@@ -869,7 +855,7 @@
case AMOTION_EVENT_ACTION_SCROLL: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
rewriteMessage(touchState, msg);
}
break;
@@ -879,9 +865,9 @@
case AMOTION_EVENT_ACTION_CANCEL: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
rewriteMessage(touchState, msg);
- mTouchStates.removeAt(index);
+ mTouchStates.erase(mTouchStates.begin() + index);
}
break;
}
@@ -938,7 +924,7 @@
return;
}
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
if (touchState.historySize < 1) {
#if DEBUG_RESAMPLING
ALOGD("Not resampled, no history for device.");
@@ -1084,11 +1070,11 @@
size_t chainIndex = 0;
for (size_t i = seqChainCount; i > 0; ) {
i--;
- const SeqChain& seqChain = mSeqChains.itemAt(i);
+ const SeqChain& seqChain = mSeqChains[i];
if (seqChain.seq == currentSeq) {
currentSeq = seqChain.chain;
chainSeqs[chainIndex++] = currentSeq;
- mSeqChains.removeAt(i);
+ mSeqChains.erase(mSeqChains.begin() + i);
}
}
status_t status = OK;
@@ -1102,7 +1088,7 @@
SeqChain seqChain;
seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
seqChain.chain = chainSeqs[chainIndex];
- mSeqChains.push(seqChain);
+ mSeqChains.push_back(seqChain);
if (!chainIndex) break;
chainIndex--;
}
@@ -1117,7 +1103,7 @@
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
InputMessage msg;
msg.header.type = InputMessage::Type::FINISHED;
- msg.body.finished.seq = seq;
+ msg.header.seq = seq;
msg.body.finished.handled = handled ? 1 : 0;
return mChannel->sendMessage(&msg);
}
@@ -1127,23 +1113,23 @@
}
bool InputConsumer::hasPendingBatch() const {
- return !mBatches.isEmpty();
+ return !mBatches.empty();
}
int32_t InputConsumer::getPendingBatchSource() const {
- if (mBatches.isEmpty()) {
+ if (mBatches.empty()) {
return AINPUT_SOURCE_CLASS_NONE;
}
- const Batch& batch = mBatches.itemAt(0);
- const InputMessage& head = batch.samples.itemAt(0);
+ const Batch& batch = mBatches[0];
+ const InputMessage& head = batch.samples[0];
return head.body.motion.source;
}
ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mBatches.size(); i++) {
- const Batch& batch = mBatches.itemAt(i);
- const InputMessage& head = batch.samples.itemAt(0);
+ const Batch& batch = mBatches[i];
+ const InputMessage& head = batch.samples[0];
if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) {
return i;
}
@@ -1153,7 +1139,7 @@
ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mTouchStates.size(); i++) {
- const TouchState& touchState = mTouchStates.itemAt(i);
+ const TouchState& touchState = mTouchStates[i];
if (touchState.deviceId == deviceId && touchState.source == source) {
return i;
}
@@ -1183,16 +1169,18 @@
pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
}
+ ui::Transform transform;
+ transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
+ msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
msg->body.motion.actionButton, msg->body.motion.flags,
msg->body.motion.edgeFlags, msg->body.motion.metaState,
- msg->body.motion.buttonState, msg->body.motion.classification,
- msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset,
- msg->body.motion.yOffset, msg->body.motion.xPrecision,
- msg->body.motion.yPrecision, msg->body.motion.xCursorPosition,
- msg->body.motion.yCursorPosition, msg->body.motion.downTime,
- msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
+ msg->body.motion.buttonState, msg->body.motion.classification, transform,
+ msg->body.motion.xPrecision, msg->body.motion.yPrecision,
+ msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
+ msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
+ pointerProperties, pointerCoords);
}
void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
@@ -1207,7 +1195,7 @@
}
bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) {
- const InputMessage& head = batch.samples.itemAt(0);
+ const InputMessage& head = batch.samples[0];
uint32_t pointerCount = msg->body.motion.pointerCount;
if (head.body.motion.pointerCount != pointerCount
|| head.body.motion.action != msg->body.motion.action) {
@@ -1225,11 +1213,72 @@
ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) {
size_t numSamples = batch.samples.size();
size_t index = 0;
- while (index < numSamples
- && batch.samples.itemAt(index).body.motion.eventTime <= time) {
+ while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) {
index += 1;
}
return ssize_t(index) - 1;
}
+std::string InputConsumer::dump() const {
+ std::string out;
+ out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n";
+ out = out + "mChannel = " + mChannel->getName() + "\n";
+ out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
+ if (mMsgDeferred) {
+ out = out + "mMsg : " + InputMessage::typeToString(mMsg.header.type) + "\n";
+ }
+ out += "Batches:\n";
+ for (const Batch& batch : mBatches) {
+ out += " Batch:\n";
+ for (const InputMessage& msg : batch.samples) {
+ out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq,
+ InputMessage::typeToString(msg.header.type));
+ switch (msg.header.type) {
+ case InputMessage::Type::KEY: {
+ out += android::base::StringPrintf("action=%s keycode=%" PRId32,
+ KeyEvent::actionToString(
+ msg.body.key.action),
+ msg.body.key.keyCode);
+ break;
+ }
+ case InputMessage::Type::MOTION: {
+ out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action);
+ for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
+ const float x = msg.body.motion.pointers[i].coords.getX();
+ const float y = msg.body.motion.pointers[i].coords.getY();
+ out += android::base::StringPrintf("\n Pointer %" PRIu32
+ " : x=%.1f y=%.1f",
+ i, x, y);
+ }
+ break;
+ }
+ case InputMessage::Type::FINISHED: {
+ out += android::base::StringPrintf("handled=%s",
+ toString(msg.body.finished.handled));
+ break;
+ }
+ case InputMessage::Type::FOCUS: {
+ out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s",
+ toString(msg.body.focus.hasFocus),
+ toString(msg.body.focus.inTouchMode));
+ break;
+ }
+ }
+ out += "\n";
+ }
+ }
+ if (mBatches.empty()) {
+ out += " <empty>\n";
+ }
+ out += "mSeqChains:\n";
+ for (const SeqChain& chain : mSeqChains) {
+ out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq,
+ chain.chain);
+ }
+ if (mSeqChains.empty()) {
+ out += " <empty>\n";
+ }
+ return out;
+}
+
} // namespace android
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 85a2015..6db9ed5 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -14,20 +14,20 @@
* limitations under the License.
*/
+#include <type_traits>
#define LOG_TAG "InputWindow"
#define LOG_NDEBUG 0
+#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
-#include <input/InputWindow.h>
#include <input/InputTransport.h>
+#include <input/InputWindow.h>
#include <log/log.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
namespace android {
+
// --- InputWindowInfo ---
void InputWindowInfo::addTouchableRegion(const Rect& region) {
touchableRegion.orSelf(region);
@@ -42,22 +42,8 @@
&& y >= frameTop && y < frameBottom;
}
-// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready.
-bool InputWindowInfo::isTrustedOverlay() const {
- return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG ||
- layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR ||
- layoutParamsType == TYPE_NOTIFICATION_SHADE ||
- layoutParamsType == TYPE_NAVIGATION_BAR ||
- layoutParamsType == TYPE_NAVIGATION_BAR_PANEL ||
- layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY ||
- layoutParamsType == TYPE_DOCK_DIVIDER ||
- layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY ||
- layoutParamsType == TYPE_INPUT_CONSUMER ||
- layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY;
-}
-
bool InputWindowInfo::supportsSplitTouch() const {
- return layoutParamsFlags & FLAG_SPLIT_TOUCH;
+ return flags.test(Flag::SPLIT_TOUCH);
}
bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
@@ -65,94 +51,148 @@
&& frameTop < other->frameBottom && frameBottom > other->frameTop;
}
-status_t InputWindowInfo::write(Parcel& output) const {
+bool InputWindowInfo::operator==(const InputWindowInfo& info) const {
+ return info.token == token && info.id == id && info.name == name && info.flags == flags &&
+ info.type == type && info.dispatchingTimeout == dispatchingTimeout &&
+ info.frameLeft == frameLeft && info.frameTop == frameTop &&
+ info.frameRight == frameRight && info.frameBottom == frameBottom &&
+ info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
+ info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
+ info.visible == visible && info.canReceiveKeys == canReceiveKeys &&
+ info.trustedOverlay == trustedOverlay && info.hasFocus == hasFocus &&
+ info.hasWallpaper == hasWallpaper && info.paused == paused &&
+ info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+ info.inputFeatures == inputFeatures && info.displayId == displayId &&
+ info.portalToDisplayId == portalToDisplayId &&
+ info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
+ info.applicationInfo == applicationInfo;
+}
+
+status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
if (name.empty()) {
- output.writeInt32(0);
+ parcel->writeInt32(0);
return OK;
}
- output.writeInt32(1);
- status_t s = output.writeStrongBinder(token);
- if (s != OK) return s;
+ parcel->writeInt32(1);
- output.writeInt32(id);
- output.writeString8(String8(name.c_str()));
- output.writeInt32(layoutParamsFlags);
- output.writeInt32(layoutParamsType);
- output.writeInt64(dispatchingTimeout);
- output.writeInt32(frameLeft);
- output.writeInt32(frameTop);
- output.writeInt32(frameRight);
- output.writeInt32(frameBottom);
- output.writeInt32(surfaceInset);
- output.writeFloat(globalScaleFactor);
- output.writeFloat(windowXScale);
- output.writeFloat(windowYScale);
- output.writeBool(visible);
- output.writeBool(canReceiveKeys);
- output.writeBool(hasFocus);
- output.writeBool(hasWallpaper);
- output.writeBool(paused);
- output.writeInt32(ownerPid);
- output.writeInt32(ownerUid);
- output.writeInt32(inputFeatures);
- output.writeInt32(displayId);
- output.writeInt32(portalToDisplayId);
- applicationInfo.write(output);
- output.write(touchableRegion);
- output.writeBool(replaceTouchableRegionWithCrop);
- output.writeStrongBinder(touchableRegionCropHandle.promote());
- return OK;
+ status_t status = parcel->writeStrongBinder(token) ?:
+ parcel->writeInt64(dispatchingTimeout.count()) ?:
+ parcel->writeInt32(id) ?:
+ parcel->writeUtf8AsUtf16(name) ?:
+ parcel->writeInt32(flags.get()) ?:
+ parcel->writeInt32(static_cast<std::underlying_type_t<InputWindowInfo::Type>>(type)) ?:
+ parcel->writeInt32(frameLeft) ?:
+ parcel->writeInt32(frameTop) ?:
+ parcel->writeInt32(frameRight) ?:
+ parcel->writeInt32(frameBottom) ?:
+ parcel->writeInt32(surfaceInset) ?:
+ parcel->writeFloat(globalScaleFactor) ?:
+ parcel->writeFloat(transform.dsdx()) ?:
+ parcel->writeFloat(transform.dtdx()) ?:
+ parcel->writeFloat(transform.tx()) ?:
+ parcel->writeFloat(transform.dtdy()) ?:
+ parcel->writeFloat(transform.dsdy()) ?:
+ parcel->writeFloat(transform.ty()) ?:
+ parcel->writeBool(visible) ?:
+ parcel->writeBool(canReceiveKeys) ?:
+ parcel->writeBool(hasFocus) ?:
+ parcel->writeBool(hasWallpaper) ?:
+ parcel->writeBool(paused) ?:
+ parcel->writeBool(trustedOverlay) ?:
+ parcel->writeInt32(ownerPid) ?:
+ parcel->writeInt32(ownerUid) ?:
+ parcel->writeInt32(inputFeatures.get()) ?:
+ parcel->writeInt32(displayId) ?:
+ parcel->writeInt32(portalToDisplayId) ?:
+ applicationInfo.writeToParcel(parcel) ?:
+ parcel->write(touchableRegion) ?:
+ parcel->writeBool(replaceTouchableRegionWithCrop) ?:
+ parcel->writeStrongBinder(touchableRegionCropHandle.promote());
+
+ return status;
}
-InputWindowInfo InputWindowInfo::read(const Parcel& from) {
- InputWindowInfo ret;
-
- if (from.readInt32() == 0) {
- return ret;
+status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ if (parcel->readInt32() == 0) {
+ return OK;
}
- ret.token = from.readStrongBinder();
- ret.id = from.readInt32();
- ret.name = from.readString8().c_str();
- ret.layoutParamsFlags = from.readInt32();
- ret.layoutParamsType = from.readInt32();
- ret.dispatchingTimeout = from.readInt64();
- ret.frameLeft = from.readInt32();
- ret.frameTop = from.readInt32();
- ret.frameRight = from.readInt32();
- ret.frameBottom = from.readInt32();
- ret.surfaceInset = from.readInt32();
- ret.globalScaleFactor = from.readFloat();
- ret.windowXScale = from.readFloat();
- ret.windowYScale = from.readFloat();
- ret.visible = from.readBool();
- ret.canReceiveKeys = from.readBool();
- ret.hasFocus = from.readBool();
- ret.hasWallpaper = from.readBool();
- ret.paused = from.readBool();
- ret.ownerPid = from.readInt32();
- ret.ownerUid = from.readInt32();
- ret.inputFeatures = from.readInt32();
- ret.displayId = from.readInt32();
- ret.portalToDisplayId = from.readInt32();
- ret.applicationInfo = InputApplicationInfo::read(from);
- from.read(ret.touchableRegion);
- ret.replaceTouchableRegionWithCrop = from.readBool();
- ret.touchableRegionCropHandle = from.readStrongBinder();
+ token = parcel->readStrongBinder();
+ dispatchingTimeout = static_cast<decltype(dispatchingTimeout)>(parcel->readInt64());
+ status_t status = parcel->readInt32(&id) ?: parcel->readUtf8FromUtf16(&name);
+ if (status != OK) {
+ return status;
+ }
- return ret;
-}
+ flags = Flags<Flag>(parcel->readInt32());
+ type = static_cast<Type>(parcel->readInt32());
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ status = parcel->readInt32(&frameLeft) ?:
+ parcel->readInt32(&frameTop) ?:
+ parcel->readInt32(&frameRight) ?:
+ parcel->readInt32(&frameBottom) ?:
+ parcel->readInt32(&surfaceInset) ?:
+ parcel->readFloat(&globalScaleFactor) ?:
+ parcel->readFloat(&dsdx) ?:
+ parcel->readFloat(&dtdx) ?:
+ parcel->readFloat(&tx) ?:
+ parcel->readFloat(&dtdy) ?:
+ parcel->readFloat(&dsdy) ?:
+ parcel->readFloat(&ty) ?:
+ parcel->readBool(&visible) ?:
+ parcel->readBool(&canReceiveKeys) ?:
+ parcel->readBool(&hasFocus) ?:
+ parcel->readBool(&hasWallpaper) ?:
+ parcel->readBool(&paused) ?:
+ parcel->readBool(&trustedOverlay) ?:
+ parcel->readInt32(&ownerPid) ?:
+ parcel->readInt32(&ownerUid);
-InputWindowInfo::InputWindowInfo(const Parcel& from) {
- *this = read(from);
+ if (status != OK) {
+ return status;
+ }
+
+ inputFeatures = Flags<Feature>(parcel->readInt32());
+ status = parcel->readInt32(&displayId) ?:
+ parcel->readInt32(&portalToDisplayId) ?:
+ applicationInfo.readFromParcel(parcel) ?:
+ parcel->read(touchableRegion) ?:
+ parcel->readBool(&replaceTouchableRegionWithCrop);
+
+ if (status != OK) {
+ return status;
+ }
+
+ touchableRegionCropHandle = parcel->readStrongBinder();
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+
+ return OK;
}
// --- InputWindowHandle ---
-InputWindowHandle::InputWindowHandle() {
+InputWindowHandle::InputWindowHandle() {}
+
+InputWindowHandle::~InputWindowHandle() {}
+
+InputWindowHandle::InputWindowHandle(const InputWindowHandle& other) : mInfo(other.mInfo) {}
+
+InputWindowHandle::InputWindowHandle(const InputWindowInfo& other) : mInfo(other) {}
+
+status_t InputWindowHandle::writeToParcel(android::Parcel* parcel) const {
+ return mInfo.writeToParcel(parcel);
}
-InputWindowHandle::~InputWindowHandle() {
+status_t InputWindowHandle::readFromParcel(const android::Parcel* parcel) {
+ return mInfo.readFromParcel(parcel);
}
void InputWindowHandle::releaseChannel() {
@@ -166,5 +206,4 @@
void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) {
mInfo = handle->mInfo;
}
-
} // namespace android
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 56900c1..25025f2 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -105,8 +105,7 @@
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
const std::string& name) {
- std::string path(getPath(deviceIdentifier, name,
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
+ std::string path(getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_LAYOUT));
if (path.empty()) {
return NAME_NOT_FOUND;
}
@@ -122,8 +121,8 @@
status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
const std::string& name) {
- std::string path = getPath(deviceIdentifier, name,
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP);
+ std::string path =
+ getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_CHARACTER_MAP);
if (path.empty()) {
return NAME_NOT_FOUND;
}
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index c6cc4fc..7c28ac5 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -104,107 +104,73 @@
// --- VelocityTracker ---
-// The default velocity tracker strategy.
-// Although other strategies are available for testing and comparison purposes,
-// this is the strategy that applications will actually use. Be very careful
-// when adjusting the default strategy because it can dramatically affect
-// (often in a bad way) the user experience.
-const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
-
-VelocityTracker::VelocityTracker(const char* strategy) :
- mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
- char value[PROPERTY_VALUE_MAX];
-
- // Allow the default strategy to be overridden using a system property for debugging.
- if (!strategy) {
- int length = property_get("persist.input.velocitytracker.strategy", value, nullptr);
- if (length > 0) {
- strategy = value;
- } else {
- strategy = DEFAULT_STRATEGY;
- }
- }
-
+VelocityTracker::VelocityTracker(const Strategy strategy)
+ : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
// Configure the strategy.
if (!configureStrategy(strategy)) {
- ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
- if (!configureStrategy(DEFAULT_STRATEGY)) {
- LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
- strategy);
+ ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
+ if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) {
+ LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32
+ "'!",
+ strategy);
}
}
}
VelocityTracker::~VelocityTracker() {
- delete mStrategy;
}
-bool VelocityTracker::configureStrategy(const char* strategy) {
- mStrategy = createStrategy(strategy);
+bool VelocityTracker::configureStrategy(Strategy strategy) {
+ if (strategy == VelocityTracker::Strategy::DEFAULT) {
+ mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
+ } else {
+ mStrategy = createStrategy(strategy);
+ }
return mStrategy != nullptr;
}
-VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
- if (!strcmp("impulse", strategy)) {
- // Physical model of pushing an object. Quality: VERY GOOD.
- // Works with duplicate coordinates, unclean finger liftoff.
- return new ImpulseVelocityTrackerStrategy();
- }
- if (!strcmp("lsq1", strategy)) {
- // 1st order least squares. Quality: POOR.
- // Frequently underfits the touch data especially when the finger accelerates
- // or changes direction. Often underestimates velocity. The direction
- // is overly influenced by historical touch points.
- return new LeastSquaresVelocityTrackerStrategy(1);
- }
- if (!strcmp("lsq2", strategy)) {
- // 2nd order least squares. Quality: VERY GOOD.
- // Pretty much ideal, but can be confused by certain kinds of touch data,
- // particularly if the panel has a tendency to generate delayed,
- // duplicate or jittery touch coordinates when the finger is released.
- return new LeastSquaresVelocityTrackerStrategy(2);
- }
- if (!strcmp("lsq3", strategy)) {
- // 3rd order least squares. Quality: UNUSABLE.
- // Frequently overfits the touch data yielding wildly divergent estimates
- // of the velocity when the finger is released.
- return new LeastSquaresVelocityTrackerStrategy(3);
- }
- if (!strcmp("wlsq2-delta", strategy)) {
- // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
- }
- if (!strcmp("wlsq2-central", strategy)) {
- // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
- }
- if (!strcmp("wlsq2-recent", strategy)) {
- // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
- }
- if (!strcmp("int1", strategy)) {
- // 1st order integrating filter. Quality: GOOD.
- // Not as good as 'lsq2' because it cannot estimate acceleration but it is
- // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate
- // the velocity of a fling but this strategy tends to respond to changes in
- // direction more quickly and accurately.
- return new IntegratingVelocityTrackerStrategy(1);
- }
- if (!strcmp("int2", strategy)) {
- // 2nd order integrating filter. Quality: EXPERIMENTAL.
- // For comparison purposes only. Unlike 'int1' this strategy can compensate
- // for acceleration but it typically overestimates the effect.
- return new IntegratingVelocityTrackerStrategy(2);
- }
- if (!strcmp("legacy", strategy)) {
- // Legacy velocity tracker algorithm. Quality: POOR.
- // For comparison purposes only. This algorithm is strongly influenced by
- // old data points, consistently underestimates velocity and takes a very long
- // time to adjust to changes in direction.
- return new LegacyVelocityTrackerStrategy();
+std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
+ VelocityTracker::Strategy strategy) {
+ switch (strategy) {
+ case VelocityTracker::Strategy::IMPULSE:
+ return std::make_unique<ImpulseVelocityTrackerStrategy>();
+
+ case VelocityTracker::Strategy::LSQ1:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
+
+ case VelocityTracker::Strategy::LSQ2:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
+
+ case VelocityTracker::Strategy::LSQ3:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(3);
+
+ case VelocityTracker::Strategy::WLSQ2_DELTA:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_DELTA);
+ case VelocityTracker::Strategy::WLSQ2_CENTRAL:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_CENTRAL);
+ case VelocityTracker::Strategy::WLSQ2_RECENT:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_RECENT);
+
+ case VelocityTracker::Strategy::INT1:
+ return std::make_unique<IntegratingVelocityTrackerStrategy>(1);
+
+ case VelocityTracker::Strategy::INT2:
+ return std::make_unique<IntegratingVelocityTrackerStrategy>(2);
+
+ case VelocityTracker::Strategy::LEGACY:
+ return std::make_unique<LegacyVelocityTrackerStrategy>();
+
+ default:
+ break;
}
return nullptr;
}
diff --git a/libs/input/android/FocusRequest.aidl b/libs/input/android/FocusRequest.aidl
new file mode 100644
index 0000000..a5034a4
--- /dev/null
+++ b/libs/input/android/FocusRequest.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android;
+
+/** @hide */
+parcelable FocusRequest {
+ /**
+ * Input channel token used to identify the window that should gain focus.
+ */
+ IBinder token;
+ /**
+ * The token that the caller expects currently to be focused. If the
+ * specified token does not match the currently focused window, this request will be dropped.
+ * If the specified focused token matches the currently focused window, the call will succeed.
+ * Set this to "null" if this call should succeed no matter what the currently focused token
+ * is.
+ */
+ @nullable IBinder focusedToken;
+ /**
+ * SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm) 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.
+ */
+ long timestamp;
+}
diff --git a/libs/input/android/InputApplicationInfo.aidl b/libs/input/android/InputApplicationInfo.aidl
new file mode 100644
index 0000000..9336039
--- /dev/null
+++ b/libs/input/android/InputApplicationInfo.aidl
@@ -0,0 +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.
+ */
+
+package android;
+
+parcelable InputApplicationInfo {
+ @nullable IBinder token;
+ @utf8InCpp String name;
+ long dispatchingTimeoutMillis;
+}
diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/InputChannel.aidl
new file mode 100644
index 0000000..c2d1112
--- /dev/null
+++ b/libs/input/android/InputChannel.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/InputChannel.aidl
+**
+** 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.
+*/
+
+package android;
+
+parcelable InputChannel cpp_header "input/InputTransport.h";
diff --git a/libs/input/android/InputWindowInfo.aidl b/libs/input/android/InputWindowInfo.aidl
new file mode 100644
index 0000000..eeaf400
--- /dev/null
+++ b/libs/input/android/InputWindowInfo.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/InputChannel.aidl
+**
+** 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.
+*/
+
+package android;
+
+parcelable InputWindowInfo cpp_header "input/InputWindow.h";
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
new file mode 100644
index 0000000..82c220f
--- /dev/null
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+
+/** @hide */
+interface IInputConstants
+{
+ const int DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
+}
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
new file mode 100644
index 0000000..5eefad3
--- /dev/null
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -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.
+ */
+
+package android.os;
+
+import android.FocusRequest;
+import android.InputChannel;
+import android.InputWindowInfo;
+import android.os.ISetInputWindowsListener;
+
+/** @hide */
+interface IInputFlinger
+{
+ // SurfaceFlinger is the caller of this method, it uses the listener callback to ensure the
+ // ordering when needed.
+ // SurfaceFlinger calls this only every VSync, so overflow of binder's oneway buffer
+ // shouldn't be a concern.
+ oneway void setInputWindows(in InputWindowInfo[] inputHandles,
+ in @nullable ISetInputWindowsListener setInputWindowsListener);
+ void registerInputChannel(in InputChannel channel);
+ void unregisterInputChannel(in InputChannel channel);
+ /**
+ * Sets focus to the window identified by the token. This must be called
+ * after updating any input window handles.
+ */
+ oneway void setFocusedWindow(in FocusRequest request);
+}
diff --git a/libs/input/android/os/ISetInputWindowsListener.aidl b/libs/input/android/os/ISetInputWindowsListener.aidl
new file mode 100644
index 0000000..bb58fb6
--- /dev/null
+++ b/libs/input/android/os/ISetInputWindowsListener.aidl
@@ -0,0 +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.
+ */
+
+package android.os;
+
+/** @hide */
+oneway interface ISetInputWindowsListener
+{
+ void onSetInputWindowsFinished();
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 3b57146..9782c1a 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -2,6 +2,7 @@
cc_test {
name: "libinput_tests",
srcs: [
+ "Flags_test.cpp",
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
"InputDevice_test.cpp",
diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp
new file mode 100644
index 0000000..0dbb4cf
--- /dev/null
+++ b/libs/input/tests/Flags_test.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <input/Flags.h>
+
+#include <type_traits>
+
+namespace android::test {
+
+using namespace android::flag_operators;
+
+enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
+
+TEST(Flags, Test) {
+ Flags<TestFlags> flags = TestFlags::ONE;
+ ASSERT_TRUE(flags.test(TestFlags::ONE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+ ASSERT_FALSE(flags.test(TestFlags::THREE));
+}
+
+TEST(Flags, Any) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_TRUE(flags.any(TestFlags::ONE));
+ ASSERT_TRUE(flags.any(TestFlags::TWO));
+ ASSERT_FALSE(flags.any(TestFlags::THREE));
+ ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO));
+ ASSERT_TRUE(flags.any(TestFlags::TWO | TestFlags::THREE));
+ ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, All) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_TRUE(flags.all(TestFlags::ONE));
+ ASSERT_TRUE(flags.all(TestFlags::TWO));
+ ASSERT_FALSE(flags.all(TestFlags::THREE));
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO));
+ ASSERT_FALSE(flags.all(TestFlags::TWO | TestFlags::THREE));
+ ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, DefaultConstructor_hasNoFlagsSet) {
+ Flags<TestFlags> flags;
+ ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, NotOperator_onEmptyFlagsSetsAllFlags) {
+ Flags<TestFlags> flags;
+ flags = ~flags;
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, NotOperator_onNonEmptyFlagsInvertsFlags) {
+ Flags<TestFlags> flags = TestFlags::TWO;
+ flags = ~flags;
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+}
+
+TEST(Flags, OrOperator_withNewFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE;
+ Flags<TestFlags> flags2 = flags | TestFlags::TWO;
+ ASSERT_FALSE(flags2.test(TestFlags::THREE));
+ ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, OrOperator_withExistingFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> flags2 = flags | TestFlags::THREE;
+ ASSERT_FALSE(flags2.test(TestFlags::TWO));
+ ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::THREE));
+}
+
+TEST(Flags, OrEqualsOperator_withNewFlag) {
+ Flags<TestFlags> flags;
+ flags |= TestFlags::THREE;
+ ASSERT_TRUE(flags.test(TestFlags::THREE));
+ ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, OrEqualsOperator_withExistingFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ flags |= TestFlags::THREE;
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withOneSetFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> andFlags = flags & TestFlags::THREE;
+ ASSERT_TRUE(andFlags.test(TestFlags::THREE));
+ ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withMultipleSetFlags) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> andFlags = flags & (TestFlags::ONE | TestFlags::THREE);
+ ASSERT_TRUE(andFlags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(andFlags.test(TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withNoSetFlags) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> andFlags = flags & TestFlags::TWO;
+ ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, Equality) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_EQ(flags1, flags2);
+}
+
+TEST(Flags, Inequality) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::THREE;
+ ASSERT_NE(flags1, flags2);
+}
+
+TEST(Flags, EqualsOperator) {
+ Flags<TestFlags> flags;
+ flags = TestFlags::ONE;
+ ASSERT_TRUE(flags.test(TestFlags::ONE));
+ ASSERT_FALSE(flags.any(TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, EqualsOperator_DontShareState) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2 = flags1;
+ ASSERT_EQ(flags1, flags2);
+
+ flags1 &= TestFlags::TWO;
+ ASSERT_NE(flags1, flags2);
+}
+
+TEST(Flags, String_NoFlags) {
+ Flags<TestFlags> flags;
+ ASSERT_EQ(flags.string(), "0x0");
+}
+
+TEST(Flags, String_KnownValues) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_EQ(flags.string(), "ONE | TWO");
+}
+
+TEST(Flags, String_UnknownValues) {
+ auto flags = Flags<TestFlags>(0b1011);
+ ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008");
+}
+
+TEST(FlagsIterator, IteratesOverAllFlags) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2;
+ for (TestFlags f : flags1) {
+ flags2 |= f;
+ }
+ ASSERT_EQ(flags2, flags1);
+}
+
+TEST(FlagsIterator, IteratesInExpectedOrder) {
+ const std::vector<TestFlags> flagOrder = {TestFlags::ONE, TestFlags::TWO};
+ Flags<TestFlags> flags;
+ for (TestFlags f : flagOrder) {
+ flags |= f;
+ }
+
+ size_t idx = 0;
+ auto iter = flags.begin();
+ while (iter != flags.end() && idx < flagOrder.size()) {
+ // Make sure the order is what we expect
+ ASSERT_EQ(*iter, flagOrder[idx]);
+ iter++;
+ idx++;
+ }
+ ASSERT_EQ(iter, flags.end());
+}
+TEST(FlagsIterator, PostFixIncrement) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ auto iter = flags.begin();
+ ASSERT_EQ(*(iter++), TestFlags::ONE);
+ ASSERT_EQ(*iter, TestFlags::TWO);
+ ASSERT_EQ(*(iter++), TestFlags::TWO);
+ ASSERT_EQ(iter, flags.end());
+}
+
+TEST(FlagsIterator, PreFixIncrement) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ auto iter = flags.begin();
+ ASSERT_EQ(*++iter, TestFlags::TWO);
+ ASSERT_EQ(++iter, flags.end());
+}
+
+TEST(FlagNames, RuntimeFlagName) {
+ TestFlags f = TestFlags::ONE;
+ ASSERT_EQ(flag_name(f), "ONE");
+}
+
+TEST(FlagNames, RuntimeUnknownFlagName) {
+ TestFlags f = static_cast<TestFlags>(0x8);
+ ASSERT_EQ(flag_name(f), std::nullopt);
+}
+
+TEST(FlagNames, CompileTimeFlagName) {
+ static_assert(flag_name<TestFlags::TWO>() == "TWO");
+}
+
+} // namespace android::test
\ No newline at end of file
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index ada275d..0661261 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -23,6 +23,7 @@
#include <errno.h>
#include <binder/Binder.h>
+#include <binder/Parcel.h>
#include <gtest/gtest.h>
#include <input/InputTransport.h>
#include <utils/StopWatch.h>
@@ -32,9 +33,6 @@
namespace android {
class InputChannelTest : public testing::Test {
-protected:
- virtual void SetUp() { }
- virtual void TearDown() { }
};
@@ -46,7 +44,7 @@
android::base::unique_fd sendFd(pipe.sendFd);
- sp<InputChannel> inputChannel =
+ std::unique_ptr<InputChannel> inputChannel =
InputChannel::create("channel name", std::move(sendFd), new BBinder());
EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created";
@@ -61,14 +59,14 @@
TEST_F(InputChannelTest, SetAndGetToken) {
Pipe pipe;
sp<IBinder> token = new BBinder();
- sp<InputChannel> channel =
+ std::unique_ptr<InputChannel> channel =
InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token);
EXPECT_EQ(token, channel->getConnectionToken());
}
TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -102,7 +100,7 @@
InputMessage clientReply;
memset(&clientReply, 0, sizeof(InputMessage));
clientReply.header.type = InputMessage::Type::FINISHED;
- clientReply.body.finished.seq = 0x11223344;
+ clientReply.header.seq = 0x11223344;
clientReply.body.finished.handled = true;
EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply))
<< "client channel should be able to send message to server channel";
@@ -112,14 +110,14 @@
<< "server channel should be able to receive message from client channel";
EXPECT_EQ(clientReply.header.type, serverReply.header.type)
<< "server channel should receive the correct message from client channel";
- EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq)
+ EXPECT_EQ(clientReply.header.seq, serverReply.header.seq)
<< "server channel should receive the correct message from client channel";
EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled)
<< "server channel should receive the correct message from client channel";
}
TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -133,7 +131,7 @@
}
TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -141,7 +139,7 @@
ASSERT_EQ(OK, result)
<< "should have successfully opened a channel pair";
- serverChannel.clear(); // close server channel
+ serverChannel.reset(); // close server channel
InputMessage msg;
EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg))
@@ -149,7 +147,7 @@
}
TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -157,7 +155,7 @@
ASSERT_EQ(OK, result)
<< "should have successfully opened a channel pair";
- serverChannel.clear(); // close server channel
+ serverChannel.reset(); // close server channel
InputMessage msg;
msg.header.type = InputMessage::Type::KEY;
@@ -166,7 +164,7 @@
}
TEST_F(InputChannelTest, SendAndReceive_MotionClassification) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
ASSERT_EQ(OK, result)
@@ -180,7 +178,7 @@
InputMessage serverMsg = {}, clientMsg;
serverMsg.header.type = InputMessage::Type::MOTION;
- serverMsg.body.motion.seq = 1;
+ serverMsg.header.seq = 1;
serverMsg.body.motion.pointerCount = 1;
for (MotionClassification classification : classifications) {
@@ -197,5 +195,36 @@
}
}
+TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+ status_t result =
+ InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+ InputChannel chan;
+ Parcel parcel;
+ ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel));
+ parcel.setDataPosition(0);
+ chan.readFromParcel(&parcel);
+
+ EXPECT_EQ(chan == *serverChannel, true)
+ << "inputchannel should be equal after parceling and unparceling.\n"
+ << "name " << chan.getName() << " name " << serverChannel->getName();
+}
+
+TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+ status_t result =
+ InputChannel::openInputChannelPair("channel dup", serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+ std::unique_ptr<InputChannel> dupChan = serverChannel->dup();
+
+ EXPECT_EQ(*serverChannel == *dupChan, true) << "inputchannel should be equal after duplication";
+}
} // namespace android
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 553dc4c..069bc0e 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -225,6 +225,7 @@
static constexpr float Y_OFFSET = 1.1;
int32_t mId;
+ ui::Transform mTransform;
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
@@ -233,6 +234,7 @@
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
mId = InputEvent::nextId();
+ mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
PointerProperties pointerProperties[2];
pointerProperties[0].clear();
@@ -266,7 +268,7 @@
event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
- MotionClassification::NONE, X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+ MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
pointerCoords);
@@ -326,8 +328,7 @@
ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
ASSERT_EQ(MotionClassification::NONE, event->getClassification());
- EXPECT_EQ(X_SCALE, event->getXScale());
- EXPECT_EQ(Y_SCALE, event->getYScale());
+ EXPECT_EQ(mTransform, event->getTransform());
ASSERT_EQ(X_OFFSET, event->getXOffset());
ASSERT_EQ(Y_OFFSET, event->getYOffset());
ASSERT_EQ(2.0f, event->getXPrecision());
@@ -545,7 +546,7 @@
ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent));
}
-static void setRotationMatrix(float matrix[9], float angle) {
+static void setRotationMatrix(std::array<float, 9>& matrix, float angle) {
float sin = sinf(angle);
float cos = cosf(angle);
matrix[0] = cos;
@@ -584,13 +585,14 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
}
MotionEvent event;
+ ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
- 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/,
- 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
+ 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+ pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -606,7 +608,7 @@
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
// Apply a rotation about the origin by ROTATION degrees clockwise.
- float matrix[9];
+ std::array<float, 9> matrix;
setRotationMatrix(matrix, ROTATION * PI_180);
event.transform(matrix);
@@ -648,11 +650,12 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
for (MotionClassification classification : classifications) {
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
- AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 1 /*xScale*/,
- 1 /*yScale*/, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
@@ -670,10 +673,11 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
- AMETA_NONE, 0, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0,
- 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
+ AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 8e2eec8..e1f2562 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -30,33 +30,21 @@
class InputPublisherAndConsumerTest : public testing::Test {
protected:
- sp<InputChannel> serverChannel, clientChannel;
- InputPublisher* mPublisher;
- InputConsumer* mConsumer;
+ std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
+ std::unique_ptr<InputPublisher> mPublisher;
+ std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
- virtual void SetUp() {
+ void SetUp() override {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
ASSERT_EQ(OK, result);
+ mServerChannel = std::move(serverChannel);
+ mClientChannel = std::move(clientChannel);
- mPublisher = new InputPublisher(serverChannel);
- mConsumer = new InputConsumer(clientChannel);
- }
-
- virtual void TearDown() {
- if (mPublisher) {
- delete mPublisher;
- mPublisher = nullptr;
- }
-
- if (mConsumer) {
- delete mConsumer;
- mConsumer = nullptr;
- }
-
- serverChannel.clear();
- clientChannel.clear();
+ mPublisher = std::make_unique<InputPublisher>(mServerChannel);
+ mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
void PublishAndConsumeKeyEvent();
@@ -65,8 +53,8 @@
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
- EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
- EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
+ EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get());
+ EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get());
}
void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
@@ -185,12 +173,13 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
}
+ ui::Transform transform;
+ transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
actionButton, flags, edgeFlags, metaState, buttonState,
- classification, xScale, yScale, xOffset, yOffset,
- xPrecision, yPrecision, xCursorPosition,
- yCursorPosition, downTime, eventTime, pointerCount,
- pointerProperties, pointerCoords);
+ classification, transform, xPrecision, yPrecision,
+ xCursorPosition, yCursorPosition, downTime, eventTime,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -218,8 +207,7 @@
EXPECT_EQ(metaState, motionEvent->getMetaState());
EXPECT_EQ(buttonState, motionEvent->getButtonState());
EXPECT_EQ(classification, motionEvent->getClassification());
- EXPECT_EQ(xScale, motionEvent->getXScale());
- EXPECT_EQ(yScale, motionEvent->getYScale());
+ EXPECT_EQ(transform, motionEvent->getTransform());
EXPECT_EQ(xOffset, motionEvent->getXOffset());
EXPECT_EQ(yOffset, motionEvent->getYOffset());
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
@@ -338,10 +326,10 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
@@ -354,10 +342,10 @@
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
+ ui::Transform identityTransform;
status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
@@ -375,10 +363,10 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index d1cb527..7e3a40d 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -22,17 +22,19 @@
#include <input/InputWindow.h>
#include <input/InputTransport.h>
+using std::chrono_literals::operator""s;
+
namespace android {
namespace test {
TEST(InputWindowInfo, ParcellingWithoutToken) {
- InputWindowInfo i;
+ InputWindowInfo i, i2;
i.token = nullptr;
Parcel p;
- ASSERT_EQ(OK, i.write(p));
+ ASSERT_EQ(OK, i.writeToParcel(&p));
p.setDataPosition(0);
- InputWindowInfo i2 = InputWindowInfo::read(p);
+ i2.readFromParcel(&p);
ASSERT_TRUE(i2.token == nullptr);
}
@@ -42,17 +44,16 @@
i.token = new BBinder();
i.id = 1;
i.name = "Foobar";
- i.layoutParamsFlags = 7;
- i.layoutParamsType = 39;
- i.dispatchingTimeout = 12;
+ i.flags = InputWindowInfo::Flag::SLIPPERY;
+ i.type = InputWindowInfo::Type::INPUT_METHOD;
+ i.dispatchingTimeout = 12s;
i.frameLeft = 93;
i.frameTop = 34;
i.frameRight = 16;
i.frameBottom = 19;
i.surfaceInset = 17;
i.globalScaleFactor = 0.3;
- i.windowXScale = 0.4;
- i.windowYScale = 0.5;
+ i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
i.visible = false;
i.canReceiveKeys = false;
i.hasFocus = false;
@@ -60,22 +61,25 @@
i.paused = false;
i.ownerPid = 19;
i.ownerUid = 24;
- i.inputFeatures = 29;
+ i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY;
i.displayId = 34;
i.portalToDisplayId = 2;
i.replaceTouchableRegionWithCrop = true;
i.touchableRegionCropHandle = touchableRegionCropHandle;
+ i.applicationInfo.name = "ApplicationFooBar";
+ i.applicationInfo.token = new BBinder();
+ i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD;
Parcel p;
- i.write(p);
-
+ i.writeToParcel(&p);
p.setDataPosition(0);
- InputWindowInfo i2 = InputWindowInfo::read(p);
+ InputWindowInfo i2;
+ i2.readFromParcel(&p);
ASSERT_EQ(i.token, i2.token);
ASSERT_EQ(i.id, i2.id);
ASSERT_EQ(i.name, i2.name);
- ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
- ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
+ ASSERT_EQ(i.flags, i2.flags);
+ ASSERT_EQ(i.type, i2.type);
ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout);
ASSERT_EQ(i.frameLeft, i2.frameLeft);
ASSERT_EQ(i.frameTop, i2.frameTop);
@@ -83,8 +87,7 @@
ASSERT_EQ(i.frameBottom, i2.frameBottom);
ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
- ASSERT_EQ(i.windowXScale, i2.windowXScale);
- ASSERT_EQ(i.windowYScale, i2.windowYScale);
+ ASSERT_EQ(i.transform, i2.transform);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys);
ASSERT_EQ(i.hasFocus, i2.hasFocus);
@@ -97,6 +100,21 @@
ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
+ ASSERT_EQ(i.applicationInfo, i2.applicationInfo);
+}
+
+TEST(InputApplicationInfo, Parcelling) {
+ InputApplicationInfo i;
+ i.token = new BBinder();
+ i.name = "ApplicationFooBar";
+ i.dispatchingTimeoutMillis = 0x12345678ABCD;
+
+ Parcel p;
+ ASSERT_EQ(i.writeToParcel(&p), OK);
+ p.setDataPosition(0);
+ InputApplicationInfo i2;
+ ASSERT_EQ(i2.readFromParcel(&p), OK);
+ ASSERT_EQ(i, i2);
}
} // namespace test
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 1fe7bb9..3c5fb22 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -34,8 +34,7 @@
void TestInputMessageAlignment() {
CHECK_OFFSET(InputMessage, body, 8);
- CHECK_OFFSET(InputMessage::Body::Key, seq, 0);
- CHECK_OFFSET(InputMessage::Body::Key, eventId, 4);
+ CHECK_OFFSET(InputMessage::Body::Key, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Key, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Key, source, 20);
@@ -49,8 +48,7 @@
CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80);
CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
- CHECK_OFFSET(InputMessage::Body::Motion, seq, 0);
- CHECK_OFFSET(InputMessage::Body::Motion, eventId, 4);
+ CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
@@ -64,27 +62,29 @@
CHECK_OFFSET(InputMessage::Body::Motion, classification, 80);
CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84);
CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88);
- CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96);
- CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100);
- CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104);
- CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108);
- CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112);
- CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116);
- CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120);
- CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdx, 96);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdx, 100);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdy, 104);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdy, 108);
+ CHECK_OFFSET(InputMessage::Body::Motion, tx, 112);
+ CHECK_OFFSET(InputMessage::Body::Motion, ty, 116);
+ CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 120);
+ CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
+ CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
+ CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144);
- CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
- CHECK_OFFSET(InputMessage::Body::Focus, eventId, 4);
- CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 12);
- CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 14);
+ CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
+ CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
- CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
}
void TestHeaderSize() {
+ CHECK_OFFSET(InputMessage::Header, type, 0);
+ CHECK_OFFSET(InputMessage::Header, seq, 4);
static_assert(sizeof(InputMessage::Header) == 8);
}
@@ -98,7 +98,7 @@
offsetof(InputMessage::Body::Motion, pointers) +
sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
static_assert(sizeof(InputMessage::Body::Finished) == 8);
- static_assert(sizeof(InputMessage::Body::Focus) == 16);
+ static_assert(sizeof(InputMessage::Body::Focus) == 8);
}
// --- VerifiedInputEvent ---
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index bf452c0..e7db4b0 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -176,12 +176,12 @@
EXPECT_EQ(pointerIndex, pointerCount);
MotionEvent event;
+ ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
- 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
entry.eventTime.count(), pointerCount, properties, coords);
@@ -191,8 +191,9 @@
return events;
}
-static void computeAndCheckVelocity(const char* strategy,
- const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity) {
+static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
+ const std::vector<MotionEventEntry>& motions, int32_t axis,
+ float targetVelocity) {
VelocityTracker vt(strategy);
float Vx, Vy;
@@ -217,7 +218,7 @@
static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions,
const std::array<float, 3>& coefficients) {
- VelocityTracker vt("lsq2");
+ VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
std::vector<MotionEvent> events = createMotionEventStream(motions);
for (MotionEvent event : events) {
vt.addMovement(&event);
@@ -238,36 +239,34 @@
// It is difficult to determine the correct answer here, but at least the direction
// of the reported velocity should be positive.
std::vector<MotionEventEntry> motions = {
- {0ms, {{ 273, NAN}}},
- {12585us, {{293, NAN}}},
- {14730us, {{293, NAN}}},
- {14730us, {{293, NAN}}}, // ACTION_UP
+ {0ms, {{273, 0}}},
+ {12585us, {{293, 0}}},
+ {14730us, {{293, 0}}},
+ {14730us, {{293, 0}}}, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1600);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 1600);
}
TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
// Same coordinate is reported 3 times in a row
std::vector<MotionEventEntry> motions = {
- { 0ms, {{293, NAN}} },
- { 6132us, {{293, NAN}} },
- { 11283us, {{293, NAN}} },
- { 11283us, {{293, NAN}} }, // ACTION_UP
+ {0ms, {{293, 0}}},
+ {6132us, {{293, 0}}},
+ {11283us, {{293, 0}}},
+ {11283us, {{293, 0}}}, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
}
TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
// Fixed velocity at 5 points per 10 milliseconds
std::vector<MotionEventEntry> motions = {
- { 0ms, {{0, NAN}} },
- { 10ms, {{5, NAN}} },
- { 20ms, {{10, NAN}} },
- { 20ms, {{10, NAN}} }, // ACTION_UP
+ {0ms, {{0, 0}}}, {10ms, {{5, 0}}}, {20ms, {{10, 0}}}, {20ms, {{10, 0}}}, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 500);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500);
}
@@ -297,8 +296,10 @@
{ 96948871ns, {{274.79245, 428.113159}} },
{ 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 623.577637);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 5970.7309);
}
// --------------- Recorded by hand on sailfish, generated by a script -----------------------------
@@ -339,10 +340,14 @@
{ 235089162955851ns, {{560.66, 843.82}} },
{ 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181);
- computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 872.794617);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 951.698181);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -3604.819336);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3044.966064);
}
@@ -368,8 +373,10 @@
{ 235110660368000ns, {{530.00, 980.00}} },
{ 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -4096.583008);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3455.094238);
}
@@ -396,10 +403,14 @@
{ 792629200000ns, {{619.00, 1115.00}} },
{ 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 574.33429);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 617.40564);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -2361.982666);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -2500.055664);
}
@@ -426,10 +437,14 @@
{ 235160520366000ns, {{679.00, 814.00}} },
{ 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 1274.141724);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 1438.53186);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -3001.4348);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3695.859619);
}
@@ -452,8 +467,10 @@
{ 847237986000ns, {{610.00, 1095.00}} },
{ 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -4280.07959);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -4241.004395);
}
@@ -476,8 +493,10 @@
{ 235200616933000ns, {{590.00, 844.00}} },
{ 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -8715.686523);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -7639.026367);
}
@@ -499,10 +518,14 @@
{ 920989261000ns, {{715.00, 903.00}} },
{ 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 5670.329102);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 5991.866699);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -13021.101562);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -15093.995117);
}
@@ -522,8 +545,10 @@
{ 235247220736000ns, {{620.00, 641.00}} },
{ 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -20286.958984);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -20494.587891);
}
@@ -541,8 +566,10 @@
{ 235302613019881ns, {{679.26, 526.73}} },
{ 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -39295.941406);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -36461.421875);
}
@@ -569,10 +596,14 @@
{ 235655842893000ns, {{563.00, 649.00}} },
{ 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -419.749695);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -398.303894);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 3309.016357);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 3969.099854);
}
@@ -599,10 +630,14 @@
{ 235671246532000ns, {{470.00, 799.00}} },
{ 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -262.80426);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -243.665344);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4215.682129);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4587.986816);
}
@@ -622,10 +657,14 @@
{ 171051052000ns, {{536.00, 586.00}} },
{ 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -723.413513);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -651.038452);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 2091.502441);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 1934.517456);
}
@@ -652,8 +691,10 @@
{ 235695373403000ns, {{564.00, 744.00}} },
{ 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4254.639648);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4698.415039);
}
@@ -677,10 +718,14 @@
{ 235709710626776ns, {{511.72, 741.85}} },
{ 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -430.440247);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -447.600311);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 3953.859375);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4316.155273);
}
@@ -706,8 +751,10 @@
{ 235727721580000ns, {{516.00, 658.00}} },
{ 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4484.617676);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4927.92627);
}
@@ -725,8 +772,10 @@
{ 235762396429369ns, {{404.37, 680.67}} },
{ 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 14227.0224);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 16064.685547);
}
@@ -744,8 +793,10 @@
{ 235772537635000ns, {{484.00, 589.00}} },
{ 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 18660.048828);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 16918.439453);
}
@@ -764,10 +815,14 @@
{ 507703352649ns, {{443.71, 857.77}} },
{ 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -4111.8173);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -6388.48877);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 29765.908203);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 28354.796875);
}
/**
@@ -789,10 +844,10 @@
// Velocity should actually be zero, but we expect 0.016 here instead.
// This is close enough to zero, and is likely caused by division by a very small number.
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0);
}
/**
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index 4e8e840..21cfe8c 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -39,13 +39,14 @@
pointerCoords[i].clear();
}
+ ui::Transform transform;
+ transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/,
- 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/,
- 540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount,
- pointerProperties, pointerCoords);
+ MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 100 /*downTime*/,
+ 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
return event;
}
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
index 3b1edcb..22d4712 100644
--- a/libs/math/Android.bp
+++ b/libs/math/Android.bp
@@ -25,6 +25,11 @@
min_sdk_version: "29",
export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ }
+ }
}
subdirs = ["tests"]
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
index 7682973..617a0ab 100644
--- a/libs/math/include/math/half.h
+++ b/libs/math/include/math/half.h
@@ -82,6 +82,7 @@
};
public:
+ CONSTEXPR half() noexcept { }
CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { }
CONSTEXPR operator float() const noexcept { return htof(mBits); }
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
index 496a7ef..604072e 100644
--- a/libs/math/tests/half_test.cpp
+++ b/libs/math/tests/half_test.cpp
@@ -35,6 +35,7 @@
EXPECT_EQ(2UL, sizeof(half));
// test +/- zero
+ EXPECT_EQ(0x0000, half().getBits());
EXPECT_EQ(0x0000, half( 0.0f).getBits());
EXPECT_EQ(0x8000, half(-0.0f).getBits());
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index f6a95ce..ff1b5e6 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -370,9 +370,8 @@
}
void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
- ALOGV("choreographer %p ~ received hotplug event (displayId=%"
- ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
- this, displayId, toString(connected));
+ ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.",
+ this, to_string(displayId).c_str(), toString(connected));
}
// TODO(b/74619554): The PhysicalDisplayId is ignored because currently
@@ -382,9 +381,8 @@
// PhysicalDisplayId should no longer be ignored.
void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId,
nsecs_t) {
- ALOGV("choreographer %p ~ received config change event "
- "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).",
- this, displayId, configId);
+ ALOGV("choreographer %p ~ received config change event (displayId=%s, configId=%d).",
+ this, to_string(displayId).c_str(), configId);
}
void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb6080f..3dcb498 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -64,6 +64,13 @@
],
}
+filegroup {
+ name: "librenderengine_threaded_sources",
+ srcs: [
+ "threaded/RenderEngineThreaded.cpp",
+ ],
+}
+
cc_library_static {
name: "librenderengine",
defaults: ["librenderengine_defaults"],
@@ -80,6 +87,7 @@
srcs: [
":librenderengine_sources",
":librenderengine_gl_sources",
+ ":librenderengine_threaded_sources",
],
lto: {
thin: true,
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 0fdf093..c3fbb60 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -20,19 +20,34 @@
#include <log/log.h>
#include <private/gui/SyncFeatures.h>
#include "gl/GLESRenderEngine.h"
+#include "threaded/RenderEngineThreaded.h"
namespace android {
namespace renderengine {
-std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+ RenderEngineType renderEngineType = args.renderEngineType;
+
+ // Keep the ability to override by PROPERTIES:
char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles");
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
if (strcmp(prop, "gles") == 0) {
- ALOGD("RenderEngine GLES Backend");
- return renderengine::gl::GLESRenderEngine::create(args);
+ renderEngineType = RenderEngineType::GLES;
}
- ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
- return renderengine::gl::GLESRenderEngine::create(args);
+ if (strcmp(prop, "threaded") == 0) {
+ renderEngineType = RenderEngineType::THREADED;
+ }
+
+ 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::GLES:
+ default:
+ ALOGD("RenderEngine with GLES Backend");
+ return renderengine::gl::GLESRenderEngine::create(args);
+ }
}
RenderEngine::~RenderEngine() = default;
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 7c84a19..672ffae 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -51,8 +51,6 @@
#include "ProgramCache.h"
#include "filters/BlurFilter.h"
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
bool checkGlError(const char* op, int lineNumber) {
bool errorFound = false;
GLint error = glGetError();
@@ -208,16 +206,16 @@
LOG_ALWAYS_FATAL("failed to initialize EGL");
}
- const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION);
+ const auto eglVersion = eglQueryString(display, EGL_VERSION);
if (!eglVersion) {
checkGlError(__FUNCTION__, __LINE__);
- LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed");
+ LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
}
- const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS);
+ const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
if (!eglExtensions) {
checkGlError(__FUNCTION__, __LINE__);
- LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed");
+ LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
}
GLExtensions& extensions = GLExtensions::getInstance();
@@ -409,6 +407,23 @@
mImageManager = std::make_unique<ImageManager>(this);
mImageManager->initThread();
mDrawingBuffer = createFramebuffer();
+ sp<GraphicBuffer> buf =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "placeholder");
+
+ const status_t err = buf->initCheck();
+ if (err != OK) {
+ ALOGE("Error allocating placeholder buffer: %d", err);
+ return;
+ }
+ mPlaceholderBuffer = buf.get();
+ EGLint attributes[] = {
+ EGL_NONE,
+ };
+ mPlaceholderImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ mPlaceholderBuffer, attributes);
+ ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x",
+ eglGetError());
}
GLESRenderEngine::~GLESRenderEngine() {
@@ -423,6 +438,7 @@
eglDestroyImageKHR(mEGLDisplay, expired);
DEBUG_EGL_IMAGE_TRACKER_DESTROY();
}
+ eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
mImageCache.clear();
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mEGLDisplay);
@@ -589,6 +605,9 @@
}
void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) {
+ for (int i = 0; i < count; ++i) {
+ mTextureView.erase(names[i]);
+ }
glDeleteTextures(count, names);
}
@@ -646,6 +665,7 @@
}
bindExternalTextureImage(texName, *cachedImage->second);
+ mTextureView.insert_or_assign(texName, buffer->getId());
}
// Wait for the new buffer to be ready.
@@ -887,7 +907,7 @@
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
-bool GLESRenderEngine::cleanupPostRender() {
+bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) {
ATRACE_CALL();
if (mPriorResourcesCleaned ||
@@ -896,6 +916,30 @@
return false;
}
+ // This is a bit of a band-aid fix for FrameCaptureProcessor, as we should
+ // not need to keep memory around if we don't need to do so.
+ if (mode == CleanupMode::CLEAN_ALL) {
+ // TODO: SurfaceFlinger memory utilization may benefit from resetting
+ // texture bindings as well. Assess if it does and there's no performance regression
+ // when rebinding the same image data to the same texture, and if so then its mode
+ // behavior can be tweaked.
+ if (mPlaceholderImage != EGL_NO_IMAGE_KHR) {
+ for (auto [textureName, bufferId] : mTextureView) {
+ if (bufferId && mPlaceholderImage != EGL_NO_IMAGE_KHR) {
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
+ static_cast<GLeglImageOES>(mPlaceholderImage));
+ mTextureView[textureName] = std::nullopt;
+ checkErrors();
+ }
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mImageCache.clear();
+ }
+ }
+
// Bind the texture to placeholder so that backing image data can be freed.
GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
@@ -982,7 +1026,7 @@
status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* const buffer,
+ const sp<GraphicBuffer>& buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) {
ATRACE_CALL();
@@ -1019,7 +1063,9 @@
const auto blurLayersSize = blurLayers.size();
if (blurLayersSize == 0) {
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+ buffer.get()->getNativeBuffer(),
+ useFramebufferCache);
if (fbo->getStatus() != NO_ERROR) {
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
buffer->handle);
@@ -1076,7 +1122,9 @@
if (blurLayers.size() == 0) {
// Done blurring, time to bind the native FBO and render our blur onto it.
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+ buffer.get()
+ ->getNativeBuffer(),
useFramebufferCache);
status = fbo->getStatus();
setViewportAndProjection(display.physicalDisplay, display.clip);
@@ -1616,6 +1664,16 @@
return cachedImage != mImageCache.end();
}
+bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) {
+ const auto& entry = mTextureView.find(texName);
+ return entry != mTextureView.end();
+}
+
+std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) {
+ const auto& entry = mTextureView.find(texName);
+ return entry != mTextureView.end() ? entry->second : std::nullopt;
+}
+
bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 61986ff..2c6eae2 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -73,9 +73,9 @@
bool useProtectedContext(bool useProtectedContext) override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* buffer, const bool useFramebufferCache,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
- bool cleanupPostRender() override;
+ bool cleanupPostRender(CleanupMode mode) override;
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
// Creates an output image for rendering to
@@ -86,6 +86,12 @@
// Test-only methods
// Returns true iff mImageCache contains an image keyed by bufferId
bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ // Returns true iff texName was previously generated by RenderEngine and was
+ // not destroyed.
+ bool isTextureNameKnownForTesting(uint32_t texName);
+ // Returns the buffer ID of the content bound to texName, or nullopt if no
+ // such mapping exists.
+ std::optional<uint64_t> getBufferIdForTextureNameForTesting(uint32_t texName);
// Returns true iff mFramebufferImageCache contains an image keyed by bufferId
bool isFramebufferImageCachedForTesting(uint64_t bufferId)
EXCLUDES(mFramebufferImageCacheMutex);
@@ -224,6 +230,8 @@
// Cache of GL images that we'll store per GraphicBuffer ID
std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
+ std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView;
+
// Mutex guarding rendering operations, so that:
// 1. GL operations aren't interleaved, and
// 2. Internal state related to rendering that is potentially modified by
@@ -237,6 +245,11 @@
// ensure that we align on a word. Allocating 16 bytes will provide a
// guarantee that we don't clobber memory.
uint32_t mPlaceholderDrawBuffer[4];
+ // Placeholder buffer and image, similar to mPlaceholderDrawBuffer, but
+ // instead these are intended for cleaning up texture memory with the
+ // GL_TEXTURE_EXTERNAL_OES target.
+ ANativeWindowBuffer* mPlaceholderBuffer = nullptr;
+ EGLImage mPlaceholderImage = EGL_NO_IMAGE_KHR;
sp<Fence> mLastDrawFence;
// Store a separate boolean checking if prior resources were cleaned up, as
// devices that don't support native sync fences can't rely on a last draw
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index e06e128..09a0f65 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -50,6 +50,10 @@
class Texture;
struct RenderEngineCreationArgs;
+namespace threaded {
+class RenderEngineThreaded;
+}
+
namespace impl {
class RenderEngine;
}
@@ -67,7 +71,12 @@
HIGH = 3,
};
- static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args);
+ enum class RenderEngineType {
+ GLES = 1,
+ THREADED = 2,
+ };
+
+ static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
virtual ~RenderEngine() = 0;
@@ -111,14 +120,25 @@
// 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,
+ CLEAN_ALL,
+ };
// Clean-up method that should be called on the main thread after the
// drawFence returned by drawLayers fires. This method will free up
// resources used by the most recently drawn frame. If the frame is still
// being drawn, then this call is silently ignored.
//
+ // If mode is CLEAN_OUTPUT_RESOURCES, then only resources related to the
+ // output framebuffer are cleaned up, including the sibling texture.
+ //
+ // If mode is CLEAN_ALL, then we also cleanup resources related to any input
+ // buffers.
+ //
// Returns true if resources were cleaned up, and false if we didn't need to
// do any work.
- virtual bool cleanupPostRender() = 0;
+ virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;
// queries
virtual size_t getMaxTextureSize() const = 0;
@@ -163,7 +183,7 @@
// now, this always returns NO_ERROR.
virtual status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* buffer, const bool useFramebufferCache,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
protected:
@@ -174,6 +194,7 @@
// live longer than RenderEngine.
virtual Framebuffer* getFramebufferForDrawing() = 0;
friend class BindNativeBufferAsFramebuffer;
+ friend class threaded::RenderEngineThreaded;
};
struct RenderEngineCreationArgs {
@@ -184,26 +205,25 @@
bool precacheToneMapperShaderOnly;
bool supportsBackgroundBlur;
RenderEngine::ContextPriority contextPriority;
+ RenderEngine::RenderEngineType renderEngineType;
struct Builder;
private:
// must be created by Builder via constructor with full argument list
- RenderEngineCreationArgs(
- int _pixelFormat,
- uint32_t _imageCacheSize,
- bool _useColorManagement,
- bool _enableProtectedContext,
- bool _precacheToneMapperShaderOnly,
- bool _supportsBackgroundBlur,
- RenderEngine::ContextPriority _contextPriority)
- : pixelFormat(_pixelFormat)
- , imageCacheSize(_imageCacheSize)
- , useColorManagement(_useColorManagement)
- , enableProtectedContext(_enableProtectedContext)
- , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly)
- , supportsBackgroundBlur(_supportsBackgroundBlur)
- , contextPriority(_contextPriority) {}
+ RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement,
+ bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
+ bool _supportsBackgroundBlur,
+ RenderEngine::ContextPriority _contextPriority,
+ RenderEngine::RenderEngineType _renderEngineType)
+ : pixelFormat(_pixelFormat),
+ imageCacheSize(_imageCacheSize),
+ useColorManagement(_useColorManagement),
+ enableProtectedContext(_enableProtectedContext),
+ precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
+ supportsBackgroundBlur(_supportsBackgroundBlur),
+ contextPriority(_contextPriority),
+ renderEngineType(_renderEngineType) {}
RenderEngineCreationArgs() = delete;
};
@@ -238,10 +258,14 @@
this->contextPriority = contextPriority;
return *this;
}
+ Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) {
+ this->renderEngineType = renderEngineType;
+ return *this;
+ }
RenderEngineCreationArgs build() const {
return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
enableProtectedContext, precacheToneMapperShaderOnly,
- supportsBackgroundBlur, contextPriority);
+ supportsBackgroundBlur, contextPriority, renderEngineType);
}
private:
@@ -253,6 +277,7 @@
bool precacheToneMapperShaderOnly = false;
bool supportsBackgroundBlur = false;
RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
+ RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES;
};
class BindNativeBufferAsFramebuffer {
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index df0f17a..e03dd58 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -56,10 +56,11 @@
MOCK_CONST_METHOD0(isProtected, bool());
MOCK_CONST_METHOD0(supportsProtectedContent, bool());
MOCK_METHOD1(useProtectedContext, bool(bool));
- MOCK_METHOD0(cleanupPostRender, bool());
+ MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
MOCK_METHOD6(drawLayers,
status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*));
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+ base::unique_fd*));
};
} // namespace mock
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index e98babc..bcf389b 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -18,10 +18,12 @@
test_suites: ["device-tests"],
srcs: [
"RenderEngineTest.cpp",
+ "RenderEngineThreadedTest.cpp",
],
static_libs: [
"libgmock",
"librenderengine",
+ "librenderengine_mocks",
],
shared_libs: [
"libbase",
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 50e5550..ba17143 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -22,12 +22,13 @@
#include <condition_variable>
#include <fstream>
-#include <gtest/gtest.h>
#include <cutils/properties.h>
+#include <gtest/gtest.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
#include <ui/PixelFormat.h>
#include "../gl/GLESRenderEngine.h"
+#include "../threaded/RenderEngineThreaded.h"
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
@@ -40,14 +41,15 @@
static void SetUpTestSuite() {
sRE = renderengine::gl::GLESRenderEngine::create(
renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
- .setImageCacheSize(1)
- .setUseColorManagerment(false)
- .setEnableProtectedContext(false)
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(true)
- .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .build());
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setUseColorManagerment(false)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
+ .build());
}
static void TearDownTestSuite() {
@@ -81,6 +83,7 @@
}
for (uint32_t texName : mTexNames) {
sRE->deleteTextures(1, &texName);
+ EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
}
}
@@ -252,8 +255,8 @@
std::vector<const renderengine::LayerSettings*> layers,
sp<GraphicBuffer> buffer) {
base::unique_fd fence;
- status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
- base::unique_fd(), &fence);
+ status_t status =
+ sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
sCurrentBuffer = buffer;
int fd = fence.release();
@@ -1004,8 +1007,7 @@
layer.alpha = 1.0;
layers.push_back(&layer);
- status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true,
- base::unique_fd(), nullptr);
+ status_t status = sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
sCurrentBuffer = mBuffer;
ASSERT_EQ(NO_ERROR, status);
expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
@@ -1023,8 +1025,7 @@
layer.alpha = 1.0;
layers.push_back(&layer);
- status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false,
- base::unique_fd(), nullptr);
+ status_t status = sRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
sCurrentBuffer = mBuffer;
ASSERT_EQ(NO_ERROR, status);
ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
@@ -1414,20 +1415,52 @@
layers.push_back(&layer);
base::unique_fd fenceOne;
- sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(),
- &fenceOne);
+ sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
base::unique_fd fenceTwo;
- sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, std::move(fenceOne),
- &fenceTwo);
+ sRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
const int fd = fenceTwo.get();
if (fd >= 0) {
sync_wait(fd, -1);
}
-
// Only cleanup the first time.
- EXPECT_TRUE(sRE->cleanupPostRender());
- EXPECT_FALSE(sRE->cleanupPostRender());
+ EXPECT_TRUE(sRE->cleanupPostRender(
+ renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
+ EXPECT_FALSE(sRE->cleanupPostRender(
+ renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
+}
+
+TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(&layer);
+
+ base::unique_fd fence;
+ sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+
+ const int fd = fence.get();
+ if (fd >= 0) {
+ sync_wait(fd, -1);
+ }
+
+ uint64_t bufferId = layer.source.buffer.buffer->getId();
+ uint32_t texName = layer.source.buffer.textureName;
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(bufferId, sRE->getBufferIdForTextureNameForTesting(texName));
+
+ EXPECT_TRUE(sRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
+
+ // Now check that our view of memory is good.
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(std::nullopt, sRE->getBufferIdForTextureNameForTesting(bufferId));
+ EXPECT_TRUE(sRE->isTextureNameKnownForTesting(texName));
}
} // namespace android
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
new file mode 100644
index 0000000..97c7442
--- /dev/null
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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 <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include "../threaded/RenderEngineThreaded.h"
+
+namespace android {
+
+using testing::_;
+using testing::Eq;
+using testing::Mock;
+using testing::Return;
+
+struct RenderEngineThreadedTest : public ::testing::Test {
+ ~RenderEngineThreadedTest() {}
+
+ void SetUp() override {
+ mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
+ [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); });
+ }
+
+ std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+};
+
+TEST_F(RenderEngineThreadedTest, dump) {
+ std::string testString = "XYZ";
+ EXPECT_CALL(*mRenderEngine, dump(_));
+ mThreadedRE->dump(testString);
+}
+
+TEST_F(RenderEngineThreadedTest, primeCache) {
+ EXPECT_CALL(*mRenderEngine, primeCache());
+ mThreadedRE->primeCache();
+}
+
+TEST_F(RenderEngineThreadedTest, genTextures) {
+ uint32_t texName;
+ EXPECT_CALL(*mRenderEngine, genTextures(1, &texName));
+ mThreadedRE->genTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, deleteTextures) {
+ uint32_t texName;
+ EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName));
+ 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);
+}
+
+TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) {
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf));
+ mThreadedRE->cacheExternalTextureBuffer(buf);
+}
+
+TEST_F(RenderEngineThreadedTest, unbindExternalTextureBuffer) {
+ EXPECT_CALL(*mRenderEngine, unbindExternalTextureBuffer(0x0));
+ 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));
+ size_t result = mThreadedRE->getMaxTextureSize();
+ ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns0) {
+ size_t size = 0;
+ EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+ size_t result = mThreadedRE->getMaxTextureSize();
+ ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns20) {
+ size_t dims = 20;
+ EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+ size_t result = mThreadedRE->getMaxViewportDims();
+ ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) {
+ size_t dims = 0;
+ EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+ size_t result = mThreadedRE->getMaxViewportDims();
+ ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
+ status_t result = mThreadedRE->isProtected();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true));
+ size_t result = mThreadedRE->isProtected();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
+ status_t result = mThreadedRE->supportsProtectedContent();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true));
+ status_t result = mThreadedRE->supportsProtectedContent();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false));
+ status_t result = mThreadedRE->useProtectedContext(false);
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
+ status_t result = mThreadedRE->useProtectedContext(false);
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine,
+ cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+ .WillOnce(Return(false));
+ status_t result =
+ mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine,
+ cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+ .WillOnce(Return(true));
+ status_t result =
+ mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, drawLayers) {
+ renderengine::DisplaySettings settings;
+ std::vector<const renderengine::LayerSettings*> layers;
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ base::unique_fd bufferFence;
+ base::unique_fd drawFence;
+
+ EXPECT_CALL(*mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings&,
+ const std::vector<const renderengine::LayerSettings*>&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+ base::unique_fd*) -> status_t { return NO_ERROR; });
+
+ status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
+ std::move(bufferFence), &drawFence);
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
new file mode 100644
index 0000000..d4184fd
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -0,0 +1,403 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "RenderEngineThreaded.h"
+
+#include <sched.h>
+#include <chrono>
+#include <future>
+
+#include <android-base/stringprintf.h>
+#include <private/gui/SyncFeatures.h>
+#include <utils/Trace.h>
+
+#include "gl/GLESRenderEngine.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
+ return std::make_unique<RenderEngineThreaded>(std::move(factory));
+}
+
+RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) {
+ ATRACE_CALL();
+
+ std::lock_guard lockThread(mThreadMutex);
+ mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
+}
+
+RenderEngineThreaded::~RenderEngineThreaded() {
+ {
+ std::lock_guard lock(mThreadMutex);
+ mRunning = false;
+ mCondition.notify_one();
+ }
+
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
+void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
+ ALOGE("Couldn't set SCHED_FIFO");
+ }
+
+ mRenderEngine = factory();
+
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ pthread_setname_np(pthread_self(), mThreadName);
+
+ while (mRunning) {
+ if (!mFunctionCalls.empty()) {
+ auto task = mFunctionCalls.front();
+ mFunctionCalls.pop();
+ task(*mRenderEngine);
+ }
+ mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
+ return !mRunning || !mFunctionCalls.empty();
+ });
+ }
+}
+
+void RenderEngineThreaded::primeCache() const {
+ 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::primeCache");
+ instance.primeCache();
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::dump(std::string& result) {
+ std::promise<std::string> resultPromise;
+ std::future<std::string> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::dump");
+ std::string localResult = result;
+ instance.dump(localResult);
+ resultPromise.set_value(std::move(localResult));
+ });
+ }
+ mCondition.notify_one();
+ // Note: This is an rvalue.
+ result.assign(resultFuture.get());
+}
+
+bool RenderEngineThreaded::useNativeFenceSync() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
+ ATRACE_NAME("REThreaded::useNativeFenceSync");
+ bool returnValue = SyncFeatures::getInstance().useNativeFenceSync();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::useWaitSync() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
+ ATRACE_NAME("REThreaded::useWaitSync");
+ bool returnValue = SyncFeatures::getInstance().useWaitSync();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::genTextures");
+ instance.genTextures(count, names);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::deleteTextures");
+ instance.deleteTextures(count, names);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ 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();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &buffer](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cacheExternalTextureBuffer");
+ instance.cacheExternalTextureBuffer(buffer);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &bufferId](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::unbindExternalTextureBuffer");
+ instance.unbindExternalTextureBuffer(bufferId);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ 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();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getMaxTextureSize");
+ size_t size = instance.getMaxTextureSize();
+ resultPromise.set_value(size);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+size_t RenderEngineThreaded::getMaxViewportDims() const {
+ std::promise<size_t> resultPromise;
+ std::future<size_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getMaxViewportDims");
+ size_t size = instance.getMaxViewportDims();
+ resultPromise.set_value(size);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::isProtected() 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::isProtected");
+ bool returnValue = instance.isProtected();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::supportsProtectedContent() 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::supportsProtectedContent");
+ bool returnValue = instance.supportsProtectedContent();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::useProtectedContext");
+ bool returnValue = instance.useProtectedContext(useProtectedContext);
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ 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();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cleanupPostRender");
+ bool returnValue = instance.cleanupPostRender(mode);
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+status_t RenderEngineThreaded::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) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
+ &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::drawLayers");
+ status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
+ std::move(bufferFence), drawFence);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
new file mode 100644
index 0000000..86a49e9
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "renderengine/RenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::RenderEngine>()>;
+
+/**
+ * This class extends a basic RenderEngine class. It contains a thread. Each time a function of
+ * this class is called, we create a lambda function that is put on a queue. The main thread then
+ * executes the functions in order.
+ */
+class RenderEngineThreaded : public RenderEngine {
+public:
+ static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory);
+
+ RenderEngineThreaded(CreateInstanceFactory factory);
+ ~RenderEngineThreaded() override;
+ void primeCache() const override;
+
+ 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;
+
+ bool isProtected() const override;
+ bool supportsProtectedContent() const override;
+ bool useProtectedContext(bool useProtectedContext) override;
+ bool cleanupPostRender(CleanupMode mode) 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;
+
+protected:
+ Framebuffer* getFramebufferForDrawing() override;
+
+private:
+ void threadMain(CreateInstanceFactory factory);
+
+ /* ------------------------------------------------------------------------
+ * Threading
+ */
+ const char* const mThreadName = "RenderEngineThread";
+ // Protects the creation and destruction of mThread.
+ mutable std::mutex mThreadMutex;
+ std::thread mThread GUARDED_BY(mThreadMutex);
+ bool mRunning GUARDED_BY(mThreadMutex) = true;
+ mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls
+ GUARDED_BY(mThreadMutex);
+ mutable std::condition_variable mCondition;
+
+ /* ------------------------------------------------------------------------
+ * Render Engine
+ */
+ std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+};
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 8ed09f8..a6b0aaf 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -216,14 +216,25 @@
int32_t type;
Vector<float> floats;
Vector<int32_t> ints;
+ uint32_t count;
handle = data.readInt32();
type = data.readInt32();
- floats.resize(data.readUint32());
+
+ count = data.readUint32();
+ if (count > (data.dataAvail() / sizeof(float))) {
+ return BAD_VALUE;
+ }
+ floats.resize(count);
for (auto &i : floats) {
i = data.readFloat();
}
- ints.resize(data.readUint32());
+
+ count = data.readUint32();
+ if (count > (data.dataAvail() / sizeof(int32_t))) {
+ return BAD_VALUE;
+ }
+ ints.resize(count);
for (auto &i : ints) {
i = data.readInt32();
}
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 1ee8c71..2acc5bb 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -12,6 +12,74 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+cc_defaults {
+ name: "libui-defaults",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ cppflags: [
+ "-Wextra",
+ ],
+
+ sanitize: {
+ integer_overflow: true,
+ misc_undefined: ["bounds"],
+ },
+
+}
+
+cc_library_static {
+ name: "libui-types",
+ vendor_available: true,
+ host_supported: true,
+ target: {
+ windows: {
+ enabled: true,
+ }
+ },
+
+ defaults: [
+ "libui-defaults",
+ ],
+
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "apex_inherit",
+
+ shared_libs: [
+ "libbase",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libarect",
+ "libmath",
+ ],
+
+ srcs: [
+ "ColorSpace.cpp",
+ "Rect.cpp",
+ "Region.cpp",
+ "Transform.cpp",
+ ],
+
+ export_include_dirs: [
+ "include",
+ "include_private",
+ "include_types",
+ ],
+
+ export_static_lib_headers: [
+ "libarect",
+ "libmath",
+ ],
+
+}
+
cc_library_shared {
name: "libui",
vendor_available: true,
@@ -35,8 +103,9 @@
},
srcs: [
- "ColorSpace.cpp",
"DebugUtils.cpp",
+ "DeviceProductInfo.cpp",
+ "DisplayInfo.cpp",
"Fence.cpp",
"FenceTime.cpp",
"FrameStats.cpp",
@@ -50,10 +119,7 @@
"HdrCapabilities.cpp",
"PixelFormat.cpp",
"PublicFormat.cpp",
- "Rect.cpp",
- "Region.cpp",
"Size.cpp",
- "Transform.cpp",
"UiConfig.cpp",
],
@@ -65,8 +131,11 @@
"include_private",
],
- // Uncomment the following line to enable VALIDATE_REGIONS traces
- //defaults: ["libui-validate-regions-defaults"],
+ defaults: [
+ "libui-defaults",
+ // Uncomment the following line to enable VALIDATE_REGIONS traces
+ //defaults: ["libui-validate-regions-defaults"],
+ ],
shared_libs: [
"android.hardware.graphics.allocator@2.0",
@@ -100,6 +169,10 @@
"libmath",
],
+ whole_static_libs: [
+ "libui-types",
+ ],
+
// bufferhub is not used when building libgui for vendors
target: {
vendor: {
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index f394635..1f006ce 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -321,10 +321,6 @@
return std::string("Unknown RenderIntent");
}
-std::string to_string(const android::Rect& rect) {
- return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom);
-}
-
std::string toString(const android::DeviceProductInfo::ManufactureOrModelDate& date) {
using ModelYear = android::DeviceProductInfo::ModelYear;
using ManufactureYear = android::DeviceProductInfo::ManufactureYear;
diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp
new file mode 100644
index 0000000..4d6ce43
--- /dev/null
+++ b/libs/ui/DeviceProductInfo.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/DeviceProductInfo.h>
+
+#include <android-base/stringprintf.h>
+#include <ui/FlattenableHelpers.h>
+#include <utils/Log.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+using base::StringAppendF;
+
+size_t DeviceProductInfo::getFlattenedSize() const {
+ return FlattenableHelpers::getFlattenedSize(name) +
+ FlattenableHelpers::getFlattenedSize(manufacturerPnpId) +
+ FlattenableHelpers::getFlattenedSize(productId) +
+ FlattenableHelpers::getFlattenedSize(manufactureOrModelDate) +
+ FlattenableHelpers::getFlattenedSize(relativeAddress);
+}
+
+status_t DeviceProductInfo::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, name));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufacturerPnpId));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, productId));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufactureOrModelDate));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, relativeAddress));
+ return OK;
+}
+
+status_t DeviceProductInfo::unflatten(void const* buffer, size_t size) {
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &name));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufacturerPnpId));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &productId));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufactureOrModelDate));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &relativeAddress));
+ return OK;
+}
+
+void DeviceProductInfo::dump(std::string& result) const {
+ StringAppendF(&result, "{name=%s, ", name.c_str());
+ StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data());
+ StringAppendF(&result, "productId=%s, ", productId.c_str());
+
+ if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "modelYear=%u, ", model->year);
+ } else if (const auto* manufactureWeekAndYear =
+ std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week);
+ StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year);
+ } else if (const auto* manufactureYear =
+ std::get_if<ManufactureYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year);
+ } else {
+ ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+ }
+
+ result.append("relativeAddress=[");
+ for (size_t i = 0; i < relativeAddress.size(); i++) {
+ if (i != 0) {
+ result.append(", ");
+ }
+ StringAppendF(&result, "%u", relativeAddress[i]);
+ }
+ result.append("]}");
+}
+
+} // namespace android
diff --git a/libs/ui/DisplayInfo.cpp b/libs/ui/DisplayInfo.cpp
new file mode 100644
index 0000000..73a78af
--- /dev/null
+++ b/libs/ui/DisplayInfo.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/DisplayInfo.h>
+
+#include <cstdint>
+
+#include <ui/FlattenableHelpers.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+size_t DisplayInfo::getFlattenedSize() const {
+ return FlattenableHelpers::getFlattenedSize(connectionType) +
+ FlattenableHelpers::getFlattenedSize(density) +
+ FlattenableHelpers::getFlattenedSize(secure) +
+ FlattenableHelpers::getFlattenedSize(deviceProductInfo);
+}
+
+status_t DisplayInfo::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, connectionType));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo));
+ return OK;
+}
+
+status_t DisplayInfo::unflatten(void const* buffer, size_t size) {
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &connectionType));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo));
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index 13fed3a..a8d6285 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/stringprintf.h>
#include <system/graphics.h>
#include <ui/Rect.h>
@@ -149,4 +150,13 @@
return result;
}
+std::string to_string(const android::Rect& rect) {
+ return android::base::StringPrintf("Rect(%d, %d, %d, %d)", rect.left, rect.top, rect.right,
+ rect.bottom);
+}
+
+void PrintTo(const Rect& rect, ::std::ostream* os) {
+ *os << to_string(rect);
+}
+
}; // namespace android
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 06b6bfe..6b1bb40 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -55,7 +55,6 @@
mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] &&
mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] &&
mMatrix[2][2] == other.mMatrix[2][2];
- ;
}
Transform Transform::operator * (const Transform& rhs) const
@@ -87,6 +86,19 @@
return r;
}
+Transform Transform::operator * (float value) const {
+ Transform r(*this);
+ const mat33& M(mMatrix);
+ mat33& R(r.mMatrix);
+ for (size_t i = 0; i < 3; i++) {
+ for (size_t j = 0; j < 2; j++) {
+ R[i][j] = M[i][j] * value;
+ }
+ }
+ r.type();
+ return r;
+}
+
Transform& Transform::operator=(const Transform& other) {
mMatrix = other.mMatrix;
mType = other.mType;
@@ -105,14 +117,30 @@
return mMatrix[2][1];
}
-float Transform::sx() const {
+float Transform::dsdx() const {
return mMatrix[0][0];
}
-float Transform::sy() const {
+float Transform::dtdx() const {
+ return mMatrix[1][0];
+}
+
+float Transform::dtdy() const {
+ return mMatrix[0][1];
+}
+
+float Transform::dsdy() const {
return mMatrix[1][1];
}
+float Transform::getScaleX() const {
+ return sqrt(dsdx() * dsdx()) + (dtdx() * dtdx());
+}
+
+float Transform::getScaleY() const {
+ return sqrt((dtdy() * dtdy()) + (dsdy() * dsdy()));
+}
+
void Transform::reset() {
mType = IDENTITY;
for(size_t i = 0; i < 3; i++) {
@@ -187,6 +215,15 @@
return NO_ERROR;
}
+void Transform::set(const std::array<float, 9>& matrix) {
+ mat33& M(mMatrix);
+ M[0][0] = matrix[0]; M[1][0] = matrix[1]; M[2][0] = matrix[2];
+ M[0][1] = matrix[3]; M[1][1] = matrix[4]; M[2][1] = matrix[5];
+ M[0][2] = matrix[6]; M[1][2] = matrix[7]; M[2][2] = matrix[8];
+ mType = UNKNOWN_TYPE;
+ type();
+}
+
vec2 Transform::transform(const vec2& v) const {
vec2 r;
const mat33& M(mMatrix);
@@ -204,9 +241,8 @@
return r;
}
-vec2 Transform::transform(int x, int y) const
-{
- return transform(vec2(x,y));
+vec2 Transform::transform(float x, float y) const {
+ return transform(vec2(x, y));
}
Rect Transform::makeBounds(int w, int h) const
@@ -421,7 +457,43 @@
return m;
}
-void Transform::dump(std::string& out, const char* name) const {
+static std::string rotationToString(const uint32_t rotationFlags) {
+ switch (rotationFlags) {
+ case Transform::ROT_0:
+ return "ROT_0";
+ case Transform::FLIP_H:
+ return "FLIP_H";
+ case Transform::FLIP_V:
+ return "FLIP_V";
+ case Transform::ROT_90:
+ return "ROT_90";
+ case Transform::ROT_180:
+ return "ROT_180";
+ case Transform::ROT_270:
+ return "ROT_270";
+ case Transform::ROT_INVALID:
+ default:
+ return "ROT_INVALID";
+ }
+}
+
+static std::string transformToString(const uint32_t transform) {
+ if (transform == Transform::IDENTITY) {
+ return "IDENTITY";
+ }
+
+ if (transform == Transform::UNKNOWN) {
+ return "UNKNOWN";
+ }
+
+ std::string out;
+ if (transform & Transform::SCALE) out.append("SCALE ");
+ if (transform & Transform::ROTATE) out.append("ROTATE ");
+ if (transform & Transform::TRANSLATE) out.append("TRANSLATE");
+ return out;
+}
+
+void Transform::dump(std::string& out, const char* name, const char* prefix) const {
using android::base::StringAppendF;
type(); // Ensure the information in mType is up to date
@@ -429,38 +501,29 @@
const uint32_t type = mType;
const uint32_t orient = type >> 8;
- StringAppendF(&out, "%s 0x%08x (", name, orient);
+ out += prefix;
+ out += name;
+ out += " ";
if (orient & ROT_INVALID) {
- out.append("ROT_INVALID ");
- } else {
- if (orient & ROT_90) {
- out.append("ROT_90 ");
- } else {
- out.append("ROT_0 ");
- }
- if (orient & FLIP_V) out.append("FLIP_V ");
- if (orient & FLIP_H) out.append("FLIP_H ");
+ StringAppendF(&out, "0x%08x ", orient);
}
+ out += "(" + rotationToString(orient) + ") ";
- StringAppendF(&out, ") 0x%02x (", type);
-
- if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY ");
- if (type & SCALE) out.append("SCALE ");
- if (type & ROTATE) out.append("ROTATE ");
- if (type & TRANSLATE) out.append("TRANSLATE ");
-
- out.append(")\n");
+ if (type & UNKNOWN) {
+ StringAppendF(&out, "0x%02x ", type);
+ }
+ out += "(" + transformToString(type) + ")\n";
for (size_t i = 0; i < 3; i++) {
- StringAppendF(&out, " %.4f %.4f %.4f\n", static_cast<double>(mMatrix[0][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]));
}
}
-void Transform::dump(const char* name) const {
+void Transform::dump(const char* name, const char* prefix) const {
std::string out;
- dump(out, name);
+ dump(out, name, prefix);
ALOGD("%s", out.c_str());
}
diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h
index 4685575..18cd487 100644
--- a/libs/ui/include/ui/DebugUtils.h
+++ b/libs/ui/include/ui/DebugUtils.h
@@ -34,5 +34,4 @@
std::string decodeColorTransform(android_color_transform colorTransform);
std::string decodePixelFormat(android::PixelFormat format);
std::string decodeRenderIntent(android::ui::RenderIntent renderIntent);
-std::string to_string(const android::Rect& rect);
std::string toString(const android::DeviceProductInfo&);
diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
index af00342..807a5d9 100644
--- a/libs/ui/include/ui/DeviceProductInfo.h
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -19,7 +19,12 @@
#include <array>
#include <cstdint>
#include <optional>
+#include <string>
+#include <type_traits>
#include <variant>
+#include <vector>
+
+#include <utils/Flattenable.h>
namespace android {
@@ -29,13 +34,7 @@
// Product-specific information about the display or the directly connected device on the
// display chain. For example, if the display is transitively connected, this field may contain
// product information about the intermediate device.
-struct DeviceProductInfo {
- static constexpr size_t TEXT_BUFFER_SIZE = 20;
- static constexpr size_t RELATIVE_ADDRESS_SIZE = 4;
-
- using RelativeAddress = std::array<uint8_t, RELATIVE_ADDRESS_SIZE>;
- static constexpr RelativeAddress NO_RELATIVE_ADDRESS = {0xff, 0xff, 0xff, 0xff};
-
+struct DeviceProductInfo : LightFlattenable<DeviceProductInfo> {
struct ModelYear {
uint32_t year;
};
@@ -48,21 +47,29 @@
};
// Display name.
- std::array<char, TEXT_BUFFER_SIZE> name;
+ std::string name;
// Manufacturer Plug and Play ID.
PnpId manufacturerPnpId;
// Manufacturer product ID.
- std::array<char, TEXT_BUFFER_SIZE> productId;
+ std::string productId;
using ManufactureOrModelDate = std::variant<ModelYear, ManufactureYear, ManufactureWeekAndYear>;
+ static_assert(std::is_trivially_copyable_v<ManufactureOrModelDate>);
ManufactureOrModelDate manufactureOrModelDate;
- // Relative address in the display network. Unavailable address is indicated
- // by all elements equal to 255.
+ // Relative address in the display network. Empty vector indicates that the
+ // address is unavailable.
// For example, for HDMI connected device this will be the physical address.
- RelativeAddress relativeAddress;
+ std::vector<uint8_t> relativeAddress;
+
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ void dump(std::string& result) const;
};
} // namespace android
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
new file mode 100644
index 0000000..9eb5483
--- /dev/null
+++ b/libs/ui/include/ui/DisplayId.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <functional>
+#include <string>
+
+namespace android {
+
+// ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t.
+struct DisplayId {
+ // TODO(b/162612135) Remove default constructor
+ DisplayId() = default;
+ constexpr DisplayId(const DisplayId&) = default;
+ DisplayId& operator=(const DisplayId&) = default;
+
+ uint64_t value;
+
+protected:
+ explicit constexpr DisplayId(uint64_t id) : value(id) {}
+};
+
+static_assert(sizeof(DisplayId) == sizeof(uint64_t));
+
+inline bool operator==(DisplayId lhs, DisplayId rhs) {
+ return lhs.value == rhs.value;
+}
+
+inline bool operator!=(DisplayId lhs, DisplayId rhs) {
+ return !(lhs == rhs);
+}
+
+inline std::string to_string(DisplayId displayId) {
+ return std::to_string(displayId.value);
+}
+
+// DisplayId of a physical display, such as the internal display or externally connected display.
+struct PhysicalDisplayId : DisplayId {
+ // Flag indicating that the ID is stable across reboots.
+ static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
+
+ // Returns a stable ID based on EDID information.
+ static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId,
+ uint32_t modelHash) {
+ return PhysicalDisplayId(FLAG_STABLE, port, manufacturerId, modelHash);
+ }
+
+ // Returns an unstable ID. If EDID is available using "fromEdid" is preferred.
+ static constexpr PhysicalDisplayId fromPort(uint8_t port) {
+ constexpr uint16_t kManufacturerId = 0;
+ constexpr uint32_t kModelHash = 0;
+ return PhysicalDisplayId(0, port, kManufacturerId, kModelHash);
+ }
+
+ // TODO(b/162612135) Remove default constructor
+ PhysicalDisplayId() = default;
+ explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
+ explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other.value) {}
+
+ constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
+
+ constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
+
+private:
+ constexpr PhysicalDisplayId(uint64_t flags, uint8_t port, uint16_t manufacturerId,
+ uint32_t modelHash)
+ : DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) |
+ (static_cast<uint64_t>(modelHash) << 8) | port) {}
+};
+
+static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
+
+} // namespace android
+
+namespace std {
+
+template <>
+struct hash<android::DisplayId> {
+ size_t operator()(android::DisplayId displayId) const {
+ return hash<uint64_t>()(displayId.value);
+ }
+};
+
+template <>
+struct hash<android::PhysicalDisplayId> : hash<android::DisplayId> {};
+
+} // namespace std
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 897060c..03e0a38 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -20,19 +20,23 @@
#include <type_traits>
#include <ui/DeviceProductInfo.h>
+#include <utils/Flattenable.h>
namespace android {
enum class DisplayConnectionType { Internal, External };
// Immutable information about physical display.
-struct DisplayInfo {
+struct DisplayInfo : LightFlattenable<DisplayInfo> {
DisplayConnectionType connectionType = DisplayConnectionType::Internal;
float density = 0.f;
bool secure = false;
std::optional<DeviceProductInfo> deviceProductInfo;
-};
-static_assert(std::is_trivially_copyable_v<DisplayInfo>);
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+};
} // namespace android
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
index 64efc84..70a0d50 100644
--- a/libs/ui/include/ui/DisplayState.h
+++ b/libs/ui/include/ui/DisplayState.h
@@ -32,7 +32,7 @@
struct DisplayState {
LayerStack layerStack = NO_LAYER_STACK;
Rotation orientation = ROTATION_0;
- Size viewport;
+ Size layerStackSpaceRect;
};
static_assert(std::is_trivially_copyable_v<DisplayState>);
diff --git a/libs/ui/include/ui/PhysicalDisplayId.h b/libs/ui/include/ui/PhysicalDisplayId.h
deleted file mode 100644
index 1a345ac..0000000
--- a/libs/ui/include/ui/PhysicalDisplayId.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cinttypes>
-#include <cstdint>
-
-#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
-
-namespace android {
-
-using PhysicalDisplayId = uint64_t;
-
-constexpr uint8_t getPhysicalDisplayPort(PhysicalDisplayId displayId) {
- return static_cast<uint8_t>(displayId);
-}
-
-} // namespace android
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 2f2229e..6670dc0 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -19,10 +19,10 @@
#include <ostream>
+#include <log/log.h>
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/TypeHelpers.h>
-#include <log/log.h>
#include <ui/FloatRect.h>
#include <ui/Point.h>
@@ -216,11 +216,10 @@
}
};
+std::string to_string(const android::Rect& rect);
+
// Defining PrintTo helps with Google Tests.
-static inline void PrintTo(const Rect& rect, ::std::ostream* os) {
- *os << "Rect(" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom
- << ")";
-}
+void PrintTo(const Rect& rect, ::std::ostream* os);
ANDROID_BASIC_TYPES_TRAITS(Rect)
diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h
index 89008f6..83d431d 100644
--- a/libs/ui/include/ui/Rotation.h
+++ b/libs/ui/include/ui/Rotation.h
@@ -41,6 +41,15 @@
return toRotation((toRotationInt(lhs) + toRotationInt(rhs)) % N);
}
+constexpr Rotation operator-(Rotation lhs, Rotation rhs) {
+ constexpr auto N = toRotationInt(ROTATION_270) + 1;
+ return toRotation((N + toRotationInt(lhs) - toRotationInt(rhs)) % N);
+}
+
+constexpr Rotation operator-(Rotation rotation) {
+ return ROTATION_0 - rotation;
+}
+
constexpr const char* toCString(Rotation rotation) {
switch (rotation) {
case ROTATION_0:
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index c6bb598..4c463bf 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -18,10 +18,10 @@
#include <stdint.h>
#include <sys/types.h>
+#include <array>
#include <ostream>
#include <string>
-#include <hardware/hardware.h>
#include <math/mat4.h>
#include <math/vec2.h>
#include <math/vec3.h>
@@ -44,9 +44,9 @@
enum RotationFlags : uint32_t {
ROT_0 = 0,
- FLIP_H = HAL_TRANSFORM_FLIP_H,
- FLIP_V = HAL_TRANSFORM_FLIP_V,
- ROT_90 = HAL_TRANSFORM_ROT_90,
+ FLIP_H = 1, // HAL_TRANSFORM_FLIP_H
+ FLIP_V = 2, // HAL_TRANSFORM_FLIP_V
+ ROT_90 = 4, // HAL_TRANSFORM_ROT_90
ROT_180 = FLIP_H | FLIP_V,
ROT_270 = ROT_180 | ROT_90,
ROT_INVALID = 0x80
@@ -69,24 +69,31 @@
const vec3& operator [] (size_t i) const; // returns column i
float tx() const;
float ty() const;
- float sx() const;
- float sy() const;
+ float dsdx() const;
+ float dtdx() const;
+ float dtdy() const;
+ float dsdy() const;
+
+ float getScaleX() const;
+ float getScaleY() const;
// modify the transform
void reset();
void set(float tx, float ty);
void set(float a, float b, float c, float d);
status_t set(uint32_t flags, float w, float h);
+ void set(const std::array<float, 9>& matrix);
// transform data
Rect makeBounds(int w, int h) const;
- vec2 transform(int x, int y) const;
+ vec2 transform(float x, float y) const;
Region transform(const Region& reg) const;
Rect transform(const Rect& bounds,
bool roundOutwards = false) const;
FloatRect transform(const FloatRect& bounds) const;
Transform& operator = (const Transform& other);
Transform operator * (const Transform& rhs) const;
+ Transform operator * (float value) const;
// assumes the last row is < 0 , 0 , 1 >
vec2 transform(const vec2& v) const;
vec3 transform(const vec3& v) const;
@@ -97,8 +104,8 @@
Transform inverse() const;
// for debugging
- void dump(std::string& result, const char* name) const;
- void dump(const char* name) const;
+ void dump(std::string& result, const char* name, const char* prefix = "") const;
+ void dump(const char* name, const char* prefix = "") const;
static RotationFlags toRotationFlags(Rotation);
diff --git a/libs/ui/include_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h
new file mode 100644
index 0000000..8e316d8
--- /dev/null
+++ b/libs/ui/include_private/ui/FlattenableHelpers.h
@@ -0,0 +1,160 @@
+/*
+ * 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 <numeric>
+#include <optional>
+#include <type_traits>
+#include <vector>
+
+#include <utils/Flattenable.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+struct FlattenableHelpers {
+ // Helpers for reading and writing POD structures
+ template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+ static constexpr size_t getFlattenedSize(const T&) {
+ return sizeof(T);
+ }
+
+ template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+ static status_t flatten(void** buffer, size_t* size, const T& value) {
+ if (*size < sizeof(T)) return NO_MEMORY;
+ FlattenableUtils::write(*buffer, *size, value);
+ return OK;
+ }
+
+ template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+ static status_t unflatten(const void** buffer, size_t* size, T* value) {
+ if (*size < sizeof(T)) return NO_MEMORY;
+ FlattenableUtils::read(*buffer, *size, *value);
+ return OK;
+ }
+
+ // Helpers for reading and writing std::string
+ static size_t getFlattenedSize(const std::string& str) {
+ return sizeof(uint64_t) + str.length();
+ }
+
+ static status_t flatten(void** buffer, size_t* size, const std::string& str) {
+ if (*size < getFlattenedSize(str)) return NO_MEMORY;
+ flatten(buffer, size, (uint64_t)str.length());
+ memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length());
+ FlattenableUtils::advance(*buffer, *size, str.length());
+ return OK;
+ }
+
+ static status_t unflatten(const void** buffer, size_t* size, std::string* str) {
+ uint64_t length;
+ RETURN_IF_ERROR(unflatten(buffer, size, &length));
+ if (*size < length) return NO_MEMORY;
+ str->assign(reinterpret_cast<const char*>(*buffer), length);
+ FlattenableUtils::advance(*buffer, *size, length);
+ return OK;
+ }
+
+ // Helpers for reading and writing LightFlattenable
+ template <class T>
+ static size_t getFlattenedSize(const LightFlattenable<T>& value) {
+ return value.getFlattenedSize();
+ }
+
+ template <class T>
+ static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) {
+ RETURN_IF_ERROR(value.flatten(*buffer, *size));
+ FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize());
+ return OK;
+ }
+
+ template <class T>
+ static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) {
+ RETURN_IF_ERROR(value->unflatten(*buffer, *size));
+ FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize());
+ return OK;
+ }
+
+ // Helpers for reading and writing std::optional
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static size_t getFlattenedSize(const std::optional<T>& value) {
+ return sizeof(bool) + (value ? getFlattenedSize(*value) : 0);
+ }
+
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) {
+ if (value) {
+ RETURN_IF_ERROR(flatten(buffer, size, true));
+ RETURN_IF_ERROR(flatten(buffer, size, *value));
+ } else {
+ RETURN_IF_ERROR(flatten(buffer, size, false));
+ }
+ return OK;
+ }
+
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) {
+ bool isPresent;
+ RETURN_IF_ERROR(unflatten(buffer, size, &isPresent));
+ if (isPresent) {
+ *value = T();
+ RETURN_IF_ERROR(unflatten(buffer, size, &(**value)));
+ } else {
+ value->reset();
+ }
+ return OK;
+ }
+
+ // Helpers for reading and writing std::vector
+ template <class T>
+ static size_t getFlattenedSize(const std::vector<T>& value) {
+ return std::accumulate(value.begin(), value.end(), sizeof(uint64_t),
+ [](size_t sum, const T& element) {
+ return sum + getFlattenedSize(element);
+ });
+ }
+
+ template <class T>
+ static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) {
+ RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size()));
+ for (const auto& element : value) {
+ RETURN_IF_ERROR(flatten(buffer, size, element));
+ }
+ return OK;
+ }
+
+ template <class T>
+ static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) {
+ uint64_t numElements;
+ RETURN_IF_ERROR(unflatten(buffer, size, &numElements));
+ // We don't need an extra size check since each iteration of the loop does that
+ std::vector<T> elements;
+ for (size_t i = 0; i < numElements; i++) {
+ T element;
+ RETURN_IF_ERROR(unflatten(buffer, size, &element));
+ elements.push_back(element);
+ }
+ *value = std::move(elements);
+ return OK;
+ }
+};
+
+} // namespace android
+
+#undef RETURN_IF_ERROR
\ No newline at end of file
diff --git a/libs/ui/include/ui/ColorSpace.h b/libs/ui/include_types/ui/ColorSpace.h
similarity index 100%
rename from libs/ui/include/ui/ColorSpace.h
rename to libs/ui/include_types/ui/ColorSpace.h
diff --git a/libs/ui/include_vndk/ui/ColorSpace.h b/libs/ui/include_vndk/ui/ColorSpace.h
index ddf70d5..7d2a6d3 120000
--- a/libs/ui/include_vndk/ui/ColorSpace.h
+++ b/libs/ui/include_vndk/ui/ColorSpace.h
@@ -1 +1 @@
-../../include/ui/ColorSpace.h
\ No newline at end of file
+../../include_types/ui/ColorSpace.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayId.h b/libs/ui/include_vndk/ui/DisplayId.h
new file mode 120000
index 0000000..73c9fe8
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayId.h
@@ -0,0 +1 @@
+../../include/ui/DisplayId.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/PhysicalDisplayId.h b/libs/ui/include_vndk/ui/PhysicalDisplayId.h
deleted file mode 120000
index 6e3fb1e..0000000
--- a/libs/ui/include_vndk/ui/PhysicalDisplayId.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/PhysicalDisplayId.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index b53342c..28ef77a 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,13 @@
}
cc_test {
+ name: "FlattenableHelpers_test",
+ shared_libs: ["libui"],
+ srcs: ["FlattenableHelpers_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "GraphicBufferAllocator_test",
header_libs: [
"libnativewindow_headers",
diff --git a/libs/ui/tests/FlattenableHelpers_test.cpp b/libs/ui/tests/FlattenableHelpers_test.cpp
new file mode 100644
index 0000000..db32bc7
--- /dev/null
+++ b/libs/ui/tests/FlattenableHelpers_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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_TAG "FlattenableHelpersTest"
+
+#include <ui/FlattenableHelpers.h>
+
+#include <gtest/gtest.h>
+#include <utils/Flattenable.h>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android {
+
+namespace {
+
+struct TestLightFlattenable : LightFlattenable<TestLightFlattenable> {
+ std::unique_ptr<int32_t> ptr;
+
+ bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const { return sizeof(int32_t); }
+
+ status_t flatten(void* buffer, size_t size) const {
+ FlattenableUtils::write(buffer, size, *ptr);
+ return OK;
+ }
+
+ status_t unflatten(void const* buffer, size_t size) {
+ int value;
+ FlattenableUtils::read(buffer, size, value);
+ ptr = std::make_unique<int32_t>(value);
+ return OK;
+ }
+};
+
+class FlattenableHelpersTest : public testing::Test {
+public:
+ template <class T>
+ void testWriteThenRead(const T& value, size_t bufferSize) {
+ std::vector<int8_t> buffer(bufferSize);
+ auto rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+
+ auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size = buffer.size();
+ T valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ EXPECT_EQ(value, valueRead);
+ }
+
+ template <class T>
+ void testTriviallyCopyable(const T& value) {
+ testWriteThenRead(value, sizeof(T));
+ }
+
+ template <class T>
+ void testWriteThenRead(const T& value) {
+ testWriteThenRead(value, FlattenableHelpers::getFlattenedSize(value));
+ }
+};
+
+TEST_F(FlattenableHelpersTest, TriviallyCopyable) {
+ testTriviallyCopyable(42);
+ testTriviallyCopyable(1LL << 63);
+ testTriviallyCopyable(false);
+ testTriviallyCopyable(true);
+ testTriviallyCopyable(std::optional<int>());
+ testTriviallyCopyable(std::optional<int>(4));
+}
+
+TEST_F(FlattenableHelpersTest, String) {
+ testWriteThenRead(std::string("Android"));
+ testWriteThenRead(std::string());
+}
+
+TEST_F(FlattenableHelpersTest, Vector) {
+ testWriteThenRead(std::vector<int>({1, 2, 3}));
+ testWriteThenRead(std::vector<int>());
+}
+
+TEST_F(FlattenableHelpersTest, OptionalOfLightFlattenable) {
+ std::vector<size_t> buffer;
+ constexpr int kInternalValue = 16;
+ {
+ std::optional<TestLightFlattenable> value =
+ TestLightFlattenable{.ptr = std::make_unique<int32_t>(kInternalValue)};
+ buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+ void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+ }
+
+ const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size_t size = buffer.size();
+ std::optional<TestLightFlattenable> valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ ASSERT_TRUE(valueRead.has_value());
+ EXPECT_EQ(kInternalValue, *valueRead->ptr);
+}
+
+TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) {
+ std::vector<size_t> buffer;
+ {
+ std::optional<TestLightFlattenable> value;
+ buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+ void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+ }
+
+ const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size_t size = buffer.size();
+ std::optional<TestLightFlattenable> valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ ASSERT_FALSE(valueRead.has_value());
+}
+
+} // namespace
+} // namespace android
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index e95a080..1681fe2 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -14,6 +14,8 @@
cc_library_shared {
name: "libvibrator",
+ vendor_available: true,
+ double_loadable: true,
shared_libs: [
"libbinder",
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
new file mode 100644
index 0000000..749c568
--- /dev/null
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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 <cstring>
+
+#include <math.h>
+
+#include <vibrator/ExternalVibrationUtils.h>
+
+namespace android::os {
+
+namespace {
+static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
+static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
+static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
+
+float getHapticScaleGamma(HapticScale scale) {
+ switch (scale) {
+ case HapticScale::VERY_LOW:
+ return 2.0f;
+ case HapticScale::LOW:
+ return 1.5f;
+ case HapticScale::HIGH:
+ return 0.5f;
+ case HapticScale::VERY_HIGH:
+ return 0.25f;
+ default:
+ return 1.0f;
+ }
+}
+
+float getHapticMaxAmplitudeRatio(HapticScale scale) {
+ switch (scale) {
+ case HapticScale::VERY_LOW:
+ return HAPTIC_SCALE_VERY_LOW_RATIO;
+ case HapticScale::LOW:
+ return HAPTIC_SCALE_LOW_RATIO;
+ case HapticScale::NONE:
+ case HapticScale::HIGH:
+ case HapticScale::VERY_HIGH:
+ return 1.0f;
+ default:
+ return 0.0f;
+ }
+}
+
+} // namespace
+
+bool isValidHapticScale(HapticScale scale) {
+ switch (scale) {
+ case HapticScale::MUTE:
+ case HapticScale::VERY_LOW:
+ case HapticScale::LOW:
+ case HapticScale::NONE:
+ case HapticScale::HIGH:
+ case HapticScale::VERY_HIGH:
+ return true;
+ }
+ return false;
+}
+
+void scaleHapticData(float* buffer, size_t length, HapticScale scale) {
+ if (!isValidHapticScale(scale) || scale == HapticScale::NONE) {
+ return;
+ }
+ if (scale == HapticScale::MUTE) {
+ memset(buffer, 0, length * sizeof(float));
+ return;
+ }
+ float gamma = getHapticScaleGamma(scale);
+ float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale);
+ for (size_t i = 0; i < length; i++) {
+ float sign = buffer[i] >= 0 ? 1.0 : -1.0;
+ buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
+ * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+ }
+}
+
+} // namespace android::os
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
new file mode 100644
index 0000000..20045d0
--- /dev/null
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -0,0 +1,39 @@
+/*
+ * 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_EXTERNAL_VIBRATION_UTILS_H
+#define ANDROID_EXTERNAL_VIBRATION_UTILS_H
+
+#include <android/os/IExternalVibratorService.h>
+
+namespace android::os {
+
+enum class HapticScale {
+ MUTE = IExternalVibratorService::SCALE_MUTE,
+ VERY_LOW = IExternalVibratorService::SCALE_VERY_LOW,
+ LOW = IExternalVibratorService::SCALE_LOW,
+ NONE = IExternalVibratorService::SCALE_NONE,
+ HIGH = IExternalVibratorService::SCALE_HIGH,
+ VERY_HIGH = IExternalVibratorService::SCALE_VERY_HIGH,
+};
+
+bool isValidHapticScale(HapticScale scale);
+
+void scaleHapticData(float* buffer, size_t length, HapticScale scale);
+
+} // namespace android::os
+
+#endif // ANDROID_EXTERNAL_VIBRATION_UTILS_H
diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
index 0556ea1..1f1bcb3 100644
--- a/opengl/include/EGL/eglext_angle.h
+++ b/opengl/include/EGL/eglext_angle.h
@@ -4,12 +4,12 @@
// found in the LICENSE file.
//
// eglext_angle.h: ANGLE modifications to the eglext.h header file.
-// Currently we don't include this file directly, we patch eglext.h
-// to include it implicitly so it is visible throughout our code.
#ifndef INCLUDE_EGL_EGLEXT_ANGLE_
#define INCLUDE_EGL_EGLEXT_ANGLE_
+#include <EGL/eglext.h>
+
// clang-format off
#ifndef EGL_ANGLE_robust_resource_initialization
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index e8d3684..3c76c62 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -102,11 +102,6 @@
"libbacktrace",
"libbase",
],
- target: {
- vendor: {
- exclude_shared_libs: ["libgraphicsenv"],
- },
- },
}
cc_library_static {
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index a27c09f..beca7f1 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -18,11 +18,11 @@
#include "BlobCache.h"
+#include <android-base/properties.h>
#include <errno.h>
#include <inttypes.h>
-
-#include <android-base/properties.h>
#include <log/log.h>
+
#include <chrono>
namespace android {
@@ -36,8 +36,8 @@
// BlobCache::Header::mDeviceVersion value
static const uint32_t blobCacheDeviceVersion = 1;
-BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
- mMaxTotalSize(maxTotalSize),
+BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize)
+ : mMaxTotalSize(maxTotalSize),
mMaxKeySize(maxKeySize),
mMaxValueSize(maxValueSize),
mTotalSize(0) {
@@ -52,21 +52,21 @@
ALOGV("initializing random seed using %lld", (unsigned long long)now);
}
-void BlobCache::set(const void* key, size_t keySize, const void* value,
- size_t valueSize) {
+void BlobCache::set(const void* key, size_t keySize, const void* value, size_t valueSize) {
if (mMaxKeySize < keySize) {
- ALOGV("set: not caching because the key is too large: %zu (limit: %zu)",
- keySize, mMaxKeySize);
+ ALOGV("set: not caching because the key is too large: %zu (limit: %zu)", keySize,
+ mMaxKeySize);
return;
}
if (mMaxValueSize < valueSize) {
- ALOGV("set: not caching because the value is too large: %zu (limit: %zu)",
- valueSize, mMaxValueSize);
+ ALOGV("set: not caching because the value is too large: %zu (limit: %zu)", valueSize,
+ mMaxValueSize);
return;
}
if (mMaxTotalSize < keySize + valueSize) {
ALOGV("set: not caching because the combined key/value size is too "
- "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize);
+ "large: %zu (limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
return;
}
if (keySize == 0) {
@@ -95,16 +95,16 @@
continue;
} else {
ALOGV("set: not caching new key/value pair because the "
- "total cache size limit would be exceeded: %zu "
- "(limit: %zu)",
- keySize + valueSize, mMaxTotalSize);
+ "total cache size limit would be exceeded: %zu "
+ "(limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
break;
}
}
mCacheEntries.insert(index, CacheEntry(keyBlob, valueBlob));
mTotalSize = newTotalSize;
- ALOGV("set: created new cache entry with %zu byte key and %zu byte value",
- keySize, valueSize);
+ ALOGV("set: created new cache entry with %zu byte key and %zu byte value", keySize,
+ valueSize);
} else {
// Update the existing cache entry.
std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true));
@@ -117,25 +117,25 @@
continue;
} else {
ALOGV("set: not caching new value because the total cache "
- "size limit would be exceeded: %zu (limit: %zu)",
- keySize + valueSize, mMaxTotalSize);
+ "size limit would be exceeded: %zu (limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
break;
}
}
index->setValue(valueBlob);
mTotalSize = newTotalSize;
ALOGV("set: updated existing cache entry with %zu byte key and %zu byte "
- "value", keySize, valueSize);
+ "value",
+ keySize, valueSize);
}
break;
}
}
-size_t BlobCache::get(const void* key, size_t keySize, void* value,
- size_t valueSize) {
+size_t BlobCache::get(const void* key, size_t keySize, void* value, size_t valueSize) {
if (mMaxKeySize < keySize) {
- ALOGV("get: not searching because the key is too large: %zu (limit %zu)",
- keySize, mMaxKeySize);
+ ALOGV("get: not searching because the key is too large: %zu (limit %zu)", keySize,
+ mMaxKeySize);
return 0;
}
std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false));
@@ -154,8 +154,8 @@
ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize);
memcpy(value, valueBlob->getData(), valueBlobSize);
} else {
- ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)",
- valueSize, valueBlobSize);
+ ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)", valueSize,
+ valueBlobSize);
}
return valueBlobSize;
}
@@ -167,7 +167,7 @@
size_t BlobCache::getFlattenedSize() const {
auto buildId = base::GetProperty("ro.build.id", "");
size_t size = align4(sizeof(Header) + buildId.size());
- for (const CacheEntry& e : mCacheEntries) {
+ for (const CacheEntry& e : mCacheEntries) {
std::shared_ptr<Blob> const& keyBlob = e.getKey();
std::shared_ptr<Blob> const& valueBlob = e.getValue();
size += align4(sizeof(EntryHeader) + keyBlob->getSize() + valueBlob->getSize());
@@ -193,7 +193,7 @@
// Write cache entries
uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
- for (const CacheEntry& e : mCacheEntries) {
+ for (const CacheEntry& e : mCacheEntries) {
std::shared_ptr<Blob> const& keyBlob = e.getKey();
std::shared_ptr<Blob> const& valueBlob = e.getValue();
size_t keySize = keyBlob->getSize();
@@ -259,8 +259,7 @@
return -EINVAL;
}
- const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
- &byteBuffer[byteOffset]);
+ const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(&byteBuffer[byteOffset]);
size_t keySize = eheader->mKeySize;
size_t valueSize = eheader->mValueSize;
size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
@@ -304,10 +303,8 @@
return mTotalSize > mMaxTotalSize / 2;
}
-BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) :
- mData(copyData ? malloc(size) : data),
- mSize(size),
- mOwnsData(copyData) {
+BlobCache::Blob::Blob(const void* data, size_t size, bool copyData)
+ : mData(copyData ? malloc(size) : data), mSize(size), mOwnsData(copyData) {
if (data != nullptr && copyData) {
memcpy(const_cast<void*>(mData), data, size);
}
@@ -335,19 +332,13 @@
return mSize;
}
-BlobCache::CacheEntry::CacheEntry() {
-}
+BlobCache::CacheEntry::CacheEntry() {}
-BlobCache::CacheEntry::CacheEntry(
- const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value):
- mKey(key),
- mValue(value) {
-}
+BlobCache::CacheEntry::CacheEntry(const std::shared_ptr<Blob>& key,
+ const std::shared_ptr<Blob>& value)
+ : mKey(key), mValue(value) {}
-BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
- mKey(ce.mKey),
- mValue(ce.mValue) {
-}
+BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce) : mKey(ce.mKey), mValue(ce.mValue) {}
bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
return *mKey < *rhs.mKey;
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index e5c5e5b..50b4e4c 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -54,8 +54,7 @@
// 0 < keySize
// value != NULL
// 0 < valueSize
- void set(const void* key, size_t keySize, const void* value,
- size_t valueSize);
+ void set(const void* key, size_t keySize, const void* value, size_t valueSize);
// get retrieves from the cache the binary value associated with a given
// binary key. If the key is present in the cache then the length of the
@@ -75,7 +74,6 @@
// 0 <= valueSize
size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
-
// getFlattenedSize returns the number of bytes needed to store the entire
// serialized cache.
size_t getFlattenedSize() const;
@@ -168,7 +166,6 @@
void setValue(const std::shared_ptr<Blob>& value);
private:
-
// mKey is the key that identifies the cache entry.
std::shared_ptr<Blob> mKey;
@@ -245,6 +242,6 @@
std::vector<CacheEntry> mCacheEntries;
};
-}
+} // namespace android
#endif // ANDROID_BLOB_CACHE_H
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
index cf67cf4..d31373b 100644
--- a/opengl/libs/EGL/BlobCache_test.cpp
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -14,25 +14,24 @@
** limitations under the License.
*/
+#include "BlobCache.h"
+
#include <fcntl.h>
+#include <gtest/gtest.h>
#include <stdio.h>
#include <memory>
-#include <gtest/gtest.h>
-
-#include "BlobCache.h"
-
namespace android {
-template<typename T> using sp = std::shared_ptr<T>;
+template <typename T>
+using sp = std::shared_ptr<T>;
class BlobCacheTest : public ::testing::Test {
protected:
-
enum {
OK = 0,
- BAD_VALUE = -EINVAL
+ BAD_VALUE = -EINVAL,
};
enum {
@@ -41,19 +40,15 @@
MAX_TOTAL_SIZE = 13,
};
- virtual void SetUp() {
- mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
- }
+ virtual void SetUp() { mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE)); }
- virtual void TearDown() {
- mBC.reset();
- }
+ virtual void TearDown() { mBC.reset(); }
std::unique_ptr<BlobCache> mBC;
};
TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
ASSERT_EQ('e', buf[0]);
@@ -63,7 +58,7 @@
}
TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
- unsigned char buf[2] = { 0xee, 0xee };
+ unsigned char buf[2] = {0xee, 0xee};
mBC->set("ab", 2, "cd", 2);
mBC->set("ef", 2, "gh", 2);
ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
@@ -75,9 +70,9 @@
}
TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
- unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[6] = {0xee, 0xee, 0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf + 1, 4));
ASSERT_EQ(0xee, buf[0]);
ASSERT_EQ('e', buf[1]);
ASSERT_EQ('f', buf[2]);
@@ -87,7 +82,7 @@
}
TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
- unsigned char buf[3] = { 0xee, 0xee, 0xee };
+ unsigned char buf[3] = {0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
ASSERT_EQ(0xee, buf[0]);
@@ -101,7 +96,7 @@
}
TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
mBC->set("abcd", 4, "ijkl", 4);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
@@ -112,9 +107,9 @@
}
TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
- unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[MAX_VALUE_SIZE + 1] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
ASSERT_EQ('e', buf[0]);
ASSERT_EQ('f', buf[1]);
@@ -123,13 +118,13 @@
}
TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
- char key[MAX_KEY_SIZE+1];
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
+ char key[MAX_KEY_SIZE + 1];
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
+ for (int i = 0; i < MAX_KEY_SIZE + 1; i++) {
key[i] = 'a';
}
- mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
- ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
+ mBC->set(key, MAX_KEY_SIZE + 1, "bbbb", 4);
+ ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE + 1, buf, 4));
ASSERT_EQ(0xee, buf[0]);
ASSERT_EQ(0xee, buf[1]);
ASSERT_EQ(0xee, buf[2]);
@@ -137,16 +132,16 @@
}
TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
- char buf[MAX_VALUE_SIZE+1];
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ char buf[MAX_VALUE_SIZE + 1];
+ for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
buf[i] = 'b';
}
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1);
+ for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
buf[i] = 0xee;
}
- ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE + 1));
+ for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
SCOPED_TRACE(i);
ASSERT_EQ(0xee, buf[i]);
}
@@ -174,7 +169,7 @@
TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
char key[MAX_KEY_SIZE];
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
for (int i = 0; i < MAX_KEY_SIZE; i++) {
key[i] = 'a';
}
@@ -195,8 +190,7 @@
for (int i = 0; i < MAX_VALUE_SIZE; i++) {
buf[i] = 0xee;
}
- ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
- MAX_VALUE_SIZE));
+ ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE));
for (int i = 0; i < MAX_VALUE_SIZE; i++) {
SCOPED_TRACE(i);
ASSERT_EQ('b', buf[i]);
@@ -223,7 +217,7 @@
}
TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
- unsigned char buf[1] = { 0xee };
+ unsigned char buf[1] = {0xee};
mBC->set("x", 1, "y", 1);
ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
ASSERT_EQ('y', buf[0]);
@@ -258,13 +252,13 @@
}
// Count the number of entries in the cache.
int numCached = 0;
- for (int i = 0; i < maxEntries+1; i++) {
+ for (int i = 0; i < maxEntries + 1; i++) {
uint8_t k = i;
if (mBC->get(&k, 1, nullptr, 0) == 1) {
numCached++;
}
}
- ASSERT_EQ(maxEntries/2 + 1, numCached);
+ ASSERT_EQ(maxEntries / 2 + 1, numCached);
}
class BlobCacheFlattenTest : public BlobCacheTest {
@@ -291,7 +285,7 @@
};
TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
roundTrip();
ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
@@ -359,7 +353,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
@@ -376,7 +370,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
@@ -395,7 +389,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
@@ -414,7 +408,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
diff --git a/opengl/libs/EGL/CallStack.h b/opengl/libs/EGL/CallStack.h
index 0e2a9b3..b7fdf97 100644
--- a/opengl/libs/EGL/CallStack.h
+++ b/opengl/libs/EGL/CallStack.h
@@ -16,8 +16,9 @@
#pragma once
-#include <log/log.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
#include <memory>
class CallStack {
@@ -30,9 +31,8 @@
if (backtrace->Unwind(2)) {
for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) {
__android_log_print(ANDROID_LOG_DEBUG, logtag, "%s",
- backtrace->FormatFrameData(i).c_str());
+ backtrace->FormatFrameData(i).c_str());
}
}
}
};
-
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index d66ef2b..1afc693 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,27 +17,23 @@
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <EGL/Loader.h>
-
-#include <string>
-
-#include <dirent.h>
-#include <dlfcn.h>
+#include "EGL/Loader.h"
#include <android-base/properties.h>
#include <android/dlext.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
#include <utils/Timers.h>
-
-#ifndef __ANDROID_VNDK__
-#include <graphicsenv/GraphicsEnv.h>
-#endif
#include <vndksupport/linker.h>
+#include <string>
+
+#include "EGL/eglext_angle.h"
#include "egl_platform_entries.h"
#include "egl_trace.h"
#include "egldefs.h"
-#include <EGL/eglext_angle.h>
namespace android {
@@ -159,13 +155,11 @@
return true;
}
-#ifndef __ANDROID_VNDK__
// Return true if updated driver namespace is set.
ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (ns) {
return true;
}
-#endif
return false;
}
@@ -276,7 +270,7 @@
// will set cnx->useAngle appropriately.
// Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native),
// not just loading ANGLE as option.
- init_angle_backend(hnd->dso[0], cnx);
+ init_angle_backend(hnd->dso[2], cnx);
}
LOG_ALWAYS_FATAL_IF(!hnd,
@@ -370,7 +364,7 @@
f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;
/*
- * GL_EXT_debug_label is special, we always report it as
+ * GL_EXT_debug_marker is special, we always report it as
* supported, it's handled by GLES_trace. If GLES_trace is not
* enabled, then these are no-ops.
*/
@@ -557,12 +551,8 @@
}
void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) {
- void* eglCreateDeviceANGLE = nullptr;
-
- ALOGV("dso: %p", dso);
- eglCreateDeviceANGLE = dlsym(dso, "eglCreateDeviceANGLE");
- ALOGV("eglCreateDeviceANGLE: %p", eglCreateDeviceANGLE);
- if (eglCreateDeviceANGLE) {
+ void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform");
+ if (pANGLEGetDisplayPlatform) {
ALOGV("ANGLE GLES library in use");
cnx->useAngle = true;
} else {
@@ -573,7 +563,7 @@
Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) {
ATRACE_CALL();
-#ifndef __ANDROID_VNDK__
+
android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (!ns) {
return nullptr;
@@ -603,9 +593,6 @@
hnd->set(dso, GLESv2);
}
return hnd;
-#else
- return nullptr;
-#endif
}
Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 7b2d7c9..81742ab 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -1,29 +1,26 @@
-/*
+/*
** Copyright 2009, The Android Open Source Project
**
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
**
- ** http://www.apache.org/licenses/LICENSE-2.0
+ ** http://www.apache.org/licenses/LICENSE-2.0
**
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT 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_EGL_LOADER_H
#define ANDROID_EGL_LOADER_H
+#include <EGL/egl.h>
#include <stdint.h>
-#include <EGL/egl.h>
-
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
struct egl_connection_t;
@@ -62,16 +59,12 @@
void initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask);
void init_angle_backend(void* dso, egl_connection_t* cnx);
- static __attribute__((noinline))
- void init_api(void* dso,
- char const * const * api,
- char const * const * ref_api,
- __eglMustCastToProperFunctionPointerType* curr,
- getProcAddressType getProcAddress);
+ static __attribute__((noinline)) void init_api(void* dso, const char* const* api,
+ const char* const* ref_api,
+ __eglMustCastToProperFunctionPointerType* curr,
+ getProcAddressType getProcAddress);
};
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif /* ANDROID_EGL_LOADER_H */
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 43f7a07..e5b9e14 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -14,45 +14,35 @@
** limitations under the License.
*/
+#include <EGL/egl.h>
+#include <android-base/properties.h>
+#include <log/log.h>
#include <stdlib.h>
-#include <EGL/egl.h>
-
-#include <android-base/properties.h>
-
-#include <log/log.h>
-
#include "../egl_impl.h"
-
-#include "egldefs.h"
-#include "egl_tls.h"
-#include "egl_display.h"
-#include "egl_object.h"
-#include "egl_layers.h"
#include "CallStack.h"
#include "Loader.h"
+#include "egl_display.h"
+#include "egl_layers.h"
+#include "egl_object.h"
+#include "egl_tls.h"
+#include "egldefs.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
egl_connection_t gEGLImpl;
gl_hooks_t gHooks[2];
gl_hooks_t gHooksNoContext;
pthread_key_t gGLWrapperKey = -1;
-// ----------------------------------------------------------------------------
-
-void setGLHooksThreadSpecific(gl_hooks_t const *value) {
+void setGLHooksThreadSpecific(gl_hooks_t const* value) {
setGlThreadSpecific(value);
}
-/*****************************************************************************/
-
static int gl_no_context() {
if (egl_tls_t::logNoContextCall()) {
- char const* const error = "call to OpenGL ES API with "
- "no current context (logged once per thread)";
+ const char* const error = "call to OpenGL ES API with "
+ "no current context (logged once per thread)";
if (LOG_NDEBUG) {
ALOGE(error);
} else {
@@ -65,10 +55,9 @@
return 0;
}
-static void early_egl_init(void)
-{
+static void early_egl_init(void) {
int numHooks = sizeof(gHooksNoContext) / sizeof(EGLFuncPointer);
- EGLFuncPointer *iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext);
+ EGLFuncPointer* iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext);
for (int hook = 0; hook < numHooks; ++hook) {
*(iter++) = reinterpret_cast<EGLFuncPointer>(gl_no_context);
}
@@ -76,75 +65,40 @@
setGLHooksThreadSpecific(&gHooksNoContext);
}
-static pthread_once_t once_control = PTHREAD_ONCE_INIT;
-static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
-
-// ----------------------------------------------------------------------------
-
-egl_display_ptr validate_display(EGLDisplay dpy) {
- egl_display_ptr dp = get_display(dpy);
- if (!dp)
- return setError(EGL_BAD_DISPLAY, egl_display_ptr(nullptr));
- if (!dp->isReady())
- return setError(EGL_NOT_INITIALIZED, egl_display_ptr(nullptr));
-
- return dp;
-}
-
-egl_display_ptr validate_display_connection(EGLDisplay dpy,
- egl_connection_t*& cnx) {
- cnx = nullptr;
- egl_display_ptr dp = validate_display(dpy);
- if (!dp)
- return dp;
- cnx = &gEGLImpl;
- if (cnx->dso == nullptr) {
- return setError(EGL_BAD_CONFIG, egl_display_ptr(nullptr));
- }
- return dp;
-}
-
-// ----------------------------------------------------------------------------
-
-const GLubyte * egl_get_string_for_current_context(GLenum name) {
+const GLubyte* egl_get_string_for_current_context(GLenum name) {
// NOTE: returning NULL here will fall-back to the default
// implementation.
EGLContext context = egl_tls_t::getContext();
- if (context == EGL_NO_CONTEXT)
- return nullptr;
+ if (context == EGL_NO_CONTEXT) return nullptr;
- egl_context_t const * const c = get_context(context);
+ const egl_context_t* const c = get_context(context);
if (c == nullptr) // this should never happen, by construction
return nullptr;
- if (name != GL_EXTENSIONS)
- return nullptr;
+ if (name != GL_EXTENSIONS) return nullptr;
- return (const GLubyte *)c->gl_extensions.c_str();
+ return (const GLubyte*)c->gl_extensions.c_str();
}
-const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index) {
+const GLubyte* egl_get_string_for_current_context(GLenum name, GLuint index) {
// NOTE: returning NULL here will fall-back to the default
// implementation.
EGLContext context = egl_tls_t::getContext();
- if (context == EGL_NO_CONTEXT)
- return nullptr;
+ if (context == EGL_NO_CONTEXT) return nullptr;
- egl_context_t const * const c = get_context(context);
+ const egl_context_t* const c = get_context(context);
if (c == nullptr) // this should never happen, by construction
return nullptr;
- if (name != GL_EXTENSIONS)
- return nullptr;
+ if (name != GL_EXTENSIONS) return nullptr;
// if index is out of bounds, assume it will be in the default
// implementation too, so we don't have to generate a GL error here
- if (index >= c->tokenized_gl_extensions.size())
- return nullptr;
+ if (index >= c->tokenized_gl_extensions.size()) return nullptr;
- return (const GLubyte *)c->tokenized_gl_extensions[index].c_str();
+ return (const GLubyte*)c->tokenized_gl_extensions[index].c_str();
}
GLint egl_get_num_extensions_for_current_context() {
@@ -152,10 +106,9 @@
// implementation.
EGLContext context = egl_tls_t::getContext();
- if (context == EGL_NO_CONTEXT)
- return -1;
+ if (context == EGL_NO_CONTEXT) return -1;
- egl_context_t const * const c = get_context(context);
+ const egl_context_t* const c = get_context(context);
if (c == nullptr) // this should never happen, by construction
return -1;
@@ -166,7 +119,8 @@
return &gEGLImpl;
}
-// ----------------------------------------------------------------------------
+static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
static EGLBoolean egl_init_drivers_locked() {
if (sEarlyInitState) {
@@ -194,7 +148,6 @@
return cnx->dso ? EGL_TRUE : EGL_FALSE;
}
-
// this mutex protects driver load logic as a critical section since it accesses to global variable
// like gEGLImpl
static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
@@ -228,13 +181,10 @@
}
}
-void gl_noop() {
-}
+void gl_noop() {}
-// ----------------------------------------------------------------------------
-
-void setGlThreadSpecific(gl_hooks_t const *value) {
- gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
+void setGlThreadSpecific(gl_hooks_t const* value) {
+ gl_hooks_t const* volatile* tls_hooks = get_tls_hooks();
tls_hooks[TLS_SLOT_OPENGL_API] = value;
}
@@ -270,8 +220,4 @@
#undef GL_ENTRY
#undef EGL_ENTRY
-
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
-
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index c51a129..502c14f 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -20,7 +20,6 @@
#include <EGL/eglext.h>
#include "../egl_impl.h"
-
#include "egl_layers.h"
#include "egl_platform_entries.h"
#include "egl_tls.h"
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index 97dc0f1..dc8e587 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -16,22 +16,26 @@
#if defined(__ANDROID__)
-#include "Loader.h"
#include "egl_angle_platform.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <EGL/Platform.h>
#pragma GCC diagnostic pop
-
#include <android/dlext.h>
#include <dlfcn.h>
#include <graphicsenv/GraphicsEnv.h>
-#include <time.h>
#include <log/log.h>
+#include <time.h>
+#include <vndksupport/linker.h>
+
+#include "Loader.h"
namespace angle {
+constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so";
+constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW;
+
static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr;
static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr;
@@ -101,11 +105,22 @@
bool initializeAnglePlatform(EGLDisplay dpy) {
// Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform
android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE,
- .library_namespace = ns,
- };
- void* so = android_dlopen_ext("libGLESv2_angle.so", RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+ void* so = nullptr;
+ if (ns) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = ns,
+ };
+ so = android_dlopen_ext(kAngleEs2Lib, kAngleDlFlags, &dlextinfo);
+ } else {
+ // If we are here, ANGLE is loaded as built-in gl driver in the sphal.
+ so = android_load_sphal_library(kAngleEs2Lib, kAngleDlFlags);
+ }
+ if (!so) {
+ ALOGE("%s failed to dlopen %s!", __FUNCTION__, kAngleEs2Lib);
+ return false;
+ }
+
angleGetDisplayPlatform =
reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform"));
@@ -114,14 +129,12 @@
return false;
}
- angleResetDisplayPlatform =
- reinterpret_cast<ResetDisplayPlatformFunc>(
- eglGetProcAddress("ANGLEResetDisplayPlatform"));
+ angleResetDisplayPlatform = reinterpret_cast<ResetDisplayPlatformFunc>(
+ eglGetProcAddress("ANGLEResetDisplayPlatform"));
PlatformMethods* platformMethods = nullptr;
- if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames,
- g_NumPlatformMethods, nullptr,
- &platformMethods))) {
+ if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr,
+ &platformMethods))) {
ALOGE("ANGLEGetDisplayPlatform call failed!");
return false;
}
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index bcf4961..efa67db 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -16,17 +16,14 @@
#include "egl_cache.h"
-#include "../egl_impl.h"
-
-#include "egl_display.h"
-
+#include <log/log.h>
#include <private/EGL/cache.h>
-
#include <unistd.h>
#include <thread>
-#include <log/log.h>
+#include "../egl_impl.h"
+#include "egl_display.h"
// Cache size limits.
static const size_t maxKeySize = 12 * 1024;
@@ -36,9 +33,7 @@
// The time in seconds to wait before saving newly inserted cache entries.
static const unsigned int deferredSaveDelay = 4;
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
#define BC_EXT_STR "EGL_ANDROID_blob_cache"
@@ -50,25 +45,22 @@
//
// Callback functions passed to EGL.
//
-static void setBlob(const void* key, EGLsizeiANDROID keySize,
- const void* value, EGLsizeiANDROID valueSize) {
+static void setBlob(const void* key, EGLsizeiANDROID keySize, const void* value,
+ EGLsizeiANDROID valueSize) {
egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
}
-static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
- void* value, EGLsizeiANDROID valueSize) {
+static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize, void* value,
+ EGLsizeiANDROID valueSize) {
return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
}
//
// egl_cache_t definition
//
-egl_cache_t::egl_cache_t() :
- mInitialized(false) {
-}
+egl_cache_t::egl_cache_t() : mInitialized(false) {}
-egl_cache_t::~egl_cache_t() {
-}
+egl_cache_t::~egl_cache_t() {}
egl_cache_t egl_cache_t::sCache;
@@ -76,7 +68,7 @@
return &sCache;
}
-void egl_cache_t::initialize(egl_display_t *display) {
+void egl_cache_t::initialize(egl_display_t* display) {
std::lock_guard<std::mutex> lock(mMutex);
egl_connection_t* const cnx = &gEGLImpl;
@@ -85,28 +77,26 @@
size_t bcExtLen = strlen(BC_EXT_STR);
size_t extsLen = strlen(exts);
bool equal = !strcmp(BC_EXT_STR, exts);
- bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
- bool atEnd = (bcExtLen+1) < extsLen &&
- !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
+ bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen + 1);
+ bool atEnd = (bcExtLen + 1) < extsLen &&
+ !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen + 1));
bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr;
if (equal || atStart || atEnd || inMiddle) {
PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
- eglSetBlobCacheFuncsANDROID =
- reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
- cnx->egl.eglGetProcAddress(
- "eglSetBlobCacheFuncsANDROID"));
+ eglSetBlobCacheFuncsANDROID = reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
+ cnx->egl.eglGetProcAddress("eglSetBlobCacheFuncsANDROID"));
if (eglSetBlobCacheFuncsANDROID == nullptr) {
ALOGE("EGL_ANDROID_blob_cache advertised, "
- "but unable to get eglSetBlobCacheFuncsANDROID");
+ "but unable to get eglSetBlobCacheFuncsANDROID");
return;
}
- eglSetBlobCacheFuncsANDROID(display->disp.dpy,
- android::setBlob, android::getBlob);
+ eglSetBlobCacheFuncsANDROID(display->disp.dpy, android::setBlob, android::getBlob);
EGLint err = cnx->egl.eglGetError();
if (err != EGL_SUCCESS) {
ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
- "%#x", err);
+ "%#x",
+ err);
}
}
}
@@ -122,8 +112,8 @@
mBlobCache = nullptr;
}
-void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
- const void* value, EGLsizeiANDROID valueSize) {
+void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, const void* value,
+ EGLsizeiANDROID valueSize) {
std::lock_guard<std::mutex> lock(mMutex);
if (keySize < 0 || valueSize < 0) {
@@ -150,8 +140,8 @@
}
}
-EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
- void* value, EGLsizeiANDROID valueSize) {
+EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, void* value,
+ EGLsizeiANDROID valueSize) {
std::lock_guard<std::mutex> lock(mMutex);
if (keySize < 0 || valueSize < 0) {
@@ -178,6 +168,4 @@
return mBlobCache.get();
}
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 7382b91..d10a615 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -20,21 +20,18 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include "FileBlobCache.h"
-
#include <memory>
#include <mutex>
#include <string>
-// ----------------------------------------------------------------------------
+#include "FileBlobCache.h"
+
namespace android {
-// ----------------------------------------------------------------------------
class egl_display_t;
class EGLAPI egl_cache_t {
public:
-
// get returns a pointer to the singleton egl_cache_t object. This
// singleton object will never be destroyed.
static egl_cache_t* get();
@@ -117,8 +114,6 @@
static egl_cache_t sCache;
};
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_CACHE_H
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 3b1cf71..07ec327 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -14,49 +14,41 @@
** limitations under the License.
*/
-#define __STDC_LIMIT_MACROS 1
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "egl_display.h"
+#include <SurfaceFlingerProperties.h>
+#include <android-base/properties.h>
+#include <android/dlext.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+
#include "../egl_impl.h"
-
-#include <EGL/eglext_angle.h>
-#include <private/EGL/display.h>
-
+#include "EGL/eglext_angle.h"
#include "Loader.h"
#include "egl_angle_platform.h"
#include "egl_cache.h"
#include "egl_object.h"
#include "egl_tls.h"
-
-#include <SurfaceFlingerProperties.h>
-#include <android-base/properties.h>
-#include <android/dlext.h>
-#include <dlfcn.h>
-#include <graphicsenv/GraphicsEnv.h>
-
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
+#include "private/EGL/display.h"
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
-static char const * const sVendorString = "Android";
-static char const* const sVersionString14 = "1.4 Android META-EGL";
-static char const* const sVersionString15 = "1.5 Android META-EGL";
-static char const * const sClientApiString = "OpenGL_ES";
+static const char* const sVendorString = "Android";
+static const char* const sVersionString14 = "1.4 Android META-EGL";
+static const char* const sVersionString15 = "1.5 Android META-EGL";
+static const char* const sClientApiString = "OpenGL_ES";
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
+extern const char* const gBuiltinExtensionString;
+extern const char* const gExtensionString;
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
-
-// ----------------------------------------------------------------------------
+extern void setGLHooksThreadSpecific(gl_hooks_t const* value);
bool findExtension(const char* exts, const char* name, size_t nameLen) {
if (exts) {
@@ -84,9 +76,12 @@
egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
-egl_display_t::egl_display_t() :
- magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0), eglIsInitialized(false) {
-}
+egl_display_t::egl_display_t()
+ : magic('_dpy'),
+ finishOnSwap(false),
+ traceGpuCompletion(false),
+ refs(0),
+ eglIsInitialized(false) {}
egl_display_t::~egl_display_t() {
magic = 0;
@@ -98,7 +93,7 @@
return nullptr;
}
- uintptr_t index = uintptr_t(dpy)-1U;
+ uintptr_t index = uintptr_t(dpy) - 1U;
if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) {
return nullptr;
}
@@ -128,8 +123,7 @@
EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp,
const EGLAttrib* attrib_list) {
- if (uintptr_t(disp) >= NUM_DISPLAYS)
- return nullptr;
+ if (uintptr_t(disp) >= NUM_DISPLAYS) return nullptr;
return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
}
@@ -204,7 +198,7 @@
// It is possible that eglGetPlatformDisplay does not have a
// working implementation for Android platform; in that case,
// one last fallback to eglGetDisplay
- if(dpy == EGL_NO_DISPLAY) {
+ if (dpy == EGL_NO_DISPLAY) {
if (attrib_list) {
ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored");
}
@@ -221,8 +215,7 @@
return EGLDisplay(uintptr_t(display) + 1U);
}
-EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) {
-
+EGLBoolean egl_display_t::initialize(EGLint* major, EGLint* minor) {
{ // scope for refLock
std::unique_lock<std::mutex> _l(refLock);
refs++;
@@ -230,7 +223,7 @@
// We don't know what to report until we know what the
// driver supports. Make sure we are initialized before
// returning the version info.
- while(!eglIsInitialized) {
+ while (!eglIsInitialized) {
refCond.wait(_l);
}
egl_connection_t* const cnx = &gEGLImpl;
@@ -243,7 +236,7 @@
if (minor != nullptr) *minor = cnx->minor;
return EGL_TRUE;
}
- while(eglIsInitialized) {
+ while (eglIsInitialized) {
refCond.wait(_l);
}
}
@@ -263,40 +256,31 @@
if (cnx->dso) {
EGLDisplay idpy = disp.dpy;
if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) {
- //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
+ // ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
// idpy, cnx->major, cnx->minor, cnx);
// display is now initialized
disp.state = egl_display_t::INITIALIZED;
// get the query-strings for this display for each implementation
- disp.queryString.vendor = cnx->egl.eglQueryString(idpy,
- EGL_VENDOR);
- disp.queryString.version = cnx->egl.eglQueryString(idpy,
- EGL_VERSION);
- disp.queryString.extensions = cnx->egl.eglQueryString(idpy,
- EGL_EXTENSIONS);
- disp.queryString.clientApi = cnx->egl.eglQueryString(idpy,
- EGL_CLIENT_APIS);
+ disp.queryString.vendor = cnx->egl.eglQueryString(idpy, EGL_VENDOR);
+ disp.queryString.version = cnx->egl.eglQueryString(idpy, EGL_VERSION);
+ disp.queryString.extensions = cnx->egl.eglQueryString(idpy, EGL_EXTENSIONS);
+ disp.queryString.clientApi = cnx->egl.eglQueryString(idpy, EGL_CLIENT_APIS);
} else {
ALOGW("eglInitialize(%p) failed (%s)", idpy,
- egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
+ egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
}
}
if (cnx->minor == 5) {
// full list in egl_entries.in
- if (!cnx->egl.eglCreateImage ||
- !cnx->egl.eglDestroyImage ||
- !cnx->egl.eglGetPlatformDisplay ||
- !cnx->egl.eglCreatePlatformWindowSurface ||
- !cnx->egl.eglCreatePlatformPixmapSurface ||
- !cnx->egl.eglCreateSync ||
- !cnx->egl.eglDestroySync ||
- !cnx->egl.eglClientWaitSync ||
- !cnx->egl.eglGetSyncAttrib ||
- !cnx->egl.eglWaitSync) {
+ if (!cnx->egl.eglCreateImage || !cnx->egl.eglDestroyImage ||
+ !cnx->egl.eglGetPlatformDisplay || !cnx->egl.eglCreatePlatformWindowSurface ||
+ !cnx->egl.eglCreatePlatformPixmapSurface || !cnx->egl.eglCreateSync ||
+ !cnx->egl.eglDestroySync || !cnx->egl.eglClientWaitSync ||
+ !cnx->egl.eglGetSyncAttrib || !cnx->egl.eglWaitSync) {
ALOGE("Driver indicates EGL 1.5 support, but does not have "
"a critical API");
cnx->minor = 4;
@@ -391,7 +375,6 @@
}
EGLBoolean egl_display_t::terminate() {
-
{ // scope for refLock
std::unique_lock<std::mutex> _rl(refLock);
if (refs == 0) {
@@ -424,7 +407,7 @@
}
if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
- egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
+ egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
}
// REVISIT: it's unclear what to do if eglTerminate() fails
disp.state = egl_display_t::TERMINATED;
@@ -457,8 +440,7 @@
return res;
}
-void egl_display_t::loseCurrent(egl_context_t * cur_c)
-{
+void egl_display_t::loseCurrent(egl_context_t* cur_c) {
if (cur_c) {
egl_display_t* display = cur_c->getDisplay();
if (display) {
@@ -467,8 +449,7 @@
}
}
-void egl_display_t::loseCurrentImpl(egl_context_t * cur_c)
-{
+void egl_display_t::loseCurrentImpl(egl_context_t* cur_c) {
// by construction, these are either 0 or valid (possibly terminated)
// it should be impossible for these to be invalid
ContextRef _cur_c(cur_c);
@@ -478,7 +459,6 @@
{ // scope for the lock
std::lock_guard<std::mutex> _l(lock);
cur_c->onLooseCurrent();
-
}
// This cannot be called with the lock held because it might end-up
@@ -489,10 +469,9 @@
_cur_d.release();
}
-EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
- EGLSurface draw, EGLSurface read, EGLContext /*ctx*/,
- EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx)
-{
+EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw,
+ EGLSurface read, EGLContext /*ctx*/, EGLSurface impl_draw,
+ EGLSurface impl_read, EGLContext impl_ctx) {
EGLBoolean result;
// by construction, these are either 0 or valid (possibly terminated)
@@ -504,14 +483,12 @@
{ // scope for the lock
std::lock_guard<std::mutex> _l(lock);
if (c) {
- result = c->cnx->egl.eglMakeCurrent(
- disp.dpy, impl_draw, impl_read, impl_ctx);
+ result = c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
c->onMakeCurrent(draw, read);
}
} else {
- result = cur_c->cnx->egl.eglMakeCurrent(
- disp.dpy, impl_draw, impl_read, impl_ctx);
+ result = cur_c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
cur_c->onLooseCurrent();
}
@@ -537,6 +514,23 @@
return findExtension(mExtensionString.c_str(), name, nameLen);
}
-// ----------------------------------------------------------------------------
+egl_display_t* validate_display(EGLDisplay dpy) {
+ egl_display_t* const dp = get_display(dpy);
+ if (!dp) return setError(EGL_BAD_DISPLAY, (egl_display_t*)nullptr);
+ if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (egl_display_t*)nullptr);
+
+ return dp;
+}
+
+egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx) {
+ *outCnx = nullptr;
+ egl_display_t* dp = validate_display(dpy);
+ if (!dp) return dp;
+ *outCnx = &gEGLImpl;
+ if ((*outCnx)->dso == nullptr) {
+ return setError(EGL_BAD_CONFIG, (egl_display_t*)nullptr);
+ }
+ return dp;
+}
+
}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index e117314..0155133 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -17,26 +17,20 @@
#ifndef ANDROID_EGL_DISPLAY_H
#define ANDROID_EGL_DISPLAY_H
-
-#include <stdint.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
#include <stddef.h>
+#include <stdint.h>
#include <condition_variable>
#include <mutex>
#include <string>
#include <unordered_set>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <cutils/compiler.h>
-
-#include "egldefs.h"
#include "../hooks.h"
+#include "egldefs.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
class egl_object_t;
class egl_context_t;
@@ -45,25 +39,23 @@
bool findExtension(const char* exts, const char* name, size_t nameLen = 0);
bool needsAndroidPEglMitigation();
-// ----------------------------------------------------------------------------
-
class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
static egl_display_t sDisplay[NUM_DISPLAYS];
EGLDisplay getDisplay(EGLNativeDisplayType display);
EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list);
- void loseCurrentImpl(egl_context_t * cur_c);
+ void loseCurrentImpl(egl_context_t* cur_c);
public:
enum {
NOT_INITIALIZED = 0,
- INITIALIZED = 1,
- TERMINATED = 2
+ INITIALIZED = 1,
+ TERMINATED = 2,
};
egl_display_t();
~egl_display_t();
- EGLBoolean initialize(EGLint *major, EGLint *minor);
+ EGLBoolean initialize(EGLint* major, EGLint* minor);
EGLBoolean terminate();
// add object to this display's list
@@ -76,123 +68,69 @@
static egl_display_t* get(EGLDisplay dpy);
static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list);
- EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c,
- EGLSurface draw, EGLSurface read, EGLContext ctx,
- EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx);
- static void loseCurrent(egl_context_t * cur_c);
+ EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw, EGLSurface read,
+ EGLContext ctx, EGLSurface impl_draw, EGLSurface impl_read,
+ EGLContext impl_ctx);
+ static void loseCurrent(egl_context_t* cur_c);
inline bool isReady() const { return (refs > 0); }
inline bool isValid() const { return magic == '_dpy'; }
inline bool isAlive() const { return isValid(); }
- char const * getVendorString() const { return mVendorString.c_str(); }
- char const * getVersionString() const { return mVersionString.c_str(); }
- char const * getClientApiString() const { return mClientApiString.c_str(); }
- char const * getExtensionString() const { return mExtensionString.c_str(); }
+ char const* getVendorString() const { return mVendorString.c_str(); }
+ char const* getVersionString() const { return mVersionString.c_str(); }
+ char const* getClientApiString() const { return mClientApiString.c_str(); }
+ char const* getExtensionString() const { return mExtensionString.c_str(); }
bool haveExtension(const char* name, size_t nameLen = 0) const;
inline uint32_t getRefsCount() const { return refs; }
struct strings_t {
- char const * vendor;
- char const * version;
- char const * clientApi;
- char const * extensions;
+ char const* vendor;
+ char const* version;
+ char const* clientApi;
+ char const* extensions;
};
struct DisplayImpl {
- DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) { }
- EGLDisplay dpy;
- EGLint state;
- strings_t queryString;
+ DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) {}
+ EGLDisplay dpy;
+ EGLint state;
+ strings_t queryString;
};
private:
- uint32_t magic;
+ uint32_t magic;
public:
- DisplayImpl disp;
- bool finishOnSwap; // property: debug.egl.finish
- bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion
- bool hasColorSpaceSupport;
+ DisplayImpl disp;
+ bool finishOnSwap; // property: debug.egl.finish
+ bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion
+ bool hasColorSpaceSupport;
private:
- friend class egl_display_ptr;
-
- uint32_t refs;
- bool eglIsInitialized;
- mutable std::mutex lock;
- mutable std::mutex refLock;
- mutable std::condition_variable refCond;
- std::unordered_set<egl_object_t*> objects;
- std::string mVendorString;
- std::string mVersionString;
- std::string mClientApiString;
- std::string mExtensionString;
+ uint32_t refs;
+ bool eglIsInitialized;
+ mutable std::mutex lock;
+ mutable std::mutex refLock;
+ mutable std::condition_variable refCond;
+ std::unordered_set<egl_object_t*> objects;
+ std::string mVendorString;
+ std::string mVersionString;
+ std::string mClientApiString;
+ std::string mExtensionString;
};
-// ----------------------------------------------------------------------------
-
-// An egl_display_ptr is a kind of smart pointer for egl_display_t objects.
-// It doesn't refcount the egl_display_t, but does ensure that the underlying
-// EGL implementation is "awake" (not hibernating) and ready for use as long
-// as the egl_display_ptr exists.
-class egl_display_ptr {
-public:
- explicit egl_display_ptr(egl_display_t* dpy): mDpy(dpy) {}
-
- // We only really need a C++11 move constructor, not a copy constructor.
- // A move constructor would save an enter()/leave() pair on every EGL API
- // call. But enabling -std=c++0x causes lots of errors elsewhere, so I
- // can't use a move constructor until those are cleaned up.
- //
- // egl_display_ptr(egl_display_ptr&& other) {
- // mDpy = other.mDpy;
- // other.mDpy = NULL;
- // }
- //
- egl_display_ptr(const egl_display_ptr& other): mDpy(other.mDpy) {}
-
- ~egl_display_ptr() {}
-
- const egl_display_t* operator->() const { return mDpy; }
- egl_display_t* operator->() { return mDpy; }
-
- const egl_display_t* get() const { return mDpy; }
- egl_display_t* get() { return mDpy; }
-
- operator bool() const { return mDpy != nullptr; }
-
-private:
- egl_display_t* mDpy;
-
- // non-assignable
- egl_display_ptr& operator=(const egl_display_ptr&);
-};
-
-// ----------------------------------------------------------------------------
-
-inline egl_display_ptr get_display(EGLDisplay dpy) {
- return egl_display_ptr(egl_display_t::get(dpy));
-}
-
-// Does not ensure EGL is unhibernated. Use with caution: calls into the
-// underlying EGL implementation are not safe.
-inline egl_display_t* get_display_nowake(EGLDisplay dpy) {
+inline egl_display_t* get_display(EGLDisplay dpy) {
return egl_display_t::get(dpy);
}
-// ----------------------------------------------------------------------------
-
-egl_display_ptr validate_display(EGLDisplay dpy);
-egl_display_ptr validate_display_connection(EGLDisplay dpy,
- egl_connection_t*& cnx);
+egl_display_t* validate_display(EGLDisplay dpy);
+egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx);
EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx);
EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface);
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_DISPLAY_H
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 2921d51..1c91f1d 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -104,11 +104,6 @@
EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
-/* IMG extensions */
-
-EGL_ENTRY(EGLBoolean, eglHibernateProcessIMG, void)
-EGL_ENTRY(EGLBoolean, eglAwakenProcessIMG, void)
-
/* Partial update extensions */
EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index ea86c9a..9752e38 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -85,17 +85,21 @@
// Look up which GPA we should use
int gpaIndex = func_indices["eglGetProcAddress"];
- ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, gpaIndex);
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name,
+ gpaIndex);
EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
- ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this address", name, gpaIndex, (unsigned long long)gpaNext);
-
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this "
+ "address",
+ name, gpaIndex, (unsigned long long)gpaNext);
// Call it for the requested function
typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
val = reinterpret_cast<EGLFuncPointer>(next(name));
- ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from GPA", name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from "
+ "GPA",
+ name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
// We should store it now, but to do that, we need to move func_idx to the class so we can
// increment it separately
@@ -105,7 +109,9 @@
int index = func_indices[name];
val = (*next_layer_funcs)[index];
- ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known entry", name, index, (unsigned long long)val);
+ ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known "
+ "entry",
+ name, index, (unsigned long long)val);
return reinterpret_cast<void*>(val);
}
@@ -117,20 +123,26 @@
// Some names overlap, only fill with initial entry
// This does mean that some indices will not be used
if (func_indices.find(name) == func_indices.end()) {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning now", name, func_idx);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning "
+ "now",
+ name, func_idx);
func_names[func_idx] = name;
func_indices[name] = func_idx;
} else {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name, func_idx);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name,
+ func_idx);
}
// Populate layer_functions once with initial value
// These values will arrive in priority order, starting with platform entries
if (functions[func_idx] == nullptr) {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning (%llu)", name, func_idx, (unsigned long long) *curr);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning "
+ "(%llu)",
+ name, func_idx, (unsigned long long)*curr);
functions[func_idx] = *curr;
} else {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, func_idx, (unsigned long long) functions[func_idx]);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name,
+ func_idx, (unsigned long long)functions[func_idx]);
}
entries++;
@@ -380,8 +392,8 @@
auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
char* error_message = nullptr;
- dlhandle_ = OpenNativeLibraryInNamespace(
- app_namespace, layer.c_str(), &native_bridge_, &error_message);
+ dlhandle_ = OpenNativeLibraryInNamespace(app_namespace, layer.c_str(),
+ &native_bridge_, &error_message);
if (!dlhandle_) {
ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
error_message);
diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h
index 1e2783f..705525d 100644
--- a/opengl/libs/EGL/egl_layers.h
+++ b/opengl/libs/EGL/egl_layers.h
@@ -17,19 +17,18 @@
#ifndef ANDROID_EGL_LAYERS_H
#define ANDROID_EGL_LAYERS_H
+#include <EGL/egldefs.h>
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <nativebridge/native_bridge.h>
+#include <nativeloader/native_loader.h>
+
#include <string>
#include <unordered_map>
#include <vector>
-#include <android/dlext.h>
-#include <dlfcn.h>
-
-#include <EGL/egldefs.h>
#include "egl_platform_entries.h"
-#include <nativebridge/native_bridge.h>
-#include <nativeloader/native_loader.h>
-
typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
namespace android {
@@ -46,8 +45,8 @@
void LoadLayers();
void InitLayers(egl_connection_t*);
- void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
- void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+ void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, const char* const*);
+ void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, const char* const*);
bool Initialized();
std::string GetDebugLayers();
@@ -59,18 +58,23 @@
std::vector<layer_setup_func> layer_setup_;
private:
- LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0), dlhandle_(nullptr), native_bridge_(false){};
+ LayerLoader()
+ : layers_loaded_(false),
+ initialized_(false),
+ current_layer_(0),
+ dlhandle_(nullptr),
+ native_bridge_(false){};
bool layers_loaded_;
bool initialized_;
unsigned current_layer_;
void* dlhandle_;
bool native_bridge_;
- template<typename Func = void*>
+ template <typename Func = void*>
Func GetTrampoline(const char* name) const {
if (native_bridge_) {
- return reinterpret_cast<Func>(android::NativeBridgeGetTrampoline(
- dlhandle_, name, nullptr, 0));
+ return reinterpret_cast<Func>(
+ android::NativeBridgeGetTrampoline(dlhandle_, name, nullptr, 0));
}
return reinterpret_cast<Func>(dlsym(dlhandle_, name));
}
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index ff4fe2d..847b351 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -18,19 +18,14 @@
#include <sstream>
-
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
-egl_object_t::egl_object_t(egl_display_t* disp) :
- display(disp), count(1) {
+egl_object_t::egl_object_t(egl_display_t* disp) : display(disp), count(1) {
// NOTE: this does an implicit incRef
display->addObject(this);
}
-egl_object_t::~egl_object_t() {
-}
+egl_object_t::~egl_object_t() {}
void egl_object_t::terminate() {
// this marks the object as "terminated"
@@ -53,8 +48,6 @@
return display->getObject(object);
}
-// ----------------------------------------------------------------------------
-
egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWindowType win,
EGLSurface surface, EGLint colorSpace, egl_connection_t const* cnx)
: egl_object_t(dpy),
@@ -66,10 +59,10 @@
colorSpace(colorSpace),
egl_smpte2086_dirty(false),
egl_cta861_3_dirty(false) {
- egl_smpte2086_metadata.displayPrimaryRed = { EGL_DONT_CARE, EGL_DONT_CARE };
- egl_smpte2086_metadata.displayPrimaryGreen = { EGL_DONT_CARE, EGL_DONT_CARE };
- egl_smpte2086_metadata.displayPrimaryBlue = { EGL_DONT_CARE, EGL_DONT_CARE };
- egl_smpte2086_metadata.whitePoint = { EGL_DONT_CARE, EGL_DONT_CARE };
+ egl_smpte2086_metadata.displayPrimaryRed = {EGL_DONT_CARE, EGL_DONT_CARE};
+ egl_smpte2086_metadata.displayPrimaryGreen = {EGL_DONT_CARE, EGL_DONT_CARE};
+ egl_smpte2086_metadata.displayPrimaryBlue = {EGL_DONT_CARE, EGL_DONT_CARE};
+ egl_smpte2086_metadata.whitePoint = {EGL_DONT_CARE, EGL_DONT_CARE};
egl_smpte2086_metadata.maxLuminance = EGL_DONT_CARE;
egl_smpte2086_metadata.minLuminance = EGL_DONT_CARE;
egl_cta861_3_metadata.maxFrameAverageLightLevel = EGL_DONT_CARE;
@@ -173,16 +166,30 @@
return EGL_FALSE;
}
- metadata.displayPrimaryRed.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.x) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryRed.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.y) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryGreen.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.x) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryGreen.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.y) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryBlue.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.x) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryBlue.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.y) / EGL_METADATA_SCALING_EXT;
- metadata.whitePoint.x = static_cast<float>(egl_smpte2086_metadata.whitePoint.x) / EGL_METADATA_SCALING_EXT;
- metadata.whitePoint.y = static_cast<float>(egl_smpte2086_metadata.whitePoint.y) / EGL_METADATA_SCALING_EXT;
- metadata.maxLuminance = static_cast<float>(egl_smpte2086_metadata.maxLuminance) / EGL_METADATA_SCALING_EXT;
- metadata.minLuminance = static_cast<float>(egl_smpte2086_metadata.minLuminance) / EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryRed.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.x) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryRed.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.y) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryGreen.x =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.x) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryGreen.y =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.y) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryBlue.x =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.x) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryBlue.y =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.y) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.whitePoint.x =
+ static_cast<float>(egl_smpte2086_metadata.whitePoint.x) / EGL_METADATA_SCALING_EXT;
+ metadata.whitePoint.y =
+ static_cast<float>(egl_smpte2086_metadata.whitePoint.y) / EGL_METADATA_SCALING_EXT;
+ metadata.maxLuminance =
+ static_cast<float>(egl_smpte2086_metadata.maxLuminance) / EGL_METADATA_SCALING_EXT;
+ metadata.minLuminance =
+ static_cast<float>(egl_smpte2086_metadata.minLuminance) / EGL_METADATA_SCALING_EXT;
return EGL_TRUE;
}
@@ -196,13 +203,15 @@
return EGL_FALSE;
}
- metadata.maxContentLightLevel = static_cast<float>(egl_cta861_3_metadata.maxContentLightLevel) / EGL_METADATA_SCALING_EXT;
- metadata.maxFrameAverageLightLevel = static_cast<float>(egl_cta861_3_metadata.maxFrameAverageLightLevel) / EGL_METADATA_SCALING_EXT;
+ metadata.maxContentLightLevel = static_cast<float>(egl_cta861_3_metadata.maxContentLightLevel) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.maxFrameAverageLightLevel =
+ static_cast<float>(egl_cta861_3_metadata.maxFrameAverageLightLevel) /
+ EGL_METADATA_SCALING_EXT;
return EGL_TRUE;
}
-
EGLBoolean egl_surface_t::getColorSpaceAttribute(EGLint attribute, EGLint* value) const {
if (attribute == EGL_GL_COLORSPACE_KHR) {
*value = colorSpace;
@@ -211,7 +220,7 @@
return EGL_FALSE;
}
-EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint *value) const {
+EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint* value) const {
switch (attribute) {
case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT:
*value = egl_smpte2086_metadata.displayPrimaryRed.x;
@@ -257,7 +266,7 @@
return EGL_FALSE;
}
-EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint *value) const {
+EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint* value) const {
switch (attribute) {
case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT:
*value = egl_cta861_3_metadata.maxContentLightLevel;
@@ -276,13 +285,16 @@
egl_object_t::terminate();
}
-// ----------------------------------------------------------------------------
-
egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
- egl_connection_t const* cnx, int version) :
- egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
- config(config), read(nullptr), draw(nullptr), cnx(cnx), version(version) {
-}
+ egl_connection_t const* cnx, int version)
+ : egl_object_t(get_display(dpy)),
+ dpy(dpy),
+ context(context),
+ config(config),
+ read(nullptr),
+ draw(nullptr),
+ cnx(cnx),
+ version(version) {}
void egl_context_t::onLooseCurrent() {
read = nullptr;
@@ -297,31 +309,39 @@
* Here we cache the GL_EXTENSIONS string for this context and we
* add the extensions always handled by the wrapper
*/
+ if (!gl_extensions.empty()) return;
- if (gl_extensions.empty()) {
- // call the implementation's glGetString(GL_EXTENSIONS)
- const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
+ // call the implementation's glGetString(GL_EXTENSIONS)
+ const char* exts = (const char*)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
+ if (!exts) return;
- // If this context is sharing with another context, and the other context was reset
- // e.g. due to robustness failure, this context might also be reset and glGetString can
- // return NULL.
- if (exts) {
- gl_extensions = exts;
- if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
- gl_extensions.insert(0, "GL_EXT_debug_marker ");
- }
+ // If this context is sharing with another context, and the other context was reset
+ // e.g. due to robustness failure, this context might also be reset and glGetString can
+ // return NULL.
+ gl_extensions = exts;
+ if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
+ gl_extensions.insert(0, "GL_EXT_debug_marker ");
+ // eglGetProcAddress could return function pointers to these
+ // functions while they actually don't work. Fix them now.
+ __eglMustCastToProperFunctionPointerType* f;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glInsertEventMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glPushGroupMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glPopGroupMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ }
- // tokenize the supported extensions for the glGetStringi() wrapper
- std::stringstream ss;
- std::string str;
- ss << gl_extensions;
- while (ss >> str) {
- tokenized_gl_extensions.push_back(str);
- }
- }
+ // tokenize the supported extensions for the glGetStringi() wrapper
+ std::stringstream ss;
+ std::string str;
+ ss << gl_extensions;
+ while (ss >> str) {
+ tokenized_gl_extensions.push_back(str);
}
}
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index fb2bdf4..e593b1c 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -17,30 +17,25 @@
#ifndef ANDROID_EGL_OBJECT_H
#define ANDROID_EGL_OBJECT_H
-#include <atomic>
-#include <stdint.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <log/log.h>
#include <stddef.h>
+#include <stdint.h>
+#include <system/window.h>
+#include <atomic>
#include <string>
#include <vector>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <system/window.h>
-
-#include <log/log.h>
-
#include "egl_display.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
class egl_display_t;
class egl_object_t {
- egl_display_t *display;
+ egl_display_t* display;
mutable std::atomic_size_t count;
protected:
@@ -64,6 +59,7 @@
egl_object_t* ref;
LocalRef() = delete;
LocalRef(const LocalRef* rhs) = delete;
+
public:
~LocalRef();
explicit LocalRef(egl_object_t* rhs);
@@ -73,9 +69,7 @@
ref = native;
}
}
- inline N* get() {
- return static_cast<N*>(ref);
- }
+ inline N* get() { return static_cast<N*>(ref); }
void acquire() const;
void release() const;
void terminate();
@@ -84,7 +78,7 @@
friend class LocalRef;
};
-template<typename N, typename T>
+template <typename N, typename T>
egl_object_t::LocalRef<N, T>::LocalRef(egl_object_t* rhs) : ref(rhs) {
if (ref) {
ref->incRef();
@@ -92,21 +86,21 @@
}
template <typename N, typename T>
-egl_object_t::LocalRef<N,T>::~LocalRef() {
+egl_object_t::LocalRef<N, T>::~LocalRef() {
if (ref) {
ref->destroy();
}
}
template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::acquire() const {
+void egl_object_t::LocalRef<N, T>::acquire() const {
if (ref) {
ref->incRef();
}
}
template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::release() const {
+void egl_object_t::LocalRef<N, T>::release() const {
if (ref) {
if (ref->decRef() == 1) {
// shouldn't happen because this is called from LocalRef
@@ -116,7 +110,7 @@
}
template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::terminate() {
+void egl_object_t::LocalRef<N, T>::terminate() {
if (ref) {
ref->terminate();
}
@@ -128,6 +122,7 @@
protected:
~egl_surface_t();
void terminate() override;
+
public:
typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
@@ -151,10 +146,13 @@
// it's not hard to imagine native games accessing them.
EGLSurface surface;
EGLConfig config;
+
private:
ANativeWindow* win;
+
public:
egl_connection_t const* cnx;
+
private:
bool connected;
void disconnect();
@@ -186,14 +184,15 @@
egl_cta861_3_metadata egl_cta861_3_metadata;
};
-class egl_context_t: public egl_object_t {
+class egl_context_t : public egl_object_t {
protected:
~egl_context_t() {}
+
public:
typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
- egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
- egl_connection_t const* cnx, int version);
+ egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, egl_connection_t const* cnx,
+ int version);
void onLooseCurrent();
void onMakeCurrent(EGLSurface draw, EGLSurface read);
@@ -209,30 +208,22 @@
std::vector<std::string> tokenized_gl_extensions;
};
-// ----------------------------------------------------------------------------
+typedef egl_surface_t::Ref SurfaceRef;
+typedef egl_context_t::Ref ContextRef;
-typedef egl_surface_t::Ref SurfaceRef;
-typedef egl_context_t::Ref ContextRef;
-
-// ----------------------------------------------------------------------------
-
-template<typename NATIVE, typename EGL>
+template <typename NATIVE, typename EGL>
static inline NATIVE* egl_to_native_cast(EGL arg) {
return reinterpret_cast<NATIVE*>(arg);
}
-static inline
-egl_surface_t* get_surface(EGLSurface surface) {
+static inline egl_surface_t* get_surface(EGLSurface surface) {
return egl_to_native_cast<egl_surface_t>(surface);
}
-static inline
-egl_context_t* get_context(EGLContext context) {
+static inline egl_context_t* get_context(EGLContext context) {
return egl_to_native_cast<egl_context_t>(context);
}
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_OBJECT_H
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index aa24e8e..398efc0 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -18,36 +18,32 @@
#include "egl_platform_entries.h"
-#include <ctype.h>
-#include <dlfcn.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <EGL/eglext_angle.h>
-
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware_buffer.h>
-#include <graphicsenv/GraphicsEnv.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
+#include <ctype.h>
#include <cutils/compiler.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <stdlib.h>
+#include <string.h>
#include <condition_variable>
#include <deque>
#include <mutex>
-#include <unordered_map>
#include <string>
#include <thread>
+#include <unordered_map>
#include "../egl_impl.h"
-
+#include "EGL/egl.h"
+#include "EGL/eglext.h"
+#include "EGL/eglext_angle.h"
#include "egl_display.h"
-#include "egl_object.h"
#include "egl_layers.h"
+#include "egl_object.h"
#include "egl_tls.h"
#include "egl_trace.h"
@@ -80,71 +76,70 @@
* NOTE: Both strings MUST have a single space as the last character.
*/
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
+extern const char* const gBuiltinExtensionString;
+extern const char* const gExtensionString;
// clang-format off
// Extensions implemented by the EGL wrapper.
-char const * const gBuiltinExtensionString =
- "EGL_KHR_get_all_proc_addresses "
- "EGL_ANDROID_presentation_time "
- "EGL_KHR_swap_buffers_with_damage "
- "EGL_ANDROID_get_native_client_buffer "
+const char* const gBuiltinExtensionString =
"EGL_ANDROID_front_buffer_auto_refresh "
"EGL_ANDROID_get_frame_timestamps "
- "EGL_EXT_surface_SMPTE2086_metadata "
+ "EGL_ANDROID_get_native_client_buffer "
+ "EGL_ANDROID_presentation_time "
"EGL_EXT_surface_CTA861_3_metadata "
+ "EGL_EXT_surface_SMPTE2086_metadata "
+ "EGL_KHR_get_all_proc_addresses "
+ "EGL_KHR_swap_buffers_with_damage "
;
// Allowed list of extensions exposed to applications if implemented in the vendor driver.
-char const * const gExtensionString =
- "EGL_KHR_image " // mandatory
- "EGL_KHR_image_base " // mandatory
+const char* const gExtensionString =
+ "EGL_ANDROID_image_native_buffer " // mandatory
+ "EGL_ANDROID_native_fence_sync " // strongly recommended
+ "EGL_ANDROID_recordable " // mandatory
+ "EGL_EXT_buffer_age " // strongly recommended with partial_update
+ "EGL_EXT_create_context_robustness "
"EGL_EXT_image_gl_colorspace "
- "EGL_KHR_image_pixmap "
- "EGL_KHR_lock_surface "
+ "EGL_EXT_pixel_format_float "
+ "EGL_EXT_protected_content "
+ "EGL_EXT_yuv_surface "
+ "EGL_IMG_context_priority "
+ "EGL_KHR_config_attribs "
+ "EGL_KHR_create_context "
+ "EGL_KHR_create_context_no_error "
+ "EGL_KHR_fence_sync "
"EGL_KHR_gl_colorspace "
+ "EGL_KHR_gl_renderbuffer_image "
"EGL_KHR_gl_texture_2D_image "
"EGL_KHR_gl_texture_3D_image "
"EGL_KHR_gl_texture_cubemap_image "
- "EGL_KHR_gl_renderbuffer_image "
+ "EGL_KHR_image " // mandatory
+ "EGL_KHR_image_base " // mandatory
+ "EGL_KHR_image_pixmap "
+ "EGL_KHR_lock_surface "
+ "EGL_KHR_mutable_render_buffer "
+ "EGL_KHR_no_config_context "
+ "EGL_KHR_partial_update " // strongly recommended
"EGL_KHR_reusable_sync "
- "EGL_KHR_fence_sync "
- "EGL_KHR_create_context "
- "EGL_KHR_config_attribs "
- "EGL_KHR_surfaceless_context "
"EGL_KHR_stream "
- "EGL_KHR_stream_fifo "
- "EGL_KHR_stream_producer_eglsurface "
"EGL_KHR_stream_consumer_gltexture "
"EGL_KHR_stream_cross_process_fd "
- "EGL_EXT_create_context_robustness "
- "EGL_NV_system_time "
- "EGL_ANDROID_image_native_buffer " // mandatory
+ "EGL_KHR_stream_fifo "
+ "EGL_KHR_stream_producer_eglsurface "
+ "EGL_KHR_surfaceless_context "
"EGL_KHR_wait_sync " // strongly recommended
- "EGL_ANDROID_recordable " // mandatory
- "EGL_KHR_partial_update " // strongly recommended
- "EGL_EXT_pixel_format_float "
- "EGL_EXT_buffer_age " // strongly recommended with partial_update
- "EGL_KHR_create_context_no_error "
- "EGL_KHR_mutable_render_buffer "
- "EGL_EXT_yuv_surface "
- "EGL_EXT_protected_content "
- "EGL_IMG_context_priority "
- "EGL_KHR_no_config_context "
+ "EGL_NV_system_time "
;
-char const * const gClientExtensionString =
+const char* const gClientExtensionString =
+ "EGL_ANDROID_GLES_layers "
+ "EGL_ANGLE_platform_angle "
"EGL_EXT_client_extensions "
"EGL_KHR_platform_android "
- "EGL_ANGLE_platform_angle "
- "EGL_ANDROID_GLES_layers";
-// clang-format on
+ ;
// extensions not exposed to applications but used by the ANDROID system
// "EGL_ANDROID_blob_cache " // strongly recommended
-// "EGL_IMG_hibernate_process " // optional
-// "EGL_ANDROID_native_fence_sync " // strongly recommended
// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1
/*
@@ -154,105 +149,69 @@
*/
static const extension_map_t sExtensionMap[] = {
// EGL_KHR_lock_surface
- { "eglLockSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
- { "eglUnlockSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
+ { "eglLockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
+ { "eglUnlockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
// EGL_KHR_image, EGL_KHR_image_base
- { "eglCreateImageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
- { "eglDestroyImageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
+ { "eglCreateImageKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
+ { "eglDestroyImageKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
// EGL_KHR_reusable_sync, EGL_KHR_fence_sync
- { "eglCreateSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
- { "eglDestroySyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
- { "eglClientWaitSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
- { "eglSignalSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
- { "eglGetSyncAttribKHR",
- (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
+ { "eglCreateSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
+ { "eglDestroySyncKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
+ { "eglClientWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
+ { "eglSignalSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
+ { "eglGetSyncAttribKHR", (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
// EGL_NV_system_time
- { "eglGetSystemTimeFrequencyNV",
- (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
- { "eglGetSystemTimeNV",
- (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
+ { "eglGetSystemTimeFrequencyNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
+ { "eglGetSystemTimeNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
// EGL_KHR_wait_sync
- { "eglWaitSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
+ { "eglWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
// EGL_ANDROID_presentation_time
- { "eglPresentationTimeANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
+ { "eglPresentationTimeANDROID", (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
// EGL_KHR_swap_buffers_with_damage
- { "eglSwapBuffersWithDamageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
+ { "eglSwapBuffersWithDamageKHR", (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
// EGL_ANDROID_get_native_client_buffer
- { "eglGetNativeClientBufferANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
+ { "eglGetNativeClientBufferANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
// EGL_KHR_partial_update
- { "eglSetDamageRegionKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
+ { "eglSetDamageRegionKHR", (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
- { "eglCreateStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
- { "eglDestroyStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
- { "eglStreamAttribKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
- { "eglQueryStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
- { "eglQueryStreamu64KHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
- { "eglQueryStreamTimeKHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
- { "eglCreateStreamProducerSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
- { "eglStreamConsumerGLTextureExternalKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
- { "eglStreamConsumerAcquireKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
- { "eglStreamConsumerReleaseKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
- { "eglGetStreamFileDescriptorKHR",
- (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
- { "eglCreateStreamFromFileDescriptorKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+ { "eglCreateStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
+ { "eglDestroyStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
+ { "eglStreamAttribKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
+ { "eglQueryStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
+ { "eglQueryStreamu64KHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
+ { "eglQueryStreamTimeKHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
+ { "eglCreateStreamProducerSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
+ { "eglStreamConsumerGLTextureExternalKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
+ { "eglStreamConsumerAcquireKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
+ { "eglStreamConsumerReleaseKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
+ { "eglGetStreamFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
+ { "eglCreateStreamFromFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
// EGL_ANDROID_get_frame_timestamps
- { "eglGetNextFrameIdANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
- { "eglGetCompositorTimingANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
- { "eglGetCompositorTimingSupportedANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
- { "eglGetFrameTimestampsANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
- { "eglGetFrameTimestampSupportedANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
+ { "eglGetNextFrameIdANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
+ { "eglGetCompositorTimingANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
+ { "eglGetCompositorTimingSupportedANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
+ { "eglGetFrameTimestampsANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+ { "eglGetFrameTimestampSupportedANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
// EGL_ANDROID_native_fence_sync
- { "eglDupNativeFenceFDANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
+ { "eglDupNativeFenceFDANDROID", (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
};
+// clang-format on
/*
* These extensions entry-points should not be exposed to applications.
* They're used internally by the Android EGL layer.
*/
-#define FILTER_EXTENSIONS(procname) \
- (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \
- !strcmp((procname), "eglHibernateProcessIMG") || \
- !strcmp((procname), "eglAwakenProcessIMG"))
+#define FILTER_EXTENSIONS(procname) (!strcmp((procname), "eglSetBlobCacheFuncsANDROID"))
// accesses protected by sExtensionMapMutex
static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtensionMap;
@@ -261,9 +220,8 @@
static int sGLExtensionSlot = 0;
static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
-static void(*findProcAddress(const char* name,
- const extension_map_t* map, size_t n))() {
- for (uint32_t i=0 ; i<n ; i++) {
+static void (*findProcAddress(const char* name, const extension_map_t* map, size_t n))() {
+ for (uint32_t i = 0; i < n; i++) {
if (!strcmp(name, map[i].name)) {
return map[i].address;
}
@@ -273,14 +231,17 @@
// ----------------------------------------------------------------------------
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
+extern void setGLHooksThreadSpecific(gl_hooks_t const* value);
extern EGLBoolean egl_init_drivers();
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
+extern const __eglMustCastToProperFunctionPointerType
+ gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
extern gl_hooks_t gHooksTrace;
// ----------------------------------------------------------------------------
-static inline EGLContext getContext() { return egl_tls_t::getContext(); }
+static inline EGLContext getContext() {
+ return egl_tls_t::getContext();
+}
// ----------------------------------------------------------------------------
@@ -313,9 +274,8 @@
// Initialization
// ----------------------------------------------------------------------------
-EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor)
-{
- egl_display_ptr dp = get_display(dpy);
+EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint* major, EGLint* minor) {
+ egl_display_t* dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
EGLBoolean res = dp->initialize(major, minor);
@@ -323,13 +283,12 @@
return res;
}
-EGLBoolean eglTerminateImpl(EGLDisplay dpy)
-{
+EGLBoolean eglTerminateImpl(EGLDisplay dpy) {
// NOTE: don't unload the drivers b/c some APIs can be called
// after eglTerminate() has been called. eglTerminate() only
// terminates an EGLDisplay, not a EGL itself.
- egl_display_ptr dp = get_display(dpy);
+ egl_display_t* dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
EGLBoolean res = dp->terminate();
@@ -341,14 +300,12 @@
// configuration
// ----------------------------------------------------------------------------
-EGLBoolean eglGetConfigsImpl(EGLDisplay dpy,
- EGLConfig *configs,
- EGLint config_size, EGLint *num_config)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetConfigsImpl(EGLDisplay dpy, EGLConfig* configs, EGLint config_size,
+ EGLint* num_config) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- if (num_config==nullptr) {
+ if (num_config == nullptr) {
return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
}
@@ -357,96 +314,88 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso) {
- res = cnx->egl.eglGetConfigs(
- dp->disp.dpy, configs, config_size, num_config);
+ res = cnx->egl.eglGetConfigs(dp->disp.dpy, configs, config_size, num_config);
}
return res;
}
-EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list,
- EGLConfig *configs, EGLint config_size,
- EGLint *num_config)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglChooseConfigImpl(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs,
+ EGLint config_size, EGLint* num_config) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- if (num_config==nullptr) {
+ if (num_config == nullptr) {
return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
}
- EGLBoolean res = EGL_FALSE;
*num_config = 0;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso) {
- if (attrib_list) {
- if (base::GetBoolProperty("debug.egl.force_msaa", false)) {
- size_t attribCount = 0;
- EGLint attrib = attrib_list[0];
+ if (!cnx->dso) return EGL_FALSE;
- // Only enable MSAA if the context is OpenGL ES 2.0 and
- // if no caveat is requested
- const EGLint *attribRendererable = nullptr;
- const EGLint *attribCaveat = nullptr;
+ if (!attrib_list || !base::GetBoolProperty("debug.egl.force_msaa", false))
+ return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size,
+ num_config);
- // Count the number of attributes and look for
- // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
- while (attrib != EGL_NONE) {
- attrib = attrib_list[attribCount];
- switch (attrib) {
- case EGL_RENDERABLE_TYPE:
- attribRendererable = &attrib_list[attribCount];
- break;
- case EGL_CONFIG_CAVEAT:
- attribCaveat = &attrib_list[attribCount];
- break;
- default:
- break;
- }
- attribCount++;
- }
+ // Force 4x MSAA
+ size_t attribCount = 0;
+ EGLint attrib = attrib_list[0];
- if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
- (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+ // Only enable MSAA if the context is OpenGL ES 2.0 and
+ // if no caveat is requested
+ const EGLint* attribRendererable = nullptr;
+ const EGLint* attribCaveat = nullptr;
- // Insert 2 extra attributes to force-enable MSAA 4x
- EGLint aaAttribs[attribCount + 4];
- aaAttribs[0] = EGL_SAMPLE_BUFFERS;
- aaAttribs[1] = 1;
- aaAttribs[2] = EGL_SAMPLES;
- aaAttribs[3] = 4;
-
- memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
-
- EGLint numConfigAA;
- EGLBoolean resAA = cnx->egl.eglChooseConfig(
- dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
-
- if (resAA == EGL_TRUE && numConfigAA > 0) {
- ALOGD("Enabling MSAA 4x");
- *num_config = numConfigAA;
- return resAA;
- }
- }
- }
+ // Count the number of attributes and look for
+ // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
+ while (attrib != EGL_NONE) {
+ attrib = attrib_list[attribCount];
+ switch (attrib) {
+ case EGL_RENDERABLE_TYPE:
+ attribRendererable = &attrib_list[attribCount];
+ break;
+ case EGL_CONFIG_CAVEAT:
+ attribCaveat = &attrib_list[attribCount];
+ break;
+ default:
+ break;
}
-
- res = cnx->egl.eglChooseConfig(
- dp->disp.dpy, attrib_list, configs, config_size, num_config);
+ attribCount++;
}
- return res;
+
+ if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
+ (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+ // Insert 2 extra attributes to force-enable MSAA 4x
+ EGLint aaAttribs[attribCount + 4];
+ aaAttribs[0] = EGL_SAMPLE_BUFFERS;
+ aaAttribs[1] = 1;
+ aaAttribs[2] = EGL_SAMPLES;
+ aaAttribs[3] = 4;
+
+ memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
+
+ EGLint numConfigAA;
+ EGLBoolean resAA = cnx->egl.eglChooseConfig(dp->disp.dpy, aaAttribs, configs, config_size,
+ &numConfigAA);
+
+ if (resAA == EGL_TRUE && numConfigAA > 0) {
+ ALOGD("Enabling MSAA 4x");
+ *num_config = numConfigAA;
+ return resAA;
+ }
+ }
+
+ return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size, num_config);
}
-EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config,
- EGLint attribute, EGLint *value)
-{
+EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, EGLint attribute,
+ EGLint* value) {
egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (!dp) return EGL_FALSE;
- return cnx->egl.eglGetConfigAttrib(
- dp->disp.dpy, config, attribute, value);
+ return cnx->egl.eglGetConfigAttrib(dp->disp.dpy, config, attribute, value);
}
// ----------------------------------------------------------------------------
@@ -484,7 +433,7 @@
}
// Returns a list of color spaces understood by the vendor EGL driver.
-static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) {
+static std::vector<EGLint> getDriverColorSpaces(egl_display_t* dp) {
std::vector<EGLint> colorSpaces;
// sRGB and linear are always supported when color space support is present.
@@ -509,7 +458,8 @@
if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) {
colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
}
- if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
+ if (findExtension(dp->disp.queryString.extensions,
+ "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT);
}
return colorSpaces;
@@ -519,7 +469,7 @@
// If there is no color space attribute in attrib_list, colorSpace is left
// unmodified.
template <typename AttrType>
-static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window,
+static EGLBoolean processAttributes(egl_display_t* dp, ANativeWindow* window,
const AttrType* attrib_list, EGLint* colorSpace,
std::vector<AttrType>* strippedAttribList) {
for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
@@ -699,7 +649,7 @@
}
template <typename AttrType, typename CreateFuncType>
-EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config,
+EGLSurface eglCreateWindowSurfaceTmpl(egl_display_t* dp, egl_connection_t* cnx, EGLConfig config,
ANativeWindow* window, const AttrType* attrib_list,
CreateFuncType createWindowSurfaceFunc) {
const AttrType* origAttribList = attrib_list;
@@ -768,7 +718,7 @@
EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface,
+ egl_surface_t* s = new egl_surface_t(dp, config, window, surface,
getReportedColorSpace(colorSpace), cnx);
return s;
}
@@ -789,8 +739,8 @@
EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
const EGLint* attrib_list) {
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ egl_connection_t* cnx = nullptr;
+ egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
return eglCreateWindowSurfaceTmpl<
EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list,
@@ -801,8 +751,8 @@
EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window,
const EGLAttrib* attrib_list) {
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ egl_connection_t* cnx = nullptr;
+ egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
if (cnx->egl.eglCreatePlatformWindowSurface) {
@@ -842,8 +792,8 @@
// belongs to the Android platform. Any such call fails and generates
// an EGL_BAD_PARAMETER error.
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ egl_connection_t* cnx = nullptr;
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
}
@@ -853,7 +803,7 @@
EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) {
egl_connection_t* cnx = nullptr;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
}
@@ -863,36 +813,33 @@
EGLSurface eglCreatePbufferSurfaceImpl(EGLDisplay dpy, EGLConfig config,
const EGLint* attrib_list) {
egl_connection_t* cnx = nullptr;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
- getNativePixelFormat(iDpy, cnx, config, &format);
+ egl_display_t* dp = validate_display_connection(dpy, &cnx);
+ if (!dp) return EGL_NO_SURFACE;
- // Select correct colorspace based on user's attribute list
- EGLint colorSpace = EGL_UNKNOWN;
- std::vector<EGLint> strippedAttribList;
- if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) {
- ALOGE("error invalid colorspace: %d", colorSpace);
- return EGL_NO_SURFACE;
- }
- attrib_list = strippedAttribList.data();
+ EGLDisplay iDpy = dp->disp.dpy;
+ android_pixel_format format;
+ getNativePixelFormat(iDpy, cnx, config, &format);
- EGLSurface surface = cnx->egl.eglCreatePbufferSurface(dp->disp.dpy, config, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
- getReportedColorSpace(colorSpace), cnx);
- return s;
- }
+ // Select correct colorspace based on user's attribute list
+ EGLint colorSpace = EGL_UNKNOWN;
+ std::vector<EGLint> strippedAttribList;
+ if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) {
+ ALOGE("error invalid colorspace: %d", colorSpace);
+ return EGL_NO_SURFACE;
}
- return EGL_NO_SURFACE;
+ attrib_list = strippedAttribList.data();
+
+ EGLSurface surface = cnx->egl.eglCreatePbufferSurface(iDpy, config, attrib_list);
+ if (surface == EGL_NO_SURFACE) return surface;
+
+ return new egl_surface_t(dp, config, nullptr, surface, getReportedColorSpace(colorSpace), cnx);
}
EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
egl_surface_t* const s = get_surface(surface);
@@ -905,10 +852,10 @@
EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute,
EGLint* value) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
egl_surface_t const* const s = get_surface(surface);
@@ -923,12 +870,12 @@
}
void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return;
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
setError(EGL_BAD_SURFACE, EGL_FALSE);
}
@@ -938,14 +885,13 @@
// Contexts
// ----------------------------------------------------------------------------
-EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config,
- EGLContext share_list, const EGLint *attrib_list)
-{
+EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, EGLContext share_list,
+ const EGLint* attrib_list) {
egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
if (share_list != EGL_NO_CONTEXT) {
- if (!ContextRef(dp.get(), share_list).get()) {
+ if (!ContextRef(dp, share_list).get()) {
return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
}
egl_context_t* const c = get_context(share_list);
@@ -967,8 +913,8 @@
}
};
}
- EGLContext context = cnx->egl.eglCreateContext(
- dp->disp.dpy, config, share_list, attrib_list);
+ EGLContext context =
+ cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
// figure out if it's a GLESv1 or GLESv2
int version = egl_connection_t::GLESv1_INDEX;
@@ -992,17 +938,14 @@
return EGL_NO_CONTEXT;
}
-EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx)
-{
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp)
- return EGL_FALSE;
+EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx) {
+ const egl_display_t* dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
- ContextRef _c(dp.get(), ctx);
- if (!_c.get())
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ ContextRef _c(dp, ctx);
+ if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
- egl_context_t * const c = get_context(ctx);
+ egl_context_t* const c = get_context(ctx);
EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
if (result == EGL_TRUE) {
_c.terminate();
@@ -1010,24 +953,21 @@
return result;
}
-EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw,
- EGLSurface read, EGLContext ctx)
-{
- egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglMakeCurrentImpl(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
+ egl_display_t* dp = validate_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
// If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
// EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
// a valid but uninitialized display.
- if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
- (draw != EGL_NO_SURFACE) ) {
+ if ((ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || (draw != EGL_NO_SURFACE)) {
if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
}
// get a reference to the object passed in
- ContextRef _c(dp.get(), ctx);
- SurfaceRef _d(dp.get(), draw);
- SurfaceRef _r(dp.get(), read);
+ ContextRef _c(dp, ctx);
+ SurfaceRef _d(dp, draw);
+ SurfaceRef _r(dp, read);
// validate the context (if not EGL_NO_CONTEXT)
if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
@@ -1036,17 +976,17 @@
}
// these are the underlying implementation's object
- EGLContext impl_ctx = EGL_NO_CONTEXT;
+ EGLContext impl_ctx = EGL_NO_CONTEXT;
EGLSurface impl_draw = EGL_NO_SURFACE;
EGLSurface impl_read = EGL_NO_SURFACE;
// these are our objects structs passed in
- egl_context_t * c = nullptr;
- egl_surface_t const * d = nullptr;
- egl_surface_t const * r = nullptr;
+ egl_context_t* c = nullptr;
+ egl_surface_t const* d = nullptr;
+ egl_surface_t const* r = nullptr;
// these are the current objects structs
- egl_context_t * cur_c = get_context(getContext());
+ egl_context_t* cur_c = get_context(getContext());
if (ctx != EGL_NO_CONTEXT) {
c = get_context(ctx);
@@ -1078,10 +1018,7 @@
impl_read = r->surface;
}
-
- EGLBoolean result = dp->makeCurrent(c, cur_c,
- draw, read, ctx,
- impl_draw, impl_read, impl_ctx);
+ EGLBoolean result = dp->makeCurrent(c, cur_c, draw, read, ctx, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
if (c) {
@@ -1102,81 +1039,72 @@
return result;
}
-EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx,
- EGLint attribute, EGLint *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryContextImpl(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- ContextRef _c(dp.get(), ctx);
+ ContextRef _c(dp, ctx);
if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
- egl_context_t * const c = get_context(ctx);
- return c->cnx->egl.eglQueryContext(
- dp->disp.dpy, c->context, attribute, value);
-
+ egl_context_t* const c = get_context(ctx);
+ return c->cnx->egl.eglQueryContext(dp->disp.dpy, c->context, attribute, value);
}
-EGLContext eglGetCurrentContextImpl(void)
-{
+EGLContext eglGetCurrentContextImpl(void) {
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would correctly return EGL_NO_CONTEXT.
EGLContext ctx = getContext();
return ctx;
}
-EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw)
-{
+EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw) {
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would correctly return EGL_NO_SURFACE.
EGLContext ctx = getContext();
if (ctx) {
- egl_context_t const * const c = get_context(ctx);
+ egl_context_t const* const c = get_context(ctx);
if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
switch (readdraw) {
- case EGL_READ: return c->read;
- case EGL_DRAW: return c->draw;
- default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ case EGL_READ:
+ return c->read;
+ case EGL_DRAW:
+ return c->draw;
+ default:
+ return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
}
}
return EGL_NO_SURFACE;
}
-EGLDisplay eglGetCurrentDisplayImpl(void)
-{
+EGLDisplay eglGetCurrentDisplayImpl(void) {
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would correctly return EGL_NO_DISPLAY.
EGLContext ctx = getContext();
if (ctx) {
- egl_context_t const * const c = get_context(ctx);
+ egl_context_t const* const c = get_context(ctx);
if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
return c->dpy;
}
return EGL_NO_DISPLAY;
}
-EGLBoolean eglWaitGLImpl(void)
-{
+EGLBoolean eglWaitGLImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
return cnx->egl.eglWaitGL();
}
-EGLBoolean eglWaitNativeImpl(EGLint engine)
-{
+EGLBoolean eglWaitNativeImpl(EGLint engine) {
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
return cnx->egl.eglWaitNative(engine);
}
-EGLint eglGetErrorImpl(void)
-{
+EGLint eglGetErrorImpl(void) {
EGLint err = EGL_SUCCESS;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso) {
@@ -1188,8 +1116,7 @@
return err;
}
-static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
- const char* procname) {
+static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(const char* procname) {
const egl_connection_t* cnx = &gEGLImpl;
void* proc = nullptr;
@@ -1205,8 +1132,7 @@
return nullptr;
}
-__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname)
-{
+__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char* procname) {
if (FILTER_EXTENSIONS(procname)) {
return nullptr;
}
@@ -1253,13 +1179,10 @@
// Ensure we have room to track it
const int slot = sGLExtensionSlot;
if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
-
if (cnx->dso && cnx->egl.eglGetProcAddress) {
-
// Extensions are independent of the bound context
addr = cnx->egl.eglGetProcAddress(procname);
if (addr) {
-
// purposefully track the bottom of the stack in extensionMap
extensionMap[name] = addr;
@@ -1268,7 +1191,7 @@
// Track the top most entry point return the extension forwarder
cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
addr = gExtensionForwarders[slot];
// Remember the slot for this extension
@@ -1300,7 +1223,7 @@
// Track the top most entry point and return the extension forwarder
cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[ext_slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
addr = gExtensionForwarders[ext_slot];
}
@@ -1310,7 +1233,6 @@
class FrameCompletionThread {
public:
-
static void queueSync(EGLSyncKHR sync) {
static FrameCompletionThread thread;
@@ -1327,7 +1249,6 @@
}
private:
-
FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
std::thread thread(&FrameCompletionThread::loop, this);
thread.detach();
@@ -1382,15 +1303,13 @@
std::mutex mMutex;
};
-EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw,
- EGLint *rects, EGLint n_rects)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+ EGLint n_rects) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), draw);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, draw);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
if (n_rects < 0 || (n_rects > 0 && rects == NULL))
return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
@@ -1406,11 +1325,11 @@
if (CC_UNLIKELY(dp->finishOnSwap)) {
uint32_t pixel;
- egl_context_t * const c = get_context( egl_tls_t::getContext() );
+ egl_context_t* const c = get_context(egl_tls_t::getContext());
if (c) {
// glReadPixels() ensures that the frame is complete
- s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
- GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
+ s->cnx->hooks[c->version]->gl.glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
+ &pixel);
}
}
@@ -1445,41 +1364,35 @@
}
if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
- return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
- rects, n_rects);
- } else {
- return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+ return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, rects, n_rects);
}
+
+ return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
}
-EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface)
-{
+EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface) {
return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0);
}
-EGLBoolean eglCopyBuffersImpl( EGLDisplay dpy, EGLSurface surface,
- NativePixmapType target)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglCopyBuffersImpl(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
}
-const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name)
-{
+const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name) {
if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) {
// Return list of client extensions
return gClientExtensionString;
}
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return (const char *) nullptr;
+ const egl_display_t* dp = validate_display(dpy);
+ if (!dp) return (const char*)nullptr;
switch (name) {
case EGL_VENDOR:
@@ -1493,13 +1406,12 @@
default:
break;
}
- return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+ return setError(EGL_BAD_PARAMETER, (const char*)nullptr);
}
-EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name)
-{
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return (const char *) nullptr;
+EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name) {
+ const egl_display_t* dp = validate_display(dpy);
+ if (!dp) return (const char*)nullptr;
switch (name) {
case EGL_VENDOR:
@@ -1513,24 +1425,22 @@
default:
break;
}
- return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+ return setError(EGL_BAD_PARAMETER, (const char*)nullptr);
}
// ----------------------------------------------------------------------------
// EGL 1.1
// ----------------------------------------------------------------------------
-EGLBoolean eglSurfaceAttribImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute,
+ EGLint value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t * const s = get_surface(surface);
+ egl_surface_t* const s = get_surface(surface);
if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
if (!s->getNativeWindow()) {
@@ -1553,51 +1463,41 @@
} else if (s->setCta8613Attribute(attribute, value)) {
return EGL_TRUE;
} else if (s->cnx->egl.eglSurfaceAttrib) {
- return s->cnx->egl.eglSurfaceAttrib(
- dp->disp.dpy, s->surface, attribute, value);
+ return s->cnx->egl.eglSurfaceAttrib(dp->disp.dpy, s->surface, attribute, value);
}
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglBindTexImageImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglBindTexImageImpl(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglBindTexImage) {
- return s->cnx->egl.eglBindTexImage(
- dp->disp.dpy, s->surface, buffer);
+ return s->cnx->egl.eglBindTexImage(dp->disp.dpy, s->surface, buffer);
}
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglReleaseTexImageImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglReleaseTexImageImpl(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglReleaseTexImage) {
- return s->cnx->egl.eglReleaseTexImage(
- dp->disp.dpy, s->surface, buffer);
+ return s->cnx->egl.eglReleaseTexImage(dp->disp.dpy, s->surface, buffer);
}
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean res = EGL_TRUE;
@@ -1609,16 +1509,13 @@
return res;
}
-
// ----------------------------------------------------------------------------
// EGL 1.2
// ----------------------------------------------------------------------------
-EGLBoolean eglWaitClientImpl(void)
-{
+EGLBoolean eglWaitClientImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
EGLBoolean res;
if (cnx->egl.eglWaitClient) {
@@ -1629,8 +1526,7 @@
return res;
}
-EGLBoolean eglBindAPIImpl(EGLenum api)
-{
+EGLBoolean eglBindAPIImpl(EGLenum api) {
// bind this API on all EGLs
EGLBoolean res = EGL_TRUE;
egl_connection_t* const cnx = &gEGLImpl;
@@ -1640,8 +1536,7 @@
return res;
}
-EGLenum eglQueryAPIImpl(void)
-{
+EGLenum eglQueryAPIImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryAPI) {
return cnx->egl.eglQueryAPI();
@@ -1651,8 +1546,7 @@
return EGL_OPENGL_ES_API;
}
-EGLBoolean eglReleaseThreadImpl(void)
-{
+EGLBoolean eglReleaseThreadImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglReleaseThread) {
cnx->egl.eglReleaseThread();
@@ -1665,16 +1559,15 @@
return EGL_TRUE;
}
-EGLSurface eglCreatePbufferFromClientBufferImpl(
- EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
- EGLConfig config, const EGLint *attrib_list)
-{
+EGLSurface eglCreatePbufferFromClientBufferImpl(EGLDisplay dpy, EGLenum buftype,
+ EGLClientBuffer buffer, EGLConfig config,
+ const EGLint* attrib_list) {
egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (!dp) return EGL_FALSE;
if (cnx->egl.eglCreatePbufferFromClientBuffer) {
- return cnx->egl.eglCreatePbufferFromClientBuffer(
- dp->disp.dpy, buftype, buffer, config, attrib_list);
+ return cnx->egl.eglCreatePbufferFromClientBuffer(dp->disp.dpy, buftype, buffer, config,
+ attrib_list);
}
return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
}
@@ -1683,34 +1576,28 @@
// EGL_EGLEXT_VERSION 3
// ----------------------------------------------------------------------------
-EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface,
- const EGLint *attrib_list)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglLockSurfaceKHR) {
- return s->cnx->egl.eglLockSurfaceKHR(
- dp->disp.dpy, s->surface, attrib_list);
+ return s->cnx->egl.eglLockSurfaceKHR(dp->disp.dpy, s->surface, attrib_list);
}
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglUnlockSurfaceKHR) {
return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
}
@@ -1723,7 +1610,7 @@
EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
EGLClientBuffer buffer, const AttrType* attrib_list,
FuncType eglCreateImageFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_IMAGE_KHR;
std::vector<AttrType> strippedAttribs;
@@ -1732,7 +1619,7 @@
// EGL_GL_COLORSPACE_LINEAR_KHR, EGL_GL_COLORSPACE_SRGB_KHR and
// EGL_GL_COLORSPACE_DEFAULT_EXT if EGL_EXT_image_gl_colorspace is supported,
// but some drivers don't like the DEFAULT value and generate an error.
- for (const AttrType *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
+ for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
if (attr[0] == EGL_GL_COLORSPACE_KHR &&
dp->haveExtension("EGL_EXT_image_gl_colorspace")) {
if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR &&
@@ -1746,14 +1633,15 @@
strippedAttribs.push_back(EGL_NONE);
}
- ContextRef _c(dp.get(), ctx);
+ ContextRef _c(dp, ctx);
egl_context_t* const c = _c.get();
EGLImageKHR result = EGL_NO_IMAGE_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && eglCreateImageFunc) {
result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer,
- needsAndroidPEglMitigation() ? strippedAttribs.data() : attrib_list);
+ needsAndroidPEglMitigation() ? strippedAttribs.data()
+ : attrib_list);
}
return result;
}
@@ -1792,7 +1680,7 @@
EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img,
PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1829,7 +1717,7 @@
template <typename AttrType, typename FuncType>
EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list,
FuncType eglCreateSyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_SYNC_KHR;
egl_connection_t* const cnx = &gEGLImpl;
@@ -1868,7 +1756,7 @@
EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync,
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1897,7 +1785,7 @@
}
EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1910,7 +1798,7 @@
EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout,
PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLint result = EGL_FALSE;
@@ -1942,7 +1830,7 @@
template <typename AttrType, typename FuncType>
EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value,
FuncType eglGetSyncAttribFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1987,106 +1875,93 @@
.eglGetSyncAttribKHR);
}
-EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint* attrib_list) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_STREAM_KHR;
EGLStreamKHR result = EGL_NO_STREAM_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
- result = cnx->egl.eglCreateStreamKHR(
- dp->disp.dpy, attrib_list);
+ result = cnx->egl.eglCreateStreamKHR(dp->disp.dpy, attrib_list);
}
return result;
}
-EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
- result = cnx->egl.eglDestroyStreamKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglDestroyStreamKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLint value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLint value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
- result = cnx->egl.eglStreamAttribKHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglStreamAttribKHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
-EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLint *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLint* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
- result = cnx->egl.eglQueryStreamKHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglQueryStreamKHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
-EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLuint64KHR *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLuint64KHR* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
- result = cnx->egl.eglQueryStreamu64KHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglQueryStreamu64KHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
-EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLTimeKHR *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLTimeKHR* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
- result = cnx->egl.eglQueryStreamTimeKHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglQueryStreamTimeKHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config,
- EGLStreamKHR stream, const EGLint *attrib_list)
-{
- egl_display_ptr dp = validate_display(dpy);
+ EGLStreamKHR stream, const EGLint* attrib_list) {
+ egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_SURFACE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
- EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
- dp->disp.dpy, config, stream, attrib_list);
+ EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(dp->disp.dpy, config,
+ stream, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
+ egl_surface_t* s = new egl_surface_t(dp, config, nullptr, surface,
EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
return s;
}
@@ -2094,77 +1969,63 @@
return EGL_NO_SURFACE;
}
-EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
- result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
- result = cnx->egl.eglStreamConsumerAcquireKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglStreamConsumerAcquireKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
- result = cnx->egl.eglStreamConsumerReleaseKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglStreamConsumerReleaseKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(
- EGLDisplay dpy, EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
- result = cnx->egl.eglGetStreamFileDescriptorKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglGetStreamFileDescriptorKHR(dpy, stream);
}
return result;
}
-EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(
- EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(EGLDisplay dpy,
+ EGLNativeFileDescriptorKHR file_descriptor) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_STREAM_KHR;
EGLStreamKHR result = EGL_NO_STREAM_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
- result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
- dp->disp.dpy, file_descriptor);
+ result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(dp->disp.dpy, file_descriptor);
}
return result;
}
@@ -2177,7 +2038,7 @@
template <typename ReturnType, typename FuncType>
ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags,
FuncType eglWaitSyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
ReturnType result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
@@ -2214,9 +2075,8 @@
// ANDROID extensions
// ----------------------------------------------------------------------------
-EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
@@ -2228,42 +2088,33 @@
}
EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLnsecsANDROID time)
-{
- const egl_display_ptr dp = validate_display(dpy);
+ EGLnsecsANDROID time) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return EGL_FALSE;
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
setError(EGL_BAD_SURFACE, EGL_FALSE);
return EGL_FALSE;
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
native_window_set_buffers_timestamp(s->getNativeWindow(), time);
return EGL_TRUE;
}
-EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) {
- // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
- // this function cannot be implemented when this libEGL is built for
- // vendors.
-#ifndef __ANDROID_VNDK__
+EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer* buffer) {
if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
- return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
-#else
- return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-#endif
+ return const_cast<ANativeWindowBuffer*>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
}
// ----------------------------------------------------------------------------
// NVIDIA extensions
// ----------------------------------------------------------------------------
-EGLuint64NV eglGetSystemTimeFrequencyNVImpl()
-{
+EGLuint64NV eglGetSystemTimeFrequencyNVImpl() {
EGLuint64NV ret = 0;
egl_connection_t* const cnx = &gEGLImpl;
@@ -2274,8 +2125,7 @@
return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
}
-EGLuint64NV eglGetSystemTimeNVImpl()
-{
+EGLuint64NV eglGetSystemTimeNVImpl() {
EGLuint64NV ret = 0;
egl_connection_t* const cnx = &gEGLImpl;
@@ -2289,43 +2139,40 @@
// ----------------------------------------------------------------------------
// Partial update extension
// ----------------------------------------------------------------------------
-EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface,
- EGLint *rects, EGLint n_rects)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface, EGLint* rects,
+ EGLint n_rects) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
setError(EGL_BAD_DISPLAY, EGL_FALSE);
return EGL_FALSE;
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
setError(EGL_BAD_SURFACE, EGL_FALSE);
return EGL_FALSE;
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglSetDamageRegionKHR) {
- return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
- rects, n_rects);
+ return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, rects, n_rects);
}
return EGL_FALSE;
}
-EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLuint64KHR *frameId) {
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (!s->getNativeWindow()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2346,19 +2193,19 @@
}
EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
-{
- const egl_display_ptr dp = validate_display(dpy);
+ EGLint numTimestamps, const EGLint* names,
+ EGLnsecsANDROID* values) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (!s->getNativeWindow()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2384,36 +2231,35 @@
}
}
- int ret = native_window_get_compositor_timing(s->getNativeWindow(),
- compositeDeadline, compositeInterval, compositeToPresentLatency);
+ int ret = native_window_get_compositor_timing(s->getNativeWindow(), compositeDeadline,
+ compositeInterval, compositeToPresentLatency);
switch (ret) {
- case 0:
- return EGL_TRUE;
- case -ENOSYS:
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- default:
- // This should not happen. Return an error that is not in the spec
- // so it's obvious something is very wrong.
- ALOGE("eglGetCompositorTiming: Unexpected error.");
- return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ case 0:
+ return EGL_TRUE;
+ case -ENOSYS:
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ default:
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ ALOGE("eglGetCompositorTiming: Unexpected error.");
+ return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
}
}
-EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint name)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLint name) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
ANativeWindow* window = s->getNativeWindow();
if (!window) {
@@ -2431,20 +2277,19 @@
}
EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
- EGLnsecsANDROID *values)
-{
- const egl_display_ptr dp = validate_display(dpy);
+ EGLuint64KHR frameId, EGLint numTimestamps,
+ const EGLint* timestamps, EGLnsecsANDROID* values) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (!s->getNativeWindow()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2494,10 +2339,11 @@
}
}
- int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
- requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
- lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
- dequeueReadyTime, releaseTime);
+ int ret =
+ native_window_get_frame_timestamps(s->getNativeWindow(), frameId, requestedPresentTime,
+ acquireTime, latchTime, firstRefreshStartTime,
+ lastRefreshStartTime, gpuCompositionDoneTime,
+ displayPresentTime, dequeueReadyTime, releaseTime);
switch (ret) {
case 0:
@@ -2516,20 +2362,19 @@
}
}
-EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLint timestamp) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
ANativeWindow* window = s->getNativeWindow();
if (!window) {
@@ -2551,8 +2396,7 @@
return EGL_TRUE;
case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
int value = 0;
- window->query(window,
- NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+ window->query(window, NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
return value == 0 ? EGL_FALSE : EGL_TRUE;
}
default:
@@ -2560,25 +2404,25 @@
}
}
-const GLubyte * glGetStringImpl(GLenum name) {
- const GLubyte * ret = egl_get_string_for_current_context(name);
+const GLubyte* glGetStringImpl(GLenum name) {
+ const GLubyte* ret = egl_get_string_for_current_context(name);
if (ret == NULL) {
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if(_c) ret = _c->glGetString(name);
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
+ if (_c) ret = _c->glGetString(name);
}
return ret;
}
-const GLubyte * glGetStringiImpl(GLenum name, GLuint index) {
- const GLubyte * ret = egl_get_string_for_current_context(name, index);
+const GLubyte* glGetStringiImpl(GLenum name, GLuint index) {
+ const GLubyte* ret = egl_get_string_for_current_context(name, index);
if (ret == NULL) {
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if(_c) ret = _c->glGetStringi(name, index);
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
+ if (_c) ret = _c->glGetStringi(name, index);
}
return ret;
}
-void glGetBooleanvImpl(GLenum pname, GLboolean * data) {
+void glGetBooleanvImpl(GLenum pname, GLboolean* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2587,11 +2431,11 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetBooleanv(pname, data);
}
-void glGetFloatvImpl(GLenum pname, GLfloat * data) {
+void glGetFloatvImpl(GLenum pname, GLfloat* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2600,11 +2444,11 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetFloatv(pname, data);
}
-void glGetIntegervImpl(GLenum pname, GLint * data) {
+void glGetIntegervImpl(GLenum pname, GLint* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2613,11 +2457,11 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetIntegerv(pname, data);
}
-void glGetInteger64vImpl(GLenum pname, GLint64 * data) {
+void glGetInteger64vImpl(GLenum pname, GLint64* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2626,7 +2470,7 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetInteger64v(pname, data);
}
@@ -2635,8 +2479,8 @@
EGLFuncPointer address;
};
+// clang-format off
static const implementation_map_t sPlatformImplMap[] = {
- // clang-format off
{ "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl },
{ "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl },
{ "eglInitialize", (EGLFuncPointer)&eglInitializeImpl },
@@ -2723,11 +2567,10 @@
{ "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl },
{ "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl },
{ "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl },
- // clang-format on
};
+// clang-format on
-EGLFuncPointer FindPlatformImplAddr(const char* name)
-{
+EGLFuncPointer FindPlatformImplAddr(const char* name) {
static const bool DEBUG = false;
if (name == nullptr) {
@@ -2741,7 +2584,8 @@
return nullptr;
}
if (!strcmp(name, sPlatformImplMap[i].name)) {
- ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name);
+ ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)",
+ (unsigned long long)sPlatformImplMap[i].address, i, name);
return sPlatformImplMap[i].address;
}
}
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index 8d118e0..dd1dcc2 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -16,10 +16,10 @@
#include "egl_tls.h"
-#include <stdlib.h>
-
#include <android-base/properties.h>
#include <log/log.h>
+#include <stdlib.h>
+
#include "CallStack.h"
#include "egl_platform_entries.h"
@@ -28,33 +28,46 @@
pthread_key_t egl_tls_t::sKey = TLS_KEY_NOT_INITIALIZED;
pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT;
-egl_tls_t::egl_tls_t()
- : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {
-}
+egl_tls_t::egl_tls_t() : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {}
-const char *egl_tls_t::egl_strerror(EGLint err) {
+const char* egl_tls_t::egl_strerror(EGLint err) {
switch (err) {
- case EGL_SUCCESS: return "EGL_SUCCESS";
- case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
- case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
- case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
- case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
- case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
- case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
- case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
- case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
- case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
- case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
- case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
- case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
- case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
- case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
- default: return "UNKNOWN";
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return "UNKNOWN";
}
}
-void egl_tls_t::validateTLSKey()
-{
+void egl_tls_t::validateTLSKey() {
struct TlsKeyInitializer {
static void create() { pthread_key_create(&sKey, destructTLSData); }
};
@@ -88,14 +101,12 @@
"EGL TLS data still exists after eglReleaseThread");
}
-void egl_tls_t::setErrorEtcImpl(
- const char* caller, int line, EGLint error, bool quiet) {
+void egl_tls_t::setErrorEtcImpl(const char* caller, int line, EGLint error, bool quiet) {
validateTLSKey();
egl_tls_t* tls = getTLS();
if (tls->error != error) {
if (!quiet) {
- ALOGE("%s:%d error %x (%s)",
- caller, line, error, egl_strerror(error));
+ ALOGE("%s:%d error %x (%s)", caller, line, error, egl_strerror(error));
if (base::GetBoolProperty("debug.egl.callstack", false)) {
CallStack::log(LOG_TAG);
}
@@ -111,7 +122,6 @@
return true;
}
return false;
-
}
egl_tls_t* egl_tls_t::getTLS() {
@@ -161,10 +171,9 @@
if (sKey == TLS_KEY_NOT_INITIALIZED) {
return EGL_NO_CONTEXT;
}
- egl_tls_t* tls = (egl_tls_t *)pthread_getspecific(sKey);
+ egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey);
if (!tls) return EGL_NO_CONTEXT;
return tls->ctx;
}
-
} // namespace android
diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h
index 86a375c..b5fcc1a 100644
--- a/opengl/libs/EGL/egl_tls.h
+++ b/opengl/libs/EGL/egl_tls.h
@@ -18,12 +18,9 @@
#define ANDROID_EGL_TLS_H
#include <EGL/egl.h>
-
#include <pthread.h>
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
class DbgContext;
@@ -32,15 +29,14 @@
static pthread_key_t sKey;
static pthread_once_t sOnceKey;
- EGLint error;
- EGLContext ctx;
- bool logCallWithNoContext;
+ EGLint error;
+ EGLContext ctx;
+ bool logCallWithNoContext;
egl_tls_t();
static void validateTLSKey();
static void destructTLSData(void* data);
- static void setErrorEtcImpl(
- const char* caller, int line, EGLint error, bool quiet);
+ static void setErrorEtcImpl(const char* caller, int line, EGLint error, bool quiet);
public:
static egl_tls_t* getTLS();
@@ -50,24 +46,20 @@
static void setContext(EGLContext ctx);
static EGLContext getContext();
static bool logNoContextCall();
- static const char *egl_strerror(EGLint err);
+ static const char* egl_strerror(EGLint err);
- template<typename T>
- static T setErrorEtc(const char* caller,
- int line, EGLint error, T returnValue, bool quiet = false) {
+ template <typename T>
+ static T setErrorEtc(const char* caller, int line, EGLint error, T returnValue,
+ bool quiet = false) {
setErrorEtcImpl(caller, line, error, quiet);
return returnValue;
}
};
-#define setError(_e, _r) \
- egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
+#define setError(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
-#define setErrorQuiet(_e, _r) \
- egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r, true)
+#define setErrorQuiet(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r, true)
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_TLS_H
diff --git a/opengl/libs/EGL/egl_trace.h b/opengl/libs/EGL/egl_trace.h
index 7664de2..ffdf676 100644
--- a/opengl/libs/EGL/egl_trace.h
+++ b/opengl/libs/EGL/egl_trace.h
@@ -18,16 +18,14 @@
#if defined(__ANDROID__)
-#include <stdint.h>
-
#include <cutils/trace.h>
+#include <stdint.h>
// See <cutils/trace.h> for more ATRACE_* macros.
// ATRACE_NAME traces from its location until the end of its enclosing scope.
-#define _PASTE(x, y) x ## y
-#define PASTE(x, y) _PASTE(x,y)
-#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+#define PASTE(x, y) x##y
+#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__)(ATRACE_TAG, name)
// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
@@ -36,13 +34,9 @@
class EglScopedTrace {
public:
- inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) {
- atrace_begin(mTag, name);
- }
+ inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); }
- inline ~EglScopedTrace() {
- atrace_end(mTag);
- }
+ inline ~EglScopedTrace() { atrace_end(mTag); }
private:
uint64_t mTag;
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 5fbffbd..fcc11f1 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -17,40 +17,32 @@
#ifndef ANDROID_EGLDEFS_H
#define ANDROID_EGLDEFS_H
+#include <log/log.h>
+
#include "../hooks.h"
#include "egl_platform_entries.h"
-#include <log/log.h>
-
#define VERSION_MAJOR 1
#define VERSION_MINOR 4
#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
-// EGLDisplay are global, not attached to a given thread
+// EGLDisplay are global, not attached to a given thread
const unsigned int NUM_DISPLAYS = 1;
-// ----------------------------------------------------------------------------
+extern const char* const platform_names[];
-extern char const * const platform_names[];
-
-// clang-format off
struct egl_connection_t {
- enum {
- GLESv1_INDEX = 0,
- GLESv2_INDEX = 1
- };
+ enum { GLESv1_INDEX = 0, GLESv2_INDEX = 1 };
- inline egl_connection_t() : dso(nullptr),
- libEgl(nullptr),
- libGles1(nullptr),
- libGles2(nullptr),
- systemDriverUnloaded(false) {
-
- char const* const* entries = platform_names;
+ inline egl_connection_t()
+ : dso(nullptr),
+ libEgl(nullptr),
+ libGles1(nullptr),
+ libGles2(nullptr),
+ systemDriverUnloaded(false) {
+ const char* const* entries = platform_names;
EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
while (*entries) {
const char* name = *entries;
@@ -66,41 +58,34 @@
}
}
- void * dso;
- gl_hooks_t * hooks[2];
- EGLint major;
- EGLint minor;
- EGLint driverVersion;
- egl_t egl;
+ void* dso;
+ gl_hooks_t* hooks[2];
+ EGLint major;
+ EGLint minor;
+ EGLint driverVersion;
+ egl_t egl;
// Functions implemented or redirected by platform libraries
- platform_impl_t platform;
+ platform_impl_t platform;
- void* libEgl;
- void* libGles1;
- void* libGles2;
+ void* libEgl;
+ void* libGles1;
+ void* libGles2;
- bool systemDriverUnloaded;
- bool useAngle; // Was ANGLE successfully loaded
+ bool systemDriverUnloaded;
+ bool useAngle; // Was ANGLE successfully loaded
};
-// clang-format on
-
-// ----------------------------------------------------------------------------
extern gl_hooks_t gHooks[2];
extern gl_hooks_t gHooksNoContext;
extern pthread_key_t gGLWrapperKey;
extern "C" void gl_unimplemented();
extern "C" void gl_noop();
-
-extern char const * const gl_names[];
-extern char const * const gl_names_1[];
-extern char const * const egl_names[];
-
+extern const char* const gl_names[];
+extern const char* const gl_names_1[];
+extern const char* const egl_names[];
extern egl_connection_t gEGLImpl;
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif /* ANDROID_EGLDEFS_H */
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index fedc789..b3d6f74 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -16,15 +16,12 @@
#include <ctype.h>
#include <errno.h>
-#include <stdlib.h>
-
#include <log/log.h>
+#include <stdlib.h>
#include "egldefs.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
#undef API_ENTRY
#undef CALL_GL_EXTENSION_API
@@ -34,6 +31,7 @@
#undef GL_EXTENSION_LIST
#undef GET_TLS
+// clang-format off
#if defined(__arm__)
#define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n"
@@ -239,13 +237,13 @@
name(248) name(249) name(250) name(251) name(252) name(253) name(254) name(255)
-GL_EXTENSION_LIST( GL_EXTENSION )
+GL_EXTENSION_LIST(GL_EXTENSION)
-#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n),
+#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n),
+// clang-format on
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {
- GL_EXTENSION_LIST( GL_EXTENSION_ARRAY )
- };
+extern const __eglMustCastToProperFunctionPointerType
+ gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {GL_EXTENSION_LIST(GL_EXTENSION_ARRAY)};
#undef GET_TLS
#undef GL_EXTENSION_LIST
@@ -255,7 +253,4 @@
#undef API_ENTRY
#undef CALL_GL_EXTENSION_API
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
-
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
index 4767406..321bb83 100644
--- a/services/automotive/display/AutomotiveDisplayProxyService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -34,7 +34,7 @@
sp<IBinder> displayToken = nullptr;
sp<SurfaceControl> surfaceControl = nullptr;
if (it == mDisplays.end()) {
- displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id);
+ displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
if (displayToken == nullptr) {
ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
return nullptr;
@@ -145,7 +145,7 @@
auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
ids.resize(displayIds.size());
for (auto i = 0; i < displayIds.size(); ++i) {
- ids[i] = displayIds[i];
+ ids[i] = displayIds[i].value;
}
_cb(ids);
@@ -157,7 +157,7 @@
HwDisplayConfig activeConfig;
HwDisplayState activeState;
- auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id);
+ auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
if (displayToken == nullptr) {
ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
} else {
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 6eed24a..1bcaab4 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -21,6 +21,8 @@
"libbinder",
"libcutils",
"libgfxstats",
+ "libgpumem",
+ "libgpumemtracer",
"libgraphicsenv",
"liblog",
"libutils",
@@ -85,6 +87,10 @@
name: "gpuservice",
defaults: ["libgpuservice_binary"],
init_rc: ["gpuservice.rc"],
+ required: [
+ "bpfloader",
+ "gpu_mem.o",
+ ],
srcs: [":gpuservice_binary_sources"],
shared_libs: [
"libgpuservice",
diff --git a/services/gpuservice/CleanSpec.mk b/services/gpuservice/CleanSpec.mk
new file mode 100644
index 0000000..482fc6d
--- /dev/null
+++ b/services/gpuservice/CleanSpec.mk
@@ -0,0 +1,52 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# Remove gpu_mem.o
+$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/frameworks/native/services/gpuservice/bpf)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpu_mem.o_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpu_mem.o_gpu_mem.o_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpu_mem.o)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpu_mem.o-timestamp)
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 81b0a46..52d5d4f 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,13 +24,16 @@
#include <binder/Parcel.h>
#include <binder/PermissionCache.h>
#include <cutils/properties.h>
+#include <gpumem/GpuMem.h>
#include <gpustats/GpuStats.h>
#include <private/android_filesystem_config.h>
+#include <tracing/GpuMemTracer.h>
#include <utils/String8.h>
#include <utils/Trace.h>
-
#include <vkjson.h>
+#include <thread>
+
namespace android {
using base::StringAppendF;
@@ -45,7 +48,16 @@
const char* const GpuService::SERVICE_NAME = "gpu";
-GpuService::GpuService() : mGpuStats(std::make_unique<GpuStats>()){};
+GpuService::GpuService()
+ : mGpuMem(std::make_shared<GpuMem>()),
+ mGpuStats(std::make_unique<GpuStats>()),
+ mGpuMemTracer(std::make_unique<GpuMemTracer>()) {
+ std::thread asyncInitThread([this]() {
+ mGpuMem->initialize();
+ mGpuMemTracer->initialize(mGpuMem);
+ });
+ asyncInitThread.detach();
+};
void GpuService::setGpuStats(const std::string& driverPackageName,
const std::string& driverVersionName, uint64_t driverVersionCode,
@@ -110,6 +122,7 @@
} else {
bool dumpAll = true;
bool dumpDriverInfo = false;
+ bool dumpMem = false;
bool dumpStats = false;
size_t numArgs = args.size();
@@ -119,15 +132,21 @@
dumpStats = true;
} else if (args[index] == String16("--gpudriverinfo")) {
dumpDriverInfo = true;
+ } else if (args[index] == String16("--gpumem")) {
+ dumpMem = true;
}
}
- dumpAll = !(dumpDriverInfo || dumpStats);
+ dumpAll = !(dumpDriverInfo || dumpMem || dumpStats);
}
if (dumpAll || dumpDriverInfo) {
dumpGameDriverInfo(&result);
result.append("\n");
}
+ if (dumpAll || dumpMem) {
+ mGpuMem->dump(args, &result);
+ result.append("\n");
+ }
if (dumpAll || dumpStats) {
mGpuStats->dump(args, &result);
result.append("\n");
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index d1c3aab..409084b 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -28,7 +28,9 @@
namespace android {
+class GpuMem;
class GpuStats;
+class GpuMemTracer;
class GpuService : public BnGpuService, public PriorityDumper {
public:
@@ -74,7 +76,9 @@
/*
* Attributes
*/
+ std::shared_ptr<GpuMem> mGpuMem;
std::unique_ptr<GpuStats> mGpuStats;
+ std::unique_ptr<GpuMemTracer> mGpuMemTracer;
std::mutex mLock;
std::string mDeveloperDriverPath;
};
diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp
new file mode 100644
index 0000000..b875814
--- /dev/null
+++ b/services/gpuservice/bpfprogs/Android.bp
@@ -0,0 +1,22 @@
+// 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.
+
+bpf {
+ name: "gpu_mem.o",
+ srcs: ["gpu_mem.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpu_mem.c
new file mode 100644
index 0000000..c75213b
--- /dev/null
+++ b/services/gpuservice/bpfprogs/gpu_mem.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <bpf_helpers.h>
+
+/*
+ * On Android the number of active processes using gpu is limited.
+ * So this is assumed to be true: SUM(num_procs_using_gpu[i]) <= 1024
+ */
+#define GPU_MEM_TOTAL_MAP_SIZE 1024
+
+/*
+ * This map maintains the global and per process gpu memory total counters.
+ *
+ * The KEY is ((gpu_id << 32) | pid) while VAL is the size in bytes.
+ * Use HASH type here since key is not int.
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ */
+DEFINE_BPF_MAP_GRO(gpu_mem_total_map, HASH, uint64_t, uint64_t, GPU_MEM_TOTAL_MAP_SIZE,
+ AID_GRAPHICS);
+
+/* This struct aligns with the fields offsets of the raw tracepoint format */
+struct gpu_mem_total_args {
+ uint64_t ignore;
+ /* Actual fields start at offset 8 */
+ uint32_t gpu_id;
+ uint32_t pid;
+ uint64_t size;
+};
+
+/*
+ * This program parses the gpu_mem/gpu_mem_total tracepoint's data into
+ * {KEY, VAL} pair used to update the corresponding bpf map.
+ *
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ * Upon seeing size 0, the corresponding KEY needs to be cleaned up.
+ */
+DEFINE_BPF_PROG("tracepoint/gpu_mem/gpu_mem_total", AID_ROOT, AID_GRAPHICS, tp_gpu_mem_total)
+(struct gpu_mem_total_args* args) {
+ uint64_t key = 0;
+ uint64_t cur_val = 0;
+ uint64_t* prev_val = NULL;
+
+ /* The upper 32 bits are for gpu_id while the lower is the pid */
+ key = ((uint64_t)args->gpu_id << 32) | args->pid;
+ cur_val = args->size;
+
+ if (!cur_val) {
+ bpf_gpu_mem_total_map_delete_elem(&key);
+ return 0;
+ }
+
+ prev_val = bpf_gpu_mem_total_map_lookup_elem(&key);
+ if (prev_val) {
+ *prev_val = cur_val;
+ } else {
+ bpf_gpu_mem_total_map_update_elem(&key, &cur_val, BPF_NOEXIST);
+ }
+ return 0;
+}
+
+char _license[] SEC("license") = "Apache 2.0";
diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp
new file mode 100644
index 0000000..b2230b6
--- /dev/null
+++ b/services/gpuservice/gpumem/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libgpumem",
+ srcs: [
+ "GpuMem.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: [
+ "libbase",
+ "libbpf_android",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
new file mode 100644
index 0000000..c78322e
--- /dev/null
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 "GpuMem"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "gpumem/GpuMem.h"
+
+#include <android-base/stringprintf.h>
+#include <libbpf.h>
+#include <libbpf_android.h>
+#include <log/log.h>
+#include <unistd.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+using base::StringAppendF;
+
+GpuMem::~GpuMem() {
+ bpf_detach_tracepoint(kGpuMemTraceGroup, kGpuMemTotalTracepoint);
+}
+
+void GpuMem::initialize() {
+ // Make sure bpf programs are loaded
+ bpf::waitForProgsLoaded();
+
+ int fd = bpf::bpfFdGet(kGpuMemTotalProgPath, BPF_F_RDONLY);
+ if (fd < 0) {
+ ALOGE("Failed to retrieve pinned program from %s", kGpuMemTotalProgPath);
+ return;
+ }
+
+ // Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
+ int count = 0;
+ while (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
+ if (++count > kGpuWaitTimeout) {
+ ALOGE("Failed to attach bpf program to %s/%s tracepoint", kGpuMemTraceGroup,
+ kGpuMemTotalTracepoint);
+ return;
+ }
+ // Retry until GPU driver loaded or timeout.
+ sleep(1);
+ }
+
+ // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
+ auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath);
+ if (!map.isValid()) {
+ ALOGE("Failed to create bpf map from %s", kGpuMemTotalMapPath);
+ return;
+ }
+ setGpuMemTotalMap(map);
+
+ mInitialized.store(true);
+}
+
+void GpuMem::setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+ mGpuMemTotalMap = std::move(map);
+}
+
+// Dump the snapshots of global and per process memory usage on all gpus
+void GpuMem::dump(const Vector<String16>& /* args */, std::string* result) {
+ ATRACE_CALL();
+
+ if (!mInitialized.load() || !mGpuMemTotalMap.isValid()) {
+ result->append("Failed to initialize GPU memory eBPF\n");
+ return;
+ }
+
+ auto res = mGpuMemTotalMap.getFirstKey();
+ if (!res.ok()) {
+ result->append("GPU memory total usage map is empty\n");
+ return;
+ }
+ uint64_t key = res.value();
+ // unordered_map<gpu_id, vector<pair<pid, size>>>
+ std::unordered_map<uint32_t, std::vector<std::pair<uint32_t, uint64_t>>> dumpMap;
+ while (true) {
+ uint32_t gpu_id = key >> 32;
+ uint32_t pid = key;
+
+ res = mGpuMemTotalMap.readValue(key);
+ if (!res.ok()) break;
+ uint64_t size = res.value();
+
+ dumpMap[gpu_id].emplace_back(pid, size);
+
+ res = mGpuMemTotalMap.getNextKey(key);
+ if (!res.ok()) break;
+ key = res.value();
+ }
+
+ for (auto& gpu : dumpMap) {
+ if (gpu.second.empty()) continue;
+ StringAppendF(result, "Memory snapshot for GPU %u:\n", gpu.first);
+
+ std::sort(gpu.second.begin(), gpu.second.end(),
+ [](auto& l, auto& r) { return l.first < r.first; });
+
+ int i = 0;
+ if (gpu.second[0].first != 0) {
+ StringAppendF(result, "Global total: N/A\n");
+ } else {
+ StringAppendF(result, "Global total: %" PRIu64 "\n", gpu.second[0].second);
+ i++;
+ }
+ for (; i < gpu.second.size(); i++) {
+ StringAppendF(result, "Proc %u total: %" PRIu64 "\n", gpu.second[i].first,
+ gpu.second[i].second);
+ }
+ }
+}
+
+void GpuMem::traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid,
+ uint64_t size)>& callback) {
+ auto res = mGpuMemTotalMap.getFirstKey();
+ if (!res.ok()) return;
+ uint64_t key = res.value();
+ while (true) {
+ uint32_t gpu_id = key >> 32;
+ uint32_t pid = key;
+
+ res = mGpuMemTotalMap.readValue(key);
+ if (!res.ok()) break;
+ uint64_t size = res.value();
+
+ callback(systemTime(), gpu_id, pid, size);
+ res = mGpuMemTotalMap.getNextKey(key);
+ if (!res.ok()) break;
+ key = res.value();
+ }
+}
+
+} // namespace android
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
new file mode 100644
index 0000000..de691e2
--- /dev/null
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <bpf/BpfMap.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <functional>
+
+namespace android {
+
+class GpuMem {
+public:
+ GpuMem() = default;
+ ~GpuMem();
+
+ // initialize eBPF program and map
+ void initialize();
+ // dumpsys interface
+ void dump(const Vector<String16>& args, std::string* result);
+ bool isInitialized() { return mInitialized.load(); }
+
+ // Traverse the gpu memory total map to feed the callback function.
+ void traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid,
+ uint64_t size)>& callback);
+
+private:
+ // Friend class for testing.
+ friend class TestableGpuMem;
+
+ // set gpu memory total map
+ void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map);
+
+ // indicate whether ebpf has been initialized
+ std::atomic<bool> mInitialized = false;
+ // bpf map for GPU memory total data
+ android::bpf::BpfMap<uint64_t, uint64_t> mGpuMemTotalMap;
+
+ // gpu memory tracepoint event category
+ static constexpr char kGpuMemTraceGroup[] = "gpu_mem";
+ // gpu memory total tracepoint
+ static constexpr char kGpuMemTotalTracepoint[] = "gpu_mem_total";
+ // pinned gpu memory total bpf c program path in bpf sysfs
+ static constexpr char kGpuMemTotalProgPath[] =
+ "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total";
+ // pinned gpu memory total bpf map path in bpf sysfs
+ static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
+ // 30 seconds timeout for trying to attach bpf program to tracepoint
+ static constexpr int kGpuWaitTimeout = 30;
+};
+
+} // namespace android
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 231d068..220952d 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -291,24 +291,27 @@
if (data) {
for (const auto& ele : mAppStats) {
- AStatsEvent* event = AStatsEventList_addStatsEvent(data);
- AStatsEvent_setAtomId(event, android::util::GPU_STATS_APP_INFO);
- AStatsEvent_writeString(event, ele.second.appPackageName.c_str());
- AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
+ std::string glDriverBytes = int64VectorToProtoByteString(
+ ele.second.glDriverLoadingTime);
+ std::string vkDriverBytes = int64VectorToProtoByteString(
+ ele.second.vkDriverLoadingTime);
+ std::string angleDriverBytes = int64VectorToProtoByteString(
+ ele.second.angleDriverLoadingTime);
- std::string bytes = int64VectorToProtoByteString(ele.second.glDriverLoadingTime);
- AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
-
- bytes = int64VectorToProtoByteString(ele.second.vkDriverLoadingTime);
- AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
-
- bytes = int64VectorToProtoByteString(ele.second.angleDriverLoadingTime);
- AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
-
- AStatsEvent_writeBool(event, ele.second.cpuVulkanInUse);
- AStatsEvent_writeBool(event, ele.second.falsePrerotation);
- AStatsEvent_writeBool(event, ele.second.gles1InUse);
- AStatsEvent_build(event);
+ android::util::addAStatsEvent(
+ data,
+ android::util::GPU_STATS_APP_INFO,
+ ele.second.appPackageName.c_str(),
+ ele.second.driverVersionCode,
+ android::util::BytesField(glDriverBytes.c_str(),
+ glDriverBytes.length()),
+ android::util::BytesField(vkDriverBytes.c_str(),
+ vkDriverBytes.length()),
+ android::util::BytesField(angleDriverBytes.c_str(),
+ angleDriverBytes.length()),
+ ele.second.cpuVulkanInUse,
+ ele.second.falsePrerotation,
+ ele.second.gles1InUse);
}
}
@@ -326,22 +329,22 @@
if (data) {
for (const auto& ele : mGlobalStats) {
- AStatsEvent* event = AStatsEventList_addStatsEvent(data);
- AStatsEvent_setAtomId(event, android::util::GPU_STATS_GLOBAL_INFO);
- AStatsEvent_writeString(event, ele.second.driverPackageName.c_str());
- AStatsEvent_writeString(event, ele.second.driverVersionName.c_str());
- AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
- AStatsEvent_writeInt64(event, ele.second.driverBuildTime);
- AStatsEvent_writeInt64(event, ele.second.glLoadingCount);
- AStatsEvent_writeInt64(event, ele.second.glLoadingFailureCount);
- AStatsEvent_writeInt64(event, ele.second.vkLoadingCount);
- AStatsEvent_writeInt64(event, ele.second.vkLoadingFailureCount);
- AStatsEvent_writeInt32(event, ele.second.vulkanVersion);
- AStatsEvent_writeInt32(event, ele.second.cpuVulkanVersion);
- AStatsEvent_writeInt32(event, ele.second.glesVersion);
- AStatsEvent_writeInt64(event, ele.second.angleLoadingCount);
- AStatsEvent_writeInt64(event, ele.second.angleLoadingFailureCount);
- AStatsEvent_build(event);
+ android::util::addAStatsEvent(
+ data,
+ android::util::GPU_STATS_GLOBAL_INFO,
+ ele.second.driverPackageName.c_str(),
+ ele.second.driverVersionName.c_str(),
+ ele.second.driverVersionCode,
+ ele.second.driverBuildTime,
+ ele.second.glLoadingCount,
+ ele.second.glLoadingFailureCount,
+ ele.second.vkLoadingCount,
+ ele.second.vkLoadingFailureCount,
+ ele.second.vulkanVersion,
+ ele.second.cpuVulkanVersion,
+ ele.second.glesVersion,
+ ele.second.angleLoadingCount,
+ ele.second.angleLoadingFailureCount);
}
}
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 538506d..db81f5d 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -19,11 +19,16 @@
address: true,
},
srcs: [
+ "GpuMemTest.cpp",
"GpuStatsTest.cpp",
],
shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
"libcutils",
"libgfxstats",
+ "libgpumem",
"libgraphicsenv",
"liblog",
"libstatslog",
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
new file mode 100644
index 0000000..45e8367
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 "gpuservice_unittest"
+
+#include <android-base/stringprintf.h>
+#include <bpf/BpfMap.h>
+#include <gmock/gmock.h>
+#include <gpumem/GpuMem.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include "TestableGpuMem.h"
+
+namespace android {
+namespace {
+
+using base::StringPrintf;
+using testing::HasSubstr;
+
+constexpr uint32_t TEST_MAP_SIZE = 10;
+constexpr uint64_t TEST_GLOBAL_KEY = 0;
+constexpr uint64_t TEST_GLOBAL_VAL = 123;
+constexpr uint64_t TEST_PROC_KEY_1 = 1;
+constexpr uint64_t TEST_PROC_VAL_1 = 234;
+constexpr uint64_t TEST_PROC_KEY_2 = 4294967298; // (1 << 32) + 2
+constexpr uint64_t TEST_PROC_VAL_2 = 345;
+constexpr uint32_t TEST_KEY_MASK = 0x1 | 0x2 | 0x4;
+constexpr uint32_t TEST_KEY_COUNT = 3;
+
+class GpuMemTest : public testing::Test {
+public:
+ GpuMemTest() {
+ 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());
+ }
+
+ ~GpuMemTest() {
+ 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 {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_EQ(0, bpf::setrlimitForTest());
+
+ mGpuMem = std::make_unique<GpuMem>();
+ mTestableGpuMem = TestableGpuMem(mGpuMem.get());
+ mTestableGpuMem.setInitialized();
+ errno = 0;
+ mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
+ BPF_F_NO_PREALLOC);
+
+ EXPECT_EQ(0, errno);
+ EXPECT_LE(0, mTestMap.getMap().get());
+ EXPECT_TRUE(mTestMap.isValid());
+ }
+
+ std::string dumpsys() {
+ std::string result;
+ Vector<String16> args;
+ mGpuMem->dump(args, &result);
+ return result;
+ }
+
+ std::unique_ptr<GpuMem> mGpuMem;
+ TestableGpuMem mTestableGpuMem;
+ bpf::BpfMap<uint64_t, uint64_t> mTestMap;
+};
+
+TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(),
+ "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map");
+}
+
+TEST_F(GpuMemTest, bpfInitializationFailed) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ EXPECT_EQ(dumpsys(), "Failed to initialize GPU memory eBPF\n");
+}
+
+TEST_F(GpuMemTest, gpuMemTotalMapEmpty) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_EQ(dumpsys(), "GPU memory total usage map is empty\n");
+}
+
+TEST_F(GpuMemTest, globalMemTotal) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(), HasSubstr(StringPrintf("Global total: %" PRIu64 "\n", TEST_GLOBAL_VAL)));
+}
+
+TEST_F(GpuMemTest, missingGlobalMemTotal) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(), HasSubstr("Global total: N/A"));
+}
+
+TEST_F(GpuMemTest, procMemTotal) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n",
+ (uint32_t)(TEST_PROC_KEY_1 >> 32))));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_1,
+ TEST_PROC_VAL_1)));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n",
+ (uint32_t)(TEST_PROC_KEY_2 >> 32))));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_2,
+ TEST_PROC_VAL_2)));
+}
+
+TEST_F(GpuMemTest, traverseGpuMemTotals) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ static uint32_t sMask = 0;
+ static uint32_t sCount = 0;
+ mGpuMem->traverseGpuMemTotals([](int64_t, uint32_t gpuId, uint32_t pid, uint64_t size) {
+ const uint64_t key = ((uint64_t)gpuId << 32) | pid;
+ switch (key) {
+ case TEST_GLOBAL_KEY:
+ EXPECT_EQ(size, TEST_GLOBAL_VAL);
+ sMask |= 0x1;
+ break;
+ case TEST_PROC_KEY_1:
+ EXPECT_EQ(size, TEST_PROC_VAL_1);
+ sMask |= 0x2;
+ break;
+ case TEST_PROC_KEY_2:
+ EXPECT_EQ(size, TEST_PROC_VAL_2);
+ sMask |= 0x4;
+ break;
+ }
+ sCount++;
+ });
+
+ EXPECT_EQ(sMask, TEST_KEY_MASK);
+ EXPECT_EQ(sCount, TEST_KEY_COUNT);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/TestableGpuMem.h b/services/gpuservice/tests/unittests/TestableGpuMem.h
new file mode 100644
index 0000000..6c8becb
--- /dev/null
+++ b/services/gpuservice/tests/unittests/TestableGpuMem.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <bpf/BpfMap.h>
+#include <gpumem/GpuMem.h>
+
+namespace android {
+
+class TestableGpuMem {
+public:
+ TestableGpuMem() = default;
+ explicit TestableGpuMem(GpuMem *gpuMem) : mGpuMem(gpuMem) {}
+
+ void setInitialized() { mGpuMem->mInitialized.store(true); }
+
+ void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+ mGpuMem->setGpuMemTotalMap(map);
+ }
+
+ std::string getGpuMemTraceGroup() { return mGpuMem->kGpuMemTraceGroup; }
+
+ std::string getGpuMemTotalTracepoint() { return mGpuMem->kGpuMemTotalTracepoint; }
+
+ std::string getGpuMemTotalProgPath() { return mGpuMem->kGpuMemTotalProgPath; }
+
+ std::string getGpuMemTotalMapPath() { return mGpuMem->kGpuMemTotalMapPath; }
+
+private:
+ GpuMem *mGpuMem;
+};
+
+} // namespace android
diff --git a/services/gpuservice/tracing/Android.bp b/services/gpuservice/tracing/Android.bp
new file mode 100644
index 0000000..919fed3
--- /dev/null
+++ b/services/gpuservice/tracing/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libgpumemtracer",
+ srcs: [
+ "GpuMemTracer.cpp",
+ ],
+ shared_libs: [
+ "libgpumem",
+ "libbase",
+ "liblog",
+ "libutils",
+ ],
+ static_libs: [
+ "libperfetto_client_experimental",
+ ],
+ export_include_dirs: ["include"],
+ export_static_lib_headers: [
+ "libperfetto_client_experimental",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
new file mode 100644
index 0000000..6366e1d
--- /dev/null
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "GpuMemTracer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "tracing/GpuMemTracer.h"
+
+#include <gpumem/GpuMem.h>
+#include <perfetto/trace/android/gpu_mem_event.pbzero.h>
+#include <unistd.h>
+
+#include <thread>
+
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::GpuMemTracer::GpuMemDataSource);
+
+namespace android {
+
+std::mutex GpuMemTracer::sTraceMutex;
+std::condition_variable GpuMemTracer::sCondition;
+bool GpuMemTracer::sTraceStarted;
+
+void GpuMemTracer::initialize(std::shared_ptr<GpuMem> gpuMem) {
+ if (!gpuMem->isInitialized()) {
+ ALOGE("Cannot initialize GpuMemTracer before GpuMem");
+ return;
+ }
+ mGpuMem = gpuMem;
+ perfetto::TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ perfetto::Tracing::Initialize(args);
+ registerDataSource();
+ std::thread tracerThread(&GpuMemTracer::threadLoop, this);
+ pthread_setname_np(tracerThread.native_handle(), "GpuMemTracerThread");
+ tracerThread.detach();
+}
+
+void GpuMemTracer::registerDataSource() {
+ perfetto::DataSourceDescriptor dsd;
+ dsd.set_name(kGpuMemDataSource);
+ GpuMemDataSource::Register(dsd);
+}
+
+void GpuMemTracer::threadLoop() {
+ while (true) {
+ {
+ std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ while (!sTraceStarted) {
+ sCondition.wait(lock);
+ }
+ }
+ traceInitialCounters();
+ {
+ std::lock_guard<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ sTraceStarted = false;
+ }
+ }
+}
+
+void GpuMemTracer::traceInitialCounters() {
+ if (!mGpuMem->isInitialized()) {
+ // This should never happen.
+ ALOGE("Cannot trace without GpuMem initialization");
+ return;
+ }
+ 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(ts);
+ auto* event = packet->set_gpu_mem_total_event();
+ event->set_gpu_id(gpuId);
+ event->set_pid(pid);
+ event->set_size(size);
+ });
+ });
+ // Flush the TraceContext. The last packet in the above loop will go
+ // missing without this flush.
+ GpuMemDataSource::Trace([](GpuMemDataSource::TraceContext ctx) { ctx.Flush(); });
+}
+
+} // namespace android
diff --git a/services/gpuservice/tracing/include/tracing/GpuMemTracer.h b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
new file mode 100644
index 0000000..40deb4c
--- /dev/null
+++ b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <perfetto/tracing.h>
+
+#include <mutex>
+
+namespace android {
+
+class GpuMem;
+
+class GpuMemTracer {
+public:
+ class GpuMemDataSource : public perfetto::DataSource<GpuMemDataSource> {
+ virtual void OnSetup(const SetupArgs&) override{};
+ virtual void OnStart(const StartArgs&) override {
+ std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ sTraceStarted = true;
+ sCondition.notify_all();
+ }
+ virtual void OnStop(const StopArgs&) override{};
+ };
+
+ ~GpuMemTracer() = default;
+
+ // Sets up the perfetto tracing backend and data source.
+ void initialize(std::shared_ptr<GpuMem>);
+ // Registers the data source with the perfetto backend. Called as part of initialize()
+ // and should not be called manually outside of tests. Public to allow for substituting a
+ // perfetto::kInProcessBackend in tests.
+ void registerDataSource();
+
+ static constexpr char kGpuMemDataSource[] = "android.gpu.memory";
+ static std::condition_variable sCondition;
+ static std::mutex sTraceMutex;
+ static bool sTraceStarted;
+
+private:
+ void traceInitialCounters();
+ void threadLoop();
+
+ std::shared_ptr<GpuMem> mGpuMem;
+};
+
+} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index f67c9d0..a0de607 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -21,7 +21,13 @@
"-Werror",
"-Wno-unused-parameter",
"-Wthread-safety",
+ "-Wshadow",
+ "-Wshadow-field-in-constructor-modified",
+ "-Wshadow-uncaptured-local",
],
+ sanitize: {
+ misc_undefined: ["bounds"],
+ },
}
/////////////////////////////////////////////////
@@ -99,6 +105,7 @@
"InputListener.cpp",
"InputReaderBase.cpp",
"InputThread.cpp",
+ "VibrationElement.cpp"
],
}
@@ -110,6 +117,7 @@
"libcutils",
"libinput",
"liblog",
+ "libui",
"libutils",
],
header_libs: [
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e68946d..e49667e 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -93,16 +93,15 @@
class BinderWindowHandle : public InputWindowHandle {
public:
- BinderWindowHandle(const InputWindowInfo& info) {
- mInfo = info;
- }
+ BinderWindowHandle(const InputWindowInfo& info) { mInfo = info; }
bool updateInfo() override {
return true;
}
};
-void InputManager::setInputWindows(const std::vector<InputWindowInfo>& infos,
+binder::Status InputManager::setInputWindows(
+ const std::vector<InputWindowInfo>& infos,
const sp<ISetInputWindowsListener>& setInputWindowsListener) {
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> handlesPerDisplay;
@@ -116,26 +115,40 @@
if (setInputWindowsListener) {
setInputWindowsListener->onSetInputWindowsFinished();
}
+ return binder::Status::ok();
}
// Used by tests only.
-void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
+binder::Status InputManager::registerInputChannel(const InputChannel& channel) {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid != AID_SHELL && uid != AID_ROOT) {
ALOGE("Invalid attempt to register input channel over IPC"
"from non shell/root entity (PID: %d)", ipc->getCallingPid());
- return;
+ return binder::Status::ok();
}
- mDispatcher->registerInputChannel(channel);
+
+ mDispatcher->registerInputChannel(channel.dup());
+ return binder::Status::ok();
}
-void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
+binder::Status InputManager::unregisterInputChannel(const InputChannel& channel) {
mDispatcher->unregisterInputChannel(channel);
+ return binder::Status::ok();
}
-void InputManager::setMotionClassifierEnabled(bool enabled) {
- mClassifier->setMotionClassifierEnabled(enabled);
+status_t InputManager::dump(int fd, const Vector<String16>& args) {
+ std::string dump;
+
+ dump += " InputFlinger dump\n";
+
+ ::write(fd, dump.c_str(), dump.size());
+ return NO_ERROR;
+}
+
+binder::Status InputManager::setFocusedWindow(const FocusRequest& request) {
+ mDispatcher->setFocusedWindow(request);
+ return binder::Status::ok();
}
} // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 0158441..bf86a98 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -26,15 +26,19 @@
#include <InputDispatcherInterface.h>
#include <InputDispatcherPolicyInterface.h>
-#include <input/ISetInputWindowsListener.h>
+#include <android/os/ISetInputWindowsListener.h>
#include <input/Input.h>
#include <input/InputTransport.h>
-#include <input/IInputFlinger.h>
+#include <android/os/BnInputFlinger.h>
+#include <android/os/IInputFlinger.h>
#include <utils/Errors.h>
-#include <utils/Vector.h>
-#include <utils/Timers.h>
#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+using android::os::BnInputFlinger;
+using android::os::ISetInputWindowsListener;
namespace android {
class InputChannel;
@@ -43,17 +47,19 @@
/*
* The input manager is the core of the system event processing.
*
- * The input manager has two components.
+ * The input manager has three components.
*
* 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
- * policy, and posts messages to a queue managed by the InputDispatcherThread.
- * 2. The InputDispatcher class starts a thread that waits for new events on the
- * queue and asynchronously dispatches them to applications.
+ * policy, and posts messages to a queue managed by the InputClassifier.
+ * 2. The InputClassifier class starts a thread to communicate with the device-specific
+ * classifiers. It then waits on the queue of events from InputReader, applies a classification
+ * to them, and queues them for the InputDispatcher.
+ * 3. The InputDispatcher class starts a thread that waits for new events on the
+ * previous queue and asynchronously dispatches them to applications.
*
- * By design, the InputReader class and InputDispatcher class do not share any
- * internal state. Moreover, all communication is done one way from the InputReader
- * into the InputDispatcherThread and never the reverse. Both classes may interact with the
- * InputDispatchPolicy, however.
+ * By design, none of these classes share any internal state. Moreover, all communication is
+ * done one way from the InputReader to the InputDispatcher and never the reverse. All
+ * classes may interact with the InputDispatchPolicy, however.
*
* The InputManager class never makes any calls into Java itself. Instead, the
* InputDispatchPolicy is responsible for performing all external interactions with the
@@ -74,33 +80,37 @@
/* Gets the input reader. */
virtual sp<InputReaderInterface> getReader() = 0;
+ /* Gets the input classifier */
+ virtual sp<InputClassifierInterface> getClassifier() = 0;
+
/* Gets the input dispatcher. */
virtual sp<InputDispatcherInterface> getDispatcher() = 0;
};
class InputManager : public InputManagerInterface, public BnInputFlinger {
protected:
- virtual ~InputManager();
+ ~InputManager() override;
public:
InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
- virtual status_t start();
- virtual status_t stop();
+ status_t start() override;
+ status_t stop() override;
- virtual sp<InputReaderInterface> getReader();
- virtual sp<InputClassifierInterface> getClassifier();
- virtual sp<InputDispatcherInterface> getDispatcher();
+ sp<InputReaderInterface> getReader() override;
+ sp<InputClassifierInterface> getClassifier() override;
+ sp<InputDispatcherInterface> getDispatcher() override;
- virtual void setInputWindows(const std::vector<InputWindowInfo>& handles,
- const sp<ISetInputWindowsListener>& setInputWindowsListener);
+ status_t dump(int fd, const Vector<String16>& args) override;
+ binder::Status setInputWindows(
+ const std::vector<InputWindowInfo>& handles,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
- virtual void registerInputChannel(const sp<InputChannel>& channel);
- virtual void unregisterInputChannel(const sp<InputChannel>& channel);
-
- void setMotionClassifierEnabled(bool enabled);
+ binder::Status registerInputChannel(const InputChannel& channel) override;
+ binder::Status unregisterInputChannel(const InputChannel& channel) override;
+ binder::Status setFocusedWindow(const FocusRequest&) override;
private:
sp<InputReaderInterface> mReader;
diff --git a/services/inputflinger/VibrationElement.cpp b/services/inputflinger/VibrationElement.cpp
new file mode 100644
index 0000000..aaf5834
--- /dev/null
+++ b/services/inputflinger/VibrationElement.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "VibrationElement.h"
+
+#include <android-base/stringprintf.h>
+
+#include <algorithm>
+#include <cinttypes>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+const std::string VibrationElement::toString() const {
+ std::string dump;
+ dump += StringPrintf("[duration=%lldms, channels=[", duration.count());
+
+ for (auto it = channels.begin(); it != channels.end(); ++it) {
+ dump += std::to_string(*it);
+ if (std::next(it) != channels.end()) {
+ dump += ", ";
+ }
+ }
+
+ dump += "]]";
+ return dump;
+}
+
+uint16_t VibrationElement::getMagnitude(size_t channelIdx) const {
+ if (channelIdx >= channels.size()) {
+ return 0;
+ }
+ // convert range [0,255] to [0,65535] (android framework to linux ff ranges)
+ return static_cast<uint16_t>(channels[channelIdx]) << 8;
+}
+
+bool VibrationElement::isOn() const {
+ return std::any_of(channels.begin(), channels.end(),
+ [](uint16_t channel) { return channel != 0; });
+}
+
+} // namespace android
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 5a14133..f99fffe 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -45,49 +45,45 @@
virtual ~FakeInputDispatcherPolicy() {}
private:
- virtual void notifyConfigurationChanged(nsecs_t) override {}
+ void notifyConfigurationChanged(nsecs_t) override {}
- virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&,
- const std::string& name) override {
+ std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>&,
+ const sp<IBinder>&, const std::string& name) override {
ALOGE("The window is not responding : %s", name.c_str());
- return 0;
+ return 0s;
}
- virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+ void notifyInputChannelBroken(const sp<IBinder>&) override {}
- virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+ void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+ void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
*outConfig = mConfig;
}
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
return true;
}
- virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+ void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
- virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+ void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
- uint32_t) override {
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
return 0;
}
- virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
- KeyEvent*) override {
+ bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
return false;
}
- virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
+ void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
- virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t) override {}
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
- return false;
- }
+ bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+ void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
InputDispatcherConfiguration mConfig;
};
@@ -98,7 +94,8 @@
virtual ~FakeApplicationHandle() {}
virtual bool updateInfo() {
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+ mInfo.dispatchingTimeoutMillis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
return true;
}
};
@@ -106,7 +103,7 @@
class FakeInputReceiver {
public:
void consumeEvent() {
- uint32_t consumeSeq;
+ uint32_t consumeSeq = 0;
InputEvent* event;
std::chrono::time_point start = std::chrono::steady_clock::now();
@@ -132,14 +129,17 @@
protected:
explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
: mDispatcher(dispatcher) {
- InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+ InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+ mServerChannel = std::move(serverChannel);
+ mClientChannel = std::move(clientChannel);
mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
virtual ~FakeInputReceiver() {}
sp<InputDispatcher> mDispatcher;
- sp<InputChannel> mServerChannel, mClientChannel;
+ std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
};
@@ -149,7 +149,7 @@
static const int32_t WIDTH = 200;
static const int32_t HEIGHT = 200;
- FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+ FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
const sp<InputDispatcher>& dispatcher, const std::string name)
: FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
mDispatcher->registerInputChannel(mServerChannel);
@@ -161,9 +161,8 @@
virtual bool updateInfo() override {
mInfo.token = mServerChannel->getConnectionToken();
mInfo.name = "FakeWindowHandle";
- mInfo.layoutParamsFlags = 0;
- mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+ mInfo.type = InputWindowInfo::Type::APPLICATION;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
mInfo.frameLeft = mFrame.left;
mInfo.frameTop = mFrame.top;
mInfo.frameRight = mFrame.right;
@@ -178,7 +177,6 @@
mInfo.paused = false;
mInfo.ownerPid = INJECTOR_PID;
mInfo.ownerUid = INJECTOR_UID;
- mInfo.inputFeatures = 0;
mInfo.displayId = ADISPLAY_ID_DEFAULT;
return true;
@@ -202,13 +200,13 @@
const nsecs_t currentTime = now();
+ ui::Transform identityTransform;
MotionEvent event;
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
/* actionButton */ 0, /* flags */ 0,
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- 1 /* xScale */, 1 /* yScale */,
- /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+ identityTransform, /* xPrecision */ 0,
/* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
@@ -249,7 +247,7 @@
dispatcher->start();
// Create a window that will receive motion events
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -285,7 +283,7 @@
dispatcher->start();
// Create a window that will receive motion events
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 390c6b8..d29d8df 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -68,4 +68,5 @@
export_header_lib_headers: [
"libinputdispatcher_headers",
],
+ logtags: ["EventLogTags.logtags"],
}
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index f5ea563..cee9c39 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -20,7 +20,7 @@
namespace android::inputdispatcher {
-Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor,
+Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
const IdGenerator& idGenerator)
: status(STATUS_NORMAL),
inputChannel(inputChannel),
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index 3b33f29..c4262ad 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -42,7 +42,7 @@
};
Status status;
- sp<InputChannel> inputChannel; // never null
+ std::shared_ptr<InputChannel> inputChannel; // never null
bool monitor;
InputPublisher inputPublisher;
InputState inputState;
@@ -59,7 +59,8 @@
// yet received a "finished" response from the application.
std::deque<DispatchEntry*> waitQueue;
- Connection(const sp<InputChannel>& inputChannel, bool monitor, const IdGenerator& idGenerator);
+ Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
+ const IdGenerator& idGenerator);
inline const std::string getInputChannelName() const { return inputChannel->getName(); }
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index fdbb1d1..4328e03 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -117,10 +117,12 @@
// --- FocusEntry ---
// Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
-FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus)
+FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
+ std::string_view reason)
: EventEntry(id, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER),
connectionToken(connectionToken),
- hasFocus(hasFocus) {}
+ hasFocus(hasFocus),
+ reason(reason) {}
FocusEntry::~FocusEntry() {}
@@ -183,7 +185,6 @@
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords, float xOffset, float yOffset)
: EventEntry(id, Type::MOTION, eventTime, policyFlags),
- eventTime(eventTime),
deviceId(deviceId),
source(source),
displayId(displayId),
@@ -240,17 +241,13 @@
volatile int32_t DispatchEntry::sNextSeqAtomic;
-DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset,
- float yOffset, float globalScaleFactor, float windowXScale,
- float windowYScale)
+DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, ui::Transform transform,
+ float globalScaleFactor)
: seq(nextSeq()),
eventEntry(eventEntry),
targetFlags(targetFlags),
- xOffset(xOffset),
- yOffset(yOffset),
+ transform(transform),
globalScaleFactor(globalScaleFactor),
- windowXScale(windowXScale),
- windowYScale(windowYScale),
deliveryTime(0),
resolvedAction(0),
resolvedFlags(0) {
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 6b7697d..d5b589e 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -112,8 +112,10 @@
struct FocusEntry : EventEntry {
sp<IBinder> connectionToken;
bool hasFocus;
+ std::string_view reason;
- FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus);
+ FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
+ std::string_view reason);
virtual void appendDescription(std::string& msg) const;
protected:
@@ -154,7 +156,6 @@
};
struct MotionEntry : EventEntry {
- nsecs_t eventTime;
int32_t deviceId;
uint32_t source;
int32_t displayId;
@@ -193,11 +194,8 @@
EventEntry* eventEntry; // the event to dispatch
int32_t targetFlags;
- float xOffset;
- float yOffset;
+ ui::Transform transform;
float globalScaleFactor;
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
// Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
// and will be undefined before that.
nsecs_t deliveryTime; // time when the event was actually delivered
@@ -209,8 +207,8 @@
int32_t resolvedAction;
int32_t resolvedFlags;
- DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset,
- float globalScaleFactor, float windowXScale, float windowYScale);
+ DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, ui::Transform transform,
+ float globalScaleFactor);
~DispatchEntry();
inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
@@ -257,12 +255,12 @@
sp<Connection> connection;
nsecs_t eventTime;
KeyEntry* keyEntry;
- sp<InputApplicationHandle> inputApplicationHandle;
+ std::shared_ptr<InputApplicationHandle> inputApplicationHandle;
std::string reason;
int32_t userActivityEventType;
uint32_t seq;
bool handled;
- sp<InputChannel> inputChannel;
+ std::shared_ptr<InputChannel> inputChannel;
sp<IBinder> oldToken;
sp<IBinder> newToken;
};
diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags
new file mode 100644
index 0000000..2836467
--- /dev/null
+++ b/services/inputflinger/dispatcher/EventLogTags.logtags
@@ -0,0 +1,42 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace. Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+# (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# See system/core/logcat/event.logtags for the master copy of the tags.
+
+# 62000 - 62199 reserved for inputflinger
+
+62000 input_interaction (windows|4)
+62001 input_focus (window|3),(reason|3)
+
+# NOTE - the range 1000000-2000000 is reserved for partners and others who
+# want to define their own log tags without conflicting with the core platform.
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index fe016af..5d71666 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -45,28 +45,31 @@
#include "InputDispatcher.h"
-#include "Connection.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <statslog.h>
-#include <stddef.h>
-#include <time.h>
-#include <unistd.h>
-#include <queue>
-#include <sstream>
-
#include <android-base/chrono_utils.h>
#include <android-base/stringprintf.h>
+#include <android/os/IInputConstants.h>
#include <binder/Binder.h>
#include <input/InputDevice.h>
+#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>
#include <utils/Trace.h>
+#include <cerrno>
+#include <cinttypes>
+#include <climits>
+#include <cstddef>
+#include <ctime>
+#include <queue>
+#include <sstream>
+
+#include "Connection.h"
+
#define INDENT " "
#define INDENT2 " "
#define INDENT3 " "
@@ -78,7 +81,8 @@
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
-constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s;
+constexpr std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT =
+ std::chrono::milliseconds(android::os::IInputConstants::DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
// Amount of time to allow for all pending events to be processed when an app switch
// key is on the way. This is used to preempt input dispatch and drop input events
@@ -103,6 +107,10 @@
// Number of recent events to keep for debugging purposes.
constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
+// Event log tags. See EventLogTags.logtags for reference
+constexpr int LOGTAG_INPUT_INTERACTION = 62000;
+constexpr int LOGTAG_INPUT_FOCUS = 62001;
+
static inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
@@ -159,6 +167,10 @@
}
}
+static int64_t millis(std::chrono::nanoseconds t) {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
+}
+
static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
const PointerProperties* pointerProperties) {
if (!isValidMotionAction(action, actionButton, pointerCount)) {
@@ -261,12 +273,11 @@
static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
EventEntry* eventEntry,
int32_t inputTargetFlags) {
- if (inputTarget.useDefaultPointerInfo()) {
- const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
+ if (inputTarget.useDefaultPointerTransform()) {
+ const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, // increments ref
- inputTargetFlags, pointerInfo.xOffset,
- pointerInfo.yOffset, inputTarget.globalScaleFactor,
- pointerInfo.windowXScale, pointerInfo.windowYScale);
+ inputTargetFlags, transform,
+ inputTarget.globalScaleFactor);
}
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
@@ -276,28 +287,24 @@
// Use the first pointer information to normalize all other pointers. This could be any pointer
// as long as all other pointers are normalized to the same value and the final DispatchEntry
- // uses the offset and scale for the normalized pointer.
- const PointerInfo& firstPointerInfo =
- inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
+ // uses the transform for the normalized pointer.
+ const ui::Transform& firstPointerTransform =
+ inputTarget.pointerTransforms[inputTarget.pointerIds.firstMarkedBit()];
+ ui::Transform inverseFirstTransform = firstPointerTransform.inverse();
// Iterate through all pointers in the event to normalize against the first.
for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
- const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
-
- // The scale factor is the ratio of the current pointers scale to the normalized scale.
- float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
- float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
+ const ui::Transform& currTransform = inputTarget.pointerTransforms[pointerId];
pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
- // First apply the current pointers offset to set the window at 0,0
- pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
- // Next scale the coordinates.
- pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
- // Lastly, offset the coordinates so they're in the normalized pointer's frame.
- pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
- -firstPointerInfo.yOffset);
+ // First, apply the current pointer's transform to update the coordinates into
+ // window space.
+ pointerCoords[pointerIndex].transform(currTransform);
+ // Next, apply the inverse transform of the normalized coordinates so the
+ // current coordinates are transformed into the normalized coordinate space.
+ pointerCoords[pointerIndex].transform(inverseFirstTransform);
}
MotionEntry* combinedMotionEntry =
@@ -319,10 +326,8 @@
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
- inputTargetFlags, firstPointerInfo.xOffset,
- firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
- firstPointerInfo.windowXScale,
- firstPointerInfo.windowYScale);
+ inputTargetFlags, firstPointerTransform,
+ inputTarget.globalScaleFactor);
combinedMotionEntry->release();
return dispatchEntry;
}
@@ -423,7 +428,7 @@
while (!mConnectionsByFd.empty()) {
sp<Connection> connection = mConnectionsByFd.begin()->second;
- unregisterInputChannel(connection->inputChannel);
+ unregisterInputChannel(*connection->inputChannel);
}
}
@@ -493,7 +498,7 @@
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
if (currentTime >= *mNoFocusedWindowTimeoutTime) {
onAnrLocked(mAwaitedFocusedApplication);
- mAwaitedFocusedApplication.clear();
+ mAwaitedFocusedApplication.reset();
return LONG_LONG_MIN;
} else {
// Keep waiting
@@ -522,12 +527,12 @@
return LONG_LONG_MIN;
}
-nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) {
+std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) {
sp<InputWindowHandle> window = getWindowHandleLocked(token);
if (window != nullptr) {
- return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count();
+ return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
}
- return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count();
+ return DEFAULT_INPUT_DISPATCHING_TIMEOUT;
}
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
@@ -812,13 +817,12 @@
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
if (windowInfo->displayId == displayId) {
- int32_t flags = windowInfo->layoutParamsFlags;
+ auto flags = windowInfo->flags;
if (windowInfo->visible) {
- if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
- bool isTouchModal = (flags &
- (InputWindowInfo::FLAG_NOT_FOCUSABLE |
- InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+ if (!flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) {
+ bool isTouchModal = !flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE) &&
+ !flags.test(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
int32_t portalToDisplayId = windowInfo->portalToDisplayId;
if (portalToDisplayId != ADISPLAY_ID_NONE &&
@@ -835,7 +839,7 @@
}
}
- if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
+ if (addOutsideTargets && flags.test(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
touchState->addOrUpdateWindow(windowHandle,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
BitSet32(0));
@@ -1074,7 +1078,8 @@
return true;
}
-void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) {
+void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus,
+ std::string_view reason) {
if (mPendingEvent != nullptr) {
// Move the pending event to the front of the queue. This will give the chance
// for the pending event to get dispatched to the newly focused window
@@ -1083,7 +1088,7 @@
}
FocusEntry* focusEntry =
- new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus);
+ new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), 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
@@ -1096,7 +1101,7 @@
}
void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) {
- sp<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+ std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
if (channel == nullptr) {
return; // Window has gone away
}
@@ -1104,7 +1109,10 @@
target.inputChannel = channel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
entry->dispatchInProgress = true;
-
+ std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") +
+ channel->getName();
+ std::string reason = std::string("reason=").append(entry->reason);
+ android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS;
dispatchEventLocked(currentTime, entry, {target});
}
@@ -1342,6 +1350,8 @@
ALOGD("dispatchEventToCurrentInputTargets");
#endif
+ updateInteractionTokensLocked(*eventEntry, inputTargets);
+
ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
pokeUserActivityLocked(*eventEntry);
@@ -1383,7 +1393,7 @@
// Reset input target wait timeout.
mNoFocusedWindowTimeoutTime = std::nullopt;
- mAwaitedFocusedApplication.clear();
+ mAwaitedFocusedApplication.reset();
}
/**
@@ -1427,7 +1437,9 @@
ALOGD("Waiting to send key to %s because there are unprocessed events that may cause "
"focus to change",
focusedWindowName);
- mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count();
+ mKeyIsWaitingForEventsTimeout = currentTime +
+ std::chrono::duration_cast<std::chrono::nanoseconds>(KEY_WAITING_FOR_EVENTS_TIMEOUT)
+ .count();
return true;
}
@@ -1453,7 +1465,7 @@
int32_t displayId = getTargetDisplayId(entry);
sp<InputWindowHandle> focusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
- sp<InputApplicationHandle> focusedApplicationHandle =
+ std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
// If there is no currently focused window and no focused application
@@ -1473,13 +1485,13 @@
if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
if (!mNoFocusedWindowTimeoutTime.has_value()) {
// We just discovered that there's no focused window. Start the ANR timer
- const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
- mNoFocusedWindowTimeoutTime = currentTime + timeout;
+ std::chrono::nanoseconds timeout = focusedApplicationHandle->getDispatchingTimeout(
+ DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+ mNoFocusedWindowTimeoutTime = currentTime + timeout.count();
mAwaitedFocusedApplication = focusedApplicationHandle;
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
- mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout));
+ mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
*nextWakeupTime = *mNoFocusedWindowTimeoutTime;
return INPUT_EVENT_INJECTION_PENDING;
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
@@ -1580,7 +1592,8 @@
// Update the touch state as needed based on the properties of the touch event.
int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
- sp<InputWindowHandle> newHoverWindowHandle;
+ sp<InputWindowHandle> newHoverWindowHandle(mLastHoverWindowHandle);
+ sp<InputWindowHandle> newTouchedWindowHandle;
// Copy current touch state into tempTouchState.
// This state will be used to update mTouchStatesByDisplay at the end of this function.
@@ -1649,7 +1662,7 @@
y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
}
bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- sp<InputWindowHandle> newTouchedWindowHandle =
+ newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
@@ -1680,15 +1693,11 @@
newTouchedWindowHandle = nullptr;
}
+ // Ensure the window has a connection and the connection is responsive
if (newTouchedWindowHandle != nullptr) {
- sp<Connection> connection = getConnectionLocked(newTouchedWindowHandle->getToken());
- if (connection == nullptr) {
- ALOGI("Could not find connection for %s",
- newTouchedWindowHandle->getName().c_str());
- newTouchedWindowHandle = nullptr;
- } else if (!connection->responsive) {
- // don't send the new touch to an unresponsive window
- ALOGW("Unresponsive window %s will not get the new gesture at %" PRIu64,
+ const bool isResponsive = hasResponsiveConnectionLocked(*newTouchedWindowHandle);
+ if (!isResponsive) {
+ ALOGW("%s will not receive the new gesture at %" PRIu64,
newTouchedWindowHandle->getName().c_str(), entry.eventTime);
newTouchedWindowHandle = nullptr;
}
@@ -1718,10 +1727,10 @@
}
// Update hover state.
- if (isHoverAction) {
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ newHoverWindowHandle = nullptr;
+ } else if (isHoverAction) {
newHoverWindowHandle = newTouchedWindowHandle;
- } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
- newHoverWindowHandle = mLastHoverWindowHandle;
}
// Update the temporary touch state.
@@ -1756,8 +1765,7 @@
sp<InputWindowHandle> oldTouchedWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
- sp<InputWindowHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
+ newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
if (oldTouchedWindowHandle != newTouchedWindowHandle &&
oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
@@ -1794,8 +1802,11 @@
}
if (newHoverWindowHandle != mLastHoverWindowHandle) {
- // Let the previous window know that the hover sequence is over.
- if (mLastHoverWindowHandle != nullptr) {
+ // Let the previous window know that the hover sequence is over, unless we already did it
+ // when dispatching it as is to newTouchedWindowHandle.
+ if (mLastHoverWindowHandle != nullptr &&
+ (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
+ mLastHoverWindowHandle != newTouchedWindowHandle)) {
#if DEBUG_HOVER
ALOGD("Sending hover exit event to window %s.",
mLastHoverWindowHandle->getName().c_str());
@@ -1804,8 +1815,11 @@
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
}
- // Let the new window know that the hover sequence is starting.
- if (newHoverWindowHandle != nullptr) {
+ // Let the new window know that the hover sequence is starting, unless we already did it
+ // when dispatching it as is to newTouchedWindowHandle.
+ if (newHoverWindowHandle != nullptr &&
+ (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ newHoverWindowHandle != newTouchedWindowHandle)) {
#if DEBUG_HOVER
ALOGD("Sending hover enter event to window %s.",
newHoverWindowHandle->getName().c_str());
@@ -1878,7 +1892,7 @@
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
const InputWindowInfo* info = windowHandle->getInfo();
if (info->displayId == displayId &&
- windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) {
+ windowHandle->getInfo()->type == InputWindowInfo::Type::WALLPAPER) {
tempTouchState
.addOrUpdateWindow(windowHandle,
InputTarget::FLAG_WINDOW_IS_OBSCURED |
@@ -2010,7 +2024,8 @@
if (it == inputTargets.end()) {
InputTarget inputTarget;
- sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+ std::shared_ptr<InputChannel> inputChannel =
+ getInputChannelLocked(windowHandle->getToken());
if (inputChannel == nullptr) {
ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
return;
@@ -2025,8 +2040,7 @@
ALOG_ASSERT(it->flags == targetFlags);
ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
- it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ it->addPointers(pointerIds, windowInfo->transform);
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -2049,7 +2063,9 @@
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
+ ui::Transform t;
+ t.set(xOffset, yOffset);
+ target.setDefaultPointerTransform(t);
inputTargets.push_back(target);
}
@@ -2092,7 +2108,7 @@
// If ownerPid is the same we don't generate occlusion events as there
// is no in-process security boundary.
return false;
- } else if (otherInfo->isTrustedOverlay()) {
+ } else if (otherInfo->trustedOverlay) {
return false;
} else if (otherInfo->displayId != info->displayId) {
return false;
@@ -2109,7 +2125,7 @@
break; // All future windows are below us. Exit early.
}
const InputWindowInfo* otherInfo = otherHandle->getInfo();
- if (canBeObscuredBy(windowHandle, otherHandle) &&
+ if (canBeObscuredBy(windowHandle, otherHandle) &&
otherInfo->frameContainsPoint(x, y)) {
return true;
}
@@ -2125,7 +2141,6 @@
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
}
-
const InputWindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
otherInfo->overlaps(windowInfo)) {
@@ -2136,7 +2151,7 @@
}
std::string InputDispatcher::getApplicationWindowLabel(
- const sp<InputApplicationHandle>& applicationHandle,
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle) {
if (applicationHandle != nullptr) {
if (windowHandle != nullptr) {
@@ -2161,7 +2176,7 @@
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
if (focusedWindowHandle != nullptr) {
const InputWindowInfo* info = focusedWindowHandle->getInfo();
- if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) {
+ if (info->inputFeatures.test(InputWindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
#endif
@@ -2425,6 +2440,75 @@
traceOutboundQueueLength(connection);
}
+/**
+ * This function is purely for debugging. It helps us understand where the user interaction
+ * was taking place. For example, if user is touching launcher, we will see a log that user
+ * started interacting with launcher. In that example, the event would go to the wallpaper as well.
+ * We will see both launcher and wallpaper in that list.
+ * Once the interaction with a particular set of connections starts, no new logs will be printed
+ * until the set of interacted connections changes.
+ *
+ * The following items are skipped, to reduce the logspam:
+ * ACTION_OUTSIDE: any windows that are receiving ACTION_OUTSIDE are not logged
+ * ACTION_UP: any windows that receive ACTION_UP are not logged (for both keys and motions).
+ * This includes situations like the soft BACK button key. When the user releases (lifts up the
+ * finger) the back button, then navigation bar will inject KEYCODE_BACK with ACTION_UP.
+ * Both of those ACTION_UP events would not be logged
+ * Monitors (both gesture and global): any gesture monitors or global monitors receiving events
+ * will not be logged. This is omitted to reduce the amount of data printed.
+ * If you see <none>, it's likely that one of the gesture monitors pilfered the event, and therefore
+ * gesture monitor is the only connection receiving the remainder of the gesture.
+ */
+void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry,
+ const std::vector<InputTarget>& targets) {
+ // Skip ACTION_UP events, and all events other than keys and motions
+ if (entry.type == EventEntry::Type::KEY) {
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+ if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
+ return;
+ }
+ } else if (entry.type == EventEntry::Type::MOTION) {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
+ if (motionEntry.action == AMOTION_EVENT_ACTION_UP ||
+ motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
+ return;
+ }
+ } else {
+ return; // Not a key or a motion
+ }
+
+ std::unordered_set<sp<IBinder>, IBinderHash> newConnectionTokens;
+ std::vector<sp<Connection>> newConnections;
+ for (const InputTarget& target : targets) {
+ if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) ==
+ InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ continue; // Skip windows that receive ACTION_OUTSIDE
+ }
+
+ sp<IBinder> token = target.inputChannel->getConnectionToken();
+ sp<Connection> connection = getConnectionLocked(token);
+ if (connection == nullptr || connection->monitor) {
+ continue; // We only need to keep track of the non-monitor connections.
+ }
+ newConnectionTokens.insert(std::move(token));
+ newConnections.emplace_back(connection);
+ }
+ if (newConnectionTokens == mInteractionConnectionTokens) {
+ return; // no change
+ }
+ mInteractionConnectionTokens = newConnectionTokens;
+
+ std::string windowList;
+ for (const sp<Connection>& connection : newConnections) {
+ windowList += connection->getWindowName() + ", ";
+ }
+ std::string message = "Interaction with windows: " + windowList;
+ if (windowList.empty()) {
+ message += "<none>";
+ }
+ android_log_event_list(LOGTAG_INPUT_INTERACTION) << message << LOG_ID_EVENTS;
+}
+
void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
const sp<IBinder>& newToken) {
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
@@ -2467,9 +2551,9 @@
while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.front();
dispatchEntry->deliveryTime = currentTime;
- const nsecs_t timeout =
+ const std::chrono::nanoseconds timeout =
getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
- dispatchEntry->timeoutTime = currentTime + timeout;
+ dispatchEntry->timeoutTime = currentTime + timeout.count();
// Publish the event.
status_t status;
@@ -2500,15 +2584,9 @@
const PointerCoords* usingCoords = motionEntry->pointerCoords;
// Set the X and Y offset and X and Y scale depending on the input source.
- float xOffset = 0.0f, yOffset = 0.0f;
- float xScale = 1.0f, yScale = 1.0f;
if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
float globalScaleFactor = dispatchEntry->globalScaleFactor;
- xScale = dispatchEntry->windowXScale;
- yScale = dispatchEntry->windowYScale;
- xOffset = dispatchEntry->xOffset * xScale;
- yOffset = dispatchEntry->yOffset * yScale;
if (globalScaleFactor != 1.0f) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
scaledCoords[i] = motionEntry->pointerCoords[i];
@@ -2543,8 +2621,9 @@
dispatchEntry->resolvedFlags,
motionEntry->edgeFlags, motionEntry->metaState,
motionEntry->buttonState,
- motionEntry->classification, xScale, yScale,
- xOffset, yOffset, motionEntry->xPrecision,
+ motionEntry->classification,
+ dispatchEntry->transform,
+ motionEntry->xPrecision,
motionEntry->yPrecision,
motionEntry->xCursorPosition,
motionEntry->yCursorPosition,
@@ -2757,7 +2836,7 @@
}
// Unregister the channel.
- d->unregisterInputChannelLocked(connection->inputChannel, notify);
+ d->unregisterInputChannelLocked(*connection->inputChannel, notify);
return 0; // remove the callback
} // release lock
}
@@ -2787,7 +2866,7 @@
}
void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
- const sp<InputChannel>& channel, const CancelationOptions& options) {
+ const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) {
sp<Connection> connection = getConnectionLocked(channel->getConnectionToken());
if (connection == nullptr) {
return;
@@ -2822,8 +2901,7 @@
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ target.setDefaultPointerTransform(windowInfo->transform);
target.globalScaleFactor = windowInfo->globalScaleFactor;
}
target.inputChannel = connection->inputChannel;
@@ -2888,8 +2966,7 @@
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ target.setDefaultPointerTransform(windowInfo->transform);
target.globalScaleFactor = windowInfo->globalScaleFactor;
}
target.inputChannel = connection->inputChannel;
@@ -3200,13 +3277,13 @@
mLock.unlock();
MotionEvent event;
+ ui::Transform transform;
event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
args->action, args->actionButton, args->flags, args->edgeFlags,
- args->metaState, args->buttonState, args->classification, 1 /*xScale*/,
- 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision,
- args->yPrecision, args->xCursorPosition, args->yCursorPosition,
- args->downTime, args->eventTime, args->pointerCount,
- args->pointerProperties, args->pointerCoords);
+ args->metaState, args->buttonState, args->classification, transform,
+ args->xPrecision, args->yPrecision, args->xCursorPosition,
+ args->yCursorPosition, args->downTime, args->eventTime,
+ args->pointerCount, args->pointerProperties, args->pointerCoords);
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
@@ -3603,7 +3680,31 @@
return false;
}
-sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token) const {
+bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const {
+ sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
+ const bool noInputChannel =
+ windowHandle.getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ if (connection != nullptr && noInputChannel) {
+ ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s",
+ windowHandle.getName().c_str(), connection->inputChannel->getName().c_str());
+ return false;
+ }
+
+ if (connection == nullptr) {
+ if (!noInputChannel) {
+ ALOGI("Could not find connection for %s", windowHandle.getName().c_str());
+ }
+ return false;
+ }
+ if (!connection->responsive) {
+ ALOGW("Window %s is not responsive", windowHandle.getName().c_str());
+ return false;
+ }
+ return true;
+}
+
+std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked(
+ const sp<IBinder>& token) const {
size_t count = mInputChannelsByToken.count(token);
if (count == 0) {
return nullptr;
@@ -3638,10 +3739,9 @@
if ((getInputChannelLocked(handle->getToken()) == nullptr &&
info->portalToDisplayId == ADISPLAY_ID_NONE)) {
const bool noInputChannel =
- info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
- const bool canReceiveInput =
- !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) ||
- !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE);
+ info->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ const bool canReceiveInput = !info->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE) ||
+ !info->flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE);
if (canReceiveInput && !noInputChannel) {
ALOGV("Window handle %s has no registered input channel",
handle->getName().c_str());
@@ -3698,6 +3798,17 @@
ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
}
+ // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
+ for (const sp<InputWindowHandle>& window : inputWindowHandles) {
+ const bool noInputWindow =
+ window->getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ if (noInputWindow && window->getToken() != nullptr) {
+ ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing",
+ window->getName().c_str());
+ window->releaseChannel();
+ }
+ }
+
// Copy old handles for release if they are no longer present.
const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
@@ -3724,33 +3835,8 @@
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
- if (oldFocusedWindowHandle != nullptr) {
- if (DEBUG_FOCUS) {
- ALOGD("Focus left window: %s in display %" PRId32,
- oldFocusedWindowHandle->getName().c_str(), displayId);
- }
- sp<InputChannel> focusedInputChannel =
- getInputChannelLocked(oldFocusedWindowHandle->getToken());
- if (focusedInputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
- "focus left window");
- synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
- enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
- }
- mFocusedWindowHandlesByDisplay.erase(displayId);
- }
- if (newFocusedWindowHandle != nullptr) {
- if (DEBUG_FOCUS) {
- ALOGD("Focus entered window: %s in display %" PRId32,
- newFocusedWindowHandle->getName().c_str(), displayId);
- }
- mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
- enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
- }
-
- if (mFocusedDisplayId == displayId) {
- onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
- }
+ onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle, displayId,
+ "setInputWindowsLocked");
}
std::unordered_map<int32_t, TouchState>::iterator stateIt =
@@ -3764,7 +3850,7 @@
ALOGD("Touched window was removed: %s in display %" PRId32,
touchedWindow.windowHandle->getName().c_str(), displayId);
}
- sp<InputChannel> touchedInputChannel =
+ std::shared_ptr<InputChannel> touchedInputChannel =
getInputChannelLocked(touchedWindow.windowHandle->getToken());
if (touchedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -3793,30 +3879,36 @@
}
void InputDispatcher::setFocusedApplication(
- int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
+ int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
if (DEBUG_FOCUS) {
ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
}
- { // acquire lock
+ if (inputApplicationHandle != nullptr &&
+ inputApplicationHandle->getApplicationToken() != nullptr) {
+ // acquire lock
std::scoped_lock _l(mLock);
- sp<InputApplicationHandle> oldFocusedApplicationHandle =
+ std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
- if (oldFocusedApplicationHandle == mAwaitedFocusedApplication &&
- inputApplicationHandle != oldFocusedApplicationHandle) {
- resetNoFocusedWindowTimeoutLocked();
+ // If oldFocusedApplicationHandle already exists
+ if (oldFocusedApplicationHandle != nullptr) {
+ // If a new focused application handle is different from the old one and
+ // old focus application info is awaited focused application info.
+ if (*oldFocusedApplicationHandle != *inputApplicationHandle &&
+ mAwaitedFocusedApplication != nullptr &&
+ *oldFocusedApplicationHandle == *mAwaitedFocusedApplication) {
+ resetNoFocusedWindowTimeoutLocked();
+ }
+ // Erase the old application from container first
+ mFocusedApplicationHandlesByDisplay.erase(displayId);
+ // Should already get freed after removed from container but just double check.
+ oldFocusedApplicationHandle.reset();
}
- if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
- if (oldFocusedApplicationHandle != inputApplicationHandle) {
- mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
- }
- } else if (oldFocusedApplicationHandle != nullptr) {
- oldFocusedApplicationHandle.clear();
- mFocusedApplicationHandlesByDisplay.erase(displayId);
- }
+ // Set the new application handle.
+ mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -3843,7 +3935,7 @@
sp<InputWindowHandle> oldFocusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
if (oldFocusedWindowHandle != nullptr) {
- sp<InputChannel> inputChannel =
+ std::shared_ptr<InputChannel> inputChannel =
getInputChannelLocked(oldFocusedWindowHandle->getToken());
if (inputChannel != nullptr) {
CancelationOptions
@@ -3855,19 +3947,18 @@
}
mFocusedDisplayId = displayId;
- // Sanity check
+ // Find new focused window and validate
sp<InputWindowHandle> newFocusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
- onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+ notifyFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
if (newFocusedWindowHandle == 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 int32_t displayId = it.first;
const sp<InputWindowHandle>& windowHandle = it.second;
- ALOGE("Display #%" PRId32 " has focused window: '%s'\n", displayId,
+ ALOGE("Display #%" PRId32 " has focused window: '%s'\n", it.first,
windowHandle->getName().c_str());
}
}
@@ -4064,14 +4155,12 @@
dump += StringPrintf(INDENT "FocusedApplications:\n");
for (auto& it : mFocusedApplicationHandlesByDisplay) {
const int32_t displayId = it.first;
- const sp<InputApplicationHandle>& applicationHandle = it.second;
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle = it.second;
+ const std::chrono::duration timeout =
+ applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
dump += StringPrintf(INDENT2 "displayId=%" PRId32
", name='%s', dispatchingTimeout=%" PRId64 "ms\n",
- displayId, applicationHandle->getName().c_str(),
- ns2ms(applicationHandle
- ->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT)
- .count()));
+ displayId, applicationHandle->getName().c_str(), millis(timeout));
}
} else {
dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
@@ -4134,9 +4223,9 @@
dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
"portalToDisplayId=%d, paused=%s, hasFocus=%s, "
"hasWallpaper=%s, visible=%s, canReceiveKeys=%s, "
- "flags=0x%08x, type=0x%08x, "
+ "flags=%s, type=0x%08x, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
- "windowScale=(%f,%f), touchableRegion=",
+ "touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->displayId,
windowInfo->portalToDisplayId,
toString(windowInfo->paused),
@@ -4144,17 +4233,19 @@
toString(windowInfo->hasWallpaper),
toString(windowInfo->visible),
toString(windowInfo->canReceiveKeys),
- windowInfo->layoutParamsFlags,
- windowInfo->layoutParamsType, windowInfo->frameLeft,
- windowInfo->frameTop, windowInfo->frameRight,
- windowInfo->frameBottom, windowInfo->globalScaleFactor,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ windowInfo->flags.string().c_str(),
+ static_cast<int32_t>(windowInfo->type),
+ windowInfo->frameLeft, windowInfo->frameTop,
+ windowInfo->frameRight, windowInfo->frameBottom,
+ windowInfo->globalScaleFactor);
dumpRegion(dump, windowInfo->touchableRegion);
- dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
+ dump += StringPrintf(", inputFeatures=%s",
+ windowInfo->inputFeatures.string().c_str());
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
"ms\n",
windowInfo->ownerPid, windowInfo->ownerUid,
- ns2ms(windowInfo->dispatchingTimeout));
+ millis(windowInfo->dispatchingTimeout));
+ windowInfo->transform.dump(dump, "transform", INDENT4);
}
} else {
dump += INDENT2 "Windows: <none>\n";
@@ -4260,10 +4351,10 @@
dump += INDENT4;
entry->eventEntry->appendDescription(dump);
dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
- "age=%" PRId64 "ms, wait=%" PRId64 "ms\n",
+ "age=%" PRId64 "ms, wait=%" PRId64 "ms seq=%" PRIu32 "\n",
entry->targetFlags, entry->resolvedAction,
ns2ms(currentTime - entry->eventEntry->eventTime),
- ns2ms(currentTime - entry->deliveryTime));
+ ns2ms(currentTime - entry->deliveryTime), entry->seq);
}
} else {
dump += INDENT3 "WaitQueue: <empty>\n";
@@ -4290,13 +4381,13 @@
const size_t numMonitors = monitors.size();
for (size_t i = 0; i < numMonitors; i++) {
const Monitor& monitor = monitors[i];
- const sp<InputChannel>& channel = monitor.inputChannel;
+ const std::shared_ptr<InputChannel>& channel = monitor.inputChannel;
dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str());
dump += "\n";
}
}
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
+status_t InputDispatcher::registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
#endif
@@ -4324,7 +4415,7 @@
return OK;
}
-status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel,
+status_t InputDispatcher::registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
int32_t displayId, bool isGestureMonitor) {
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -4356,9 +4447,9 @@
return OK;
}
-status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
+status_t InputDispatcher::unregisterInputChannel(const InputChannel& inputChannel) {
#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().c_str());
+ ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel.getName().c_str());
#endif
{ // acquire lock
@@ -4376,23 +4467,23 @@
return OK;
}
-status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
+status_t InputDispatcher::unregisterInputChannelLocked(const InputChannel& inputChannel,
bool notify) {
- sp<Connection> connection = getConnectionLocked(inputChannel->getConnectionToken());
+ sp<Connection> connection = getConnectionLocked(inputChannel.getConnectionToken());
if (connection == nullptr) {
ALOGW("Attempted to unregister already unregistered input channel '%s'",
- inputChannel->getName().c_str());
+ inputChannel.getName().c_str());
return BAD_VALUE;
}
removeConnectionLocked(connection);
- mInputChannelsByToken.erase(inputChannel->getConnectionToken());
+ mInputChannelsByToken.erase(inputChannel.getConnectionToken());
if (connection->monitor) {
removeMonitorChannelLocked(inputChannel);
}
- mLooper->removeFd(inputChannel->getFd());
+ mLooper->removeFd(inputChannel.getFd());
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
@@ -4401,19 +4492,19 @@
return OK;
}
-void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
+void InputDispatcher::removeMonitorChannelLocked(const InputChannel& inputChannel) {
removeMonitorChannelLocked(inputChannel, mGlobalMonitorsByDisplay);
removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay);
}
void InputDispatcher::removeMonitorChannelLocked(
- const sp<InputChannel>& inputChannel,
+ const InputChannel& inputChannel,
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 == inputChannel) {
monitors.erase(monitors.begin() + i);
break;
}
@@ -4464,7 +4555,8 @@
options.deviceId = deviceId;
options.displayId = displayId;
for (const TouchedWindow& window : state.windows) {
- sp<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken());
+ std::shared_ptr<InputChannel> channel =
+ getInputChannelLocked(window.windowHandle->getToken());
if (channel != nullptr) {
synthesizeCancelationEventsForInputChannelLocked(channel, options);
}
@@ -4531,8 +4623,8 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
- const sp<InputWindowHandle>& newFocus) {
+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;
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
@@ -4577,7 +4669,7 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) {
+void InputDispatcher::onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) {
std::string reason = android::base::StringPrintf("%s does not have a focused window",
application->getName().c_str());
@@ -4597,8 +4689,8 @@
updateLastAnrStateLocked(windowLabel, reason);
}
-void InputDispatcher::updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
- const std::string& reason) {
+void InputDispatcher::updateLastAnrStateLocked(
+ const std::shared_ptr<InputApplicationHandle>& application, const std::string& reason) {
const std::string windowLabel = getApplicationWindowLabel(application, nullptr);
updateLastAnrStateLocked(windowLabel, reason);
}
@@ -4652,12 +4744,12 @@
commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
mLock.unlock();
- const nsecs_t timeoutExtension =
+ const std::chrono::nanoseconds timeoutExtension =
mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
mLock.lock();
- if (timeoutExtension > 0) {
+ if (timeoutExtension > 0s) {
extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
} else {
// stop waking up for events in this connection, it is already not responding
@@ -4669,14 +4761,14 @@
}
}
-void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
- const sp<IBinder>& connectionToken,
- nsecs_t timeoutExtension) {
+void InputDispatcher::extendAnrTimeoutsLocked(
+ const std::shared_ptr<InputApplicationHandle>& application,
+ const sp<IBinder>& connectionToken, std::chrono::nanoseconds timeoutExtension) {
sp<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) {
// Maybe ANR happened because there's no focused window?
- mNoFocusedWindowTimeoutTime = now() + timeoutExtension;
+ mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count();
mAwaitedFocusedApplication = application;
} else {
// It's also possible that the connection already disappeared. No action necessary.
@@ -4685,10 +4777,10 @@
}
ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer",
- connection->inputChannel->getName().c_str(), ns2ms(timeoutExtension));
+ connection->inputChannel->getName().c_str(), millis(timeoutExtension));
connection->responsive = true;
- const nsecs_t newTimeout = now() + timeoutExtension;
+ const nsecs_t newTimeout = now() + timeoutExtension.count();
for (DispatchEntry* entry : connection->waitQueue) {
if (newTimeout >= entry->timeoutTime) {
// Already removed old entries when connection was marked unresponsive
@@ -5097,4 +5189,51 @@
return result == std::cv_status::no_timeout;
}
+/**
+ * Sets focus to the window identified by the token. This must be called
+ * after updating any input window handles.
+ *
+ * Params:
+ * request.token - input channel token used to identify the window that should gain focus.
+ * request.focusedToken - the token that the caller expects currently to be focused. If the
+ * specified token does not match the currently focused window, this request will be dropped.
+ * If the specified focused token matches the currently focused window, the call will succeed.
+ * Set this to "null" if this call should succeed no matter what the currently focused token is.
+ * request.timestamp - SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm)
+ * 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::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);
+ }
+ std::shared_ptr<InputChannel> focusedInputChannel =
+ getInputChannelLocked(oldFocusedWindowHandle->getToken());
+ if (focusedInputChannel) {
+ CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+ "focus left window");
+ synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
+ enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/, reason);
+ }
+ mFocusedWindowHandlesByDisplay.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 (mFocusedDisplayId == displayId) {
+ notifyFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+ }
+}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e679c6b..8988714 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -49,6 +49,7 @@
#include <deque>
#include <optional>
#include <unordered_map>
+#include <unordered_set>
#include <InputListener.h>
#include <InputReporterInterface.h>
@@ -114,7 +115,8 @@
const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
handlesPerDisplay) override;
virtual void setFocusedApplication(
- int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) override;
+ int32_t displayId,
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
virtual void setFocusedDisplay(int32_t displayId) override;
virtual void setInputDispatchMode(bool enabled, bool frozen) override;
virtual void setInputFilterEnabled(bool enabled) override;
@@ -123,10 +125,12 @@
virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
const sp<IBinder>& toToken) override;
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) override;
- virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
- bool isGestureMonitor) override;
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
+ virtual status_t registerInputChannel(
+ const std::shared_ptr<InputChannel>& inputChannel) override;
+ virtual void setFocusedWindow(const FocusRequest&) override;
+ virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor) override;
+ virtual status_t unregisterInputChannel(const InputChannel& inputChannel) override;
virtual status_t pilferPointers(const sp<IBinder>& token) override;
private:
@@ -174,7 +178,8 @@
void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock);
// Enqueues a focus event.
- void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) REQUIRES(mLock);
+ void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus,
+ std::string_view reason) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
@@ -209,8 +214,8 @@
return std::hash<IBinder*>{}(b.get());
}
};
- std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken
- GUARDED_BY(mLock);
+ std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, IBinderHash>
+ mInputChannelsByToken GUARDED_BY(mLock);
// Finds the display ID of the gesture monitor identified by the provided token.
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
@@ -274,7 +279,7 @@
void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
nsecs_t processAnrsLocked() REQUIRES(mLock);
- nsecs_t getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
+ std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
// Input filter processing.
bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
@@ -300,8 +305,10 @@
REQUIRES(mLock);
sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
REQUIRES(mLock);
- sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
+ std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
+ REQUIRES(mLock);
bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+ bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
/*
* Validate and update InputWindowHandles for a given display.
@@ -317,8 +324,8 @@
std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
// Focused applications.
- std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
- GUARDED_BY(mLock);
+ std::unordered_map<int32_t, std::shared_ptr<InputApplicationHandle>>
+ mFocusedApplicationHandlesByDisplay GUARDED_BY(mLock);
// Top focused display.
int32_t mFocusedDisplayId GUARDED_BY(mLock);
@@ -326,6 +333,11 @@
// Dispatcher state at time of last ANR.
std::string mLastAnrState GUARDED_BY(mLock);
+ // The connection tokens of the channels that the user last interacted, for debugging
+ std::unordered_set<sp<IBinder>, IBinderHash> mInteractionConnectionTokens GUARDED_BY(mLock);
+ void updateInteractionTokensLocked(const EventEntry& entry,
+ const std::vector<InputTarget>& targets) REQUIRES(mLock);
+
// Dispatch inbound events.
bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry)
REQUIRES(mLock);
@@ -365,7 +377,7 @@
* The focused application at the time when no focused window was present.
* Used to raise an ANR when we have no focused window.
*/
- sp<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+ std::shared_ptr<InputApplicationHandle> mAwaitedFocusedApplication 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.
@@ -373,9 +385,9 @@
// Once a connection becomes unresponsive, its entries are removed from AnrTracker to
// prevent unneeded wakeups.
AnrTracker mAnrTracker GUARDED_BY(mLock);
- void extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
- const sp<IBinder>& connectionToken, nsecs_t timeoutExtension)
- REQUIRES(mLock);
+ void extendAnrTimeoutsLocked(const std::shared_ptr<InputApplicationHandle>& application,
+ const sp<IBinder>& connectionToken,
+ std::chrono::nanoseconds timeoutExtension) REQUIRES(mLock);
// Contains the last window which received a hover event.
sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
@@ -417,8 +429,9 @@
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
int32_t y) const REQUIRES(mLock);
bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
- std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle);
+ std::string getApplicationWindowLabel(
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle,
+ const sp<InputWindowHandle>& windowHandle);
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
@@ -453,8 +466,8 @@
void synthesizeCancelationEventsForMonitorsLocked(
const CancelationOptions& options,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
- void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
- const CancelationOptions& options)
+ void synthesizeCancelationEventsForInputChannelLocked(
+ const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options)
REQUIRES(mLock);
void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
const CancelationOptions& options)
@@ -475,11 +488,11 @@
void logDispatchStateLocked() REQUIRES(mLock);
// Registration.
- void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+ void removeMonitorChannelLocked(const InputChannel& inputChannel) REQUIRES(mLock);
void removeMonitorChannelLocked(
- const sp<InputChannel>& inputChannel,
+ const InputChannel& inputChannel,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
- status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
+ status_t unregisterInputChannelLocked(const InputChannel& inputChannel, bool notify)
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
@@ -488,12 +501,15 @@
void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
- const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
+ const sp<InputWindowHandle>& newFocus, int32_t displayId,
+ std::string_view reason) REQUIRES(mLock);
+ void notifyFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
+ const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
- void onAnrLocked(const sp<InputApplicationHandle>& application) REQUIRES(mLock);
+ void onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
REQUIRES(mLock);
- void updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+ void updateLastAnrStateLocked(const std::shared_ptr<InputApplicationHandle>& application,
const std::string& reason) REQUIRES(mLock);
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 0588374..d39113b 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,12 +42,11 @@
return StringPrintf("%" PRId32, dispatchMode);
}
-void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
- float windowXScale, float windowYScale) {
+void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
// The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
// and non splittable windows since we will just use all the pointers from the input event.
if (newPointerIds.isEmpty()) {
- setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
+ setDefaultPointerTransform(transform);
return;
}
@@ -57,47 +56,38 @@
pointerIds |= newPointerIds;
while (!newPointerIds.isEmpty()) {
int32_t pointerId = newPointerIds.clearFirstMarkedBit();
- pointerInfos[pointerId].xOffset = xOffset;
- pointerInfos[pointerId].yOffset = yOffset;
- pointerInfos[pointerId].windowXScale = windowXScale;
- pointerInfos[pointerId].windowYScale = windowYScale;
+ pointerTransforms[pointerId] = transform;
}
}
-void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
- float windowYScale) {
+void InputTarget::setDefaultPointerTransform(const ui::Transform& transform) {
pointerIds.clear();
- pointerInfos[0].xOffset = xOffset;
- pointerInfos[0].yOffset = yOffset;
- pointerInfos[0].windowXScale = windowXScale;
- pointerInfos[0].windowYScale = windowYScale;
+ pointerTransforms[0] = transform;
}
-bool InputTarget::useDefaultPointerInfo() const {
+bool InputTarget::useDefaultPointerTransform() const {
return pointerIds.isEmpty();
}
-const PointerInfo& InputTarget::getDefaultPointerInfo() const {
- return pointerInfos[0];
+const ui::Transform& InputTarget::getDefaultPointerTransform() const {
+ return pointerTransforms[0];
}
std::string InputTarget::getPointerInfoString() const {
- if (useDefaultPointerInfo()) {
- const PointerInfo& pointerInfo = getDefaultPointerInfo();
- return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)",
- pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale,
- pointerInfo.windowYScale);
+ std::string out = "\n";
+ if (useDefaultPointerTransform()) {
+ const ui::Transform& transform = getDefaultPointerTransform();
+ transform.dump(out, "default", " ");
+ return out;
}
- std::string out;
for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
if (!pointerIds.hasBit(i)) {
continue;
}
- out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f "
- "windowScaleFactor=(%.1f, %.1f)",
- i, pointerInfos[i].xOffset, pointerInfos[i].yOffset,
- pointerInfos[i].windowXScale, pointerInfos[i].windowYScale);
+
+ const std::string name = "pointerId " + std::to_string(i) + ":";
+ pointerTransforms[i].dump(out, name.c_str(), " ");
}
return out;
}
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 499a75f..debf805 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -18,28 +18,13 @@
#define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
#include <input/InputTransport.h>
+#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/RefBase.h>
namespace android::inputdispatcher {
/*
- * Information about each pointer for an InputTarget. This includes offset and scale so
- * all pointers can be normalized to a single offset and scale.
- *
- * These values are ignored for KeyEvents
- */
-struct PointerInfo {
- // The x and y offset to add to a MotionEvent as it is delivered.
- float xOffset = 0.0f;
- float yOffset = 0.0f;
-
- // Scaling factor to apply to MotionEvent as it is delivered.
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
-};
-
-/*
* An input target specifies how an input event is to be dispatched to a particular window
* including the window's input channel, control flags, a timeout, and an X / Y offset to
* be added to input event coordinates to compensate for the absolute position of the
@@ -106,7 +91,7 @@
};
// The input channel to be targeted.
- sp<InputChannel> inputChannel;
+ std::shared_ptr<InputChannel> inputChannel;
// Flags for the input target.
int32_t flags = 0;
@@ -119,13 +104,11 @@
// if FLAG_SPLIT is set.
BitSet32 pointerIds;
// The data is stored by the pointerId. Use the bit position of pointerIds to look up
- // PointerInfo per pointerId.
- PointerInfo pointerInfos[MAX_POINTERS];
+ // Transform per pointerId.
+ ui::Transform pointerTransforms[MAX_POINTERS];
- void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
- float windowYScale);
- void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
- float windowYScale);
+ void addPointers(BitSet32 pointerIds, const ui::Transform& transform);
+ void setDefaultPointerTransform(const ui::Transform& transform);
/**
* Returns whether the default pointer information should be used. This will be true when the
@@ -133,13 +116,13 @@
* and non splittable windows since we want all pointers for the EventEntry to go to this
* target.
*/
- bool useDefaultPointerInfo() const;
+ bool useDefaultPointerTransform() const;
/**
- * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
+ * Returns the default Transform object. This should be used when useDefaultPointerTransform is
* true.
*/
- const PointerInfo& getDefaultPointerInfo() const;
+ const ui::Transform& getDefaultPointerTransform() const;
std::string getPointerInfoString() const;
};
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
index 289b084..b347674 100644
--- a/services/inputflinger/dispatcher/Monitor.cpp
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -19,7 +19,7 @@
namespace android::inputdispatcher {
// --- Monitor ---
-Monitor::Monitor(const sp<InputChannel>& inputChannel) : inputChannel(inputChannel) {}
+Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel) : inputChannel(inputChannel) {}
// --- TouchedMonitor ---
TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset)
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index b67c9eb..fc0b020 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -22,9 +22,9 @@
namespace android::inputdispatcher {
struct Monitor {
- sp<InputChannel> inputChannel; // never null
+ std::shared_ptr<InputChannel> inputChannel; // never null
- explicit Monitor(const sp<InputChannel>& inputChannel);
+ explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel);
};
// For tracking the offsets we need to apply when adding gesture monitor targets.
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 2baceba..81b3cf0 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -137,8 +137,7 @@
for (const TouchedWindow& window : windows) {
if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
if (haveSlipperyForegroundWindow ||
- !(window.windowHandle->getInfo()->layoutParamsFlags &
- InputWindowInfo::FLAG_SLIPPERY)) {
+ !window.windowHandle->getInfo()->flags.test(InputWindowInfo::Flag::SLIPPERY)) {
return false;
}
haveSlipperyForegroundWindow = true;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 9b002f4..179a263 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -18,15 +18,15 @@
#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
#include <InputListener.h>
-#include <input/ISetInputWindowsListener.h>
+#include <android/FocusRequest.h>
+#include <android/os/ISetInputWindowsListener.h>
+#include <input/InputApplication.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
#include <unordered_map>
namespace android {
-class InputApplicationHandle;
-class InputChannel;
-class InputWindowHandle;
-
/*
* Constants used to report the outcome of input event injection.
*/
@@ -113,7 +113,8 @@
* This method may be called on any thread (usually by the input manager).
*/
virtual void setFocusedApplication(
- int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+ int32_t displayId,
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
/* Sets the focused display.
*
@@ -149,11 +150,16 @@
*/
virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
+ /**
+ * Sets focus on the specified window.
+ */
+ virtual void setFocusedWindow(const FocusRequest&) = 0;
+
/* Registers input channels that may be used as targets for input events.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+ virtual status_t registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) = 0;
/* Registers input channels to be used to monitor input events.
*
@@ -163,14 +169,14 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
- bool gestureMonitor) = 0;
+ virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
+ int32_t displayId, bool gestureMonitor) = 0;
/* Unregister input channels that will no longer receive input events.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
+ virtual status_t unregisterInputChannel(const InputChannel& inputChannel) = 0;
/* Allows an input monitor steal the current pointer stream away from normal input windows.
*
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 667af9b..d04d797 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -21,11 +21,11 @@
#include <binder/IBinder.h>
#include <input/Input.h>
+#include <input/InputApplication.h>
#include <utils/RefBase.h>
namespace android {
-class InputApplicationHandle;
/*
* Input dispatcher policy interface.
@@ -47,8 +47,9 @@
/* Notifies the system that an application is not responding.
* Returns a new timeout to continue waiting, or 0 to abort dispatch. */
- virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) = 0;
+ virtual std::chrono::nanoseconds notifyAnr(
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token, const std::string& reason) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index b56f356..3496a24 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -59,9 +59,11 @@
shared_libs: [
"libbinder",
"libinputflingerhost",
- "libutils"
+ "libutils",
+ "libinput"
],
static_libs: [
"libarect",
+ "libui-types",
],
}
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index 683c05d..ff69800 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -217,8 +217,10 @@
idi.product = id->productId;
idi.version = id->version;
- std::string configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
- idi, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
+ std::string configFile =
+ getInputDeviceConfigurationFilePathByDeviceIdentifier(idi,
+ InputDeviceConfigurationFileType::
+ CONFIGURATION);
if (configFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.",
idi.name.c_str());
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 973b4f9..2a2cea5 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -22,13 +22,17 @@
#include "InputHost.h"
+#include <android/os/BnInputFlinger.h>
+#include <android/os/ISetInputWindowsListener.h>
+#include <binder/Binder.h>
#include <cutils/compiler.h>
-#include <input/IInputFlinger.h>
-#include <input/ISetInputWindowsListener.h>
-#include <utils/String8.h>
#include <utils/String16.h>
+#include <utils/String8.h>
#include <utils/StrongPointer.h>
+using android::os::BnInputFlinger;
+using android::os::ISetInputWindowsListener;
+
namespace android {
class InputFlinger : public BnInputFlinger {
@@ -40,10 +44,13 @@
InputFlinger() ANDROID_API;
virtual status_t dump(int fd, const Vector<String16>& args);
- void setInputWindows(const std::vector<InputWindowInfo>&,
- const sp<ISetInputWindowsListener>&) {}
- void registerInputChannel(const sp<InputChannel>&) {}
- void unregisterInputChannel(const sp<InputChannel>&) {}
+ binder::Status setInputWindows(const std::vector<InputWindowInfo>&,
+ const sp<ISetInputWindowsListener>&) {
+ return binder::Status::ok();
+ }
+ binder::Status registerInputChannel(const InputChannel&) { return binder::Status::ok(); }
+ binder::Status unregisterInputChannel(const InputChannel&) { return binder::Status::ok(); }
+ binder::Status setFocusedWindow(const FocusRequest&) { return binder::Status::ok(); }
private:
virtual ~InputFlinger();
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 0fa8787..a24b293 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -17,31 +17,28 @@
#ifndef _UI_INPUT_READER_BASE_H
#define _UI_INPUT_READER_BASE_H
-#include "PointerControllerInterface.h"
-
#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/VelocityControl.h>
#include <input/VelocityTracker.h>
+#include <stddef.h>
+#include <unistd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
-#include <stddef.h>
-#include <unistd.h>
#include <optional>
#include <set>
#include <unordered_map>
#include <vector>
+#include "PointerControllerInterface.h"
+#include "VibrationElement.h"
+
// Maximum supported size of a vibration pattern.
// Must be at least 2.
#define MAX_VIBRATE_PATTERN_SIZE 100
-// Maximum allowable delay value in a vibration pattern before
-// which the delay will be truncated.
-#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
-
namespace android {
// --- InputReaderInterface ---
@@ -104,8 +101,8 @@
virtual void requestRefreshConfiguration(uint32_t changes) = 0;
/* Controls the vibrator of a particular input device. */
- virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
- ssize_t repeat, int32_t token) = 0;
+ virtual void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
+ ssize_t repeat, int32_t token) = 0;
virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
/* Return true if the device can send input events to the specified display. */
diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h
new file mode 100644
index 0000000..b60ffac
--- /dev/null
+++ b/services/inputflinger/include/VibrationElement.h
@@ -0,0 +1,44 @@
+/*
+ * 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 _VIBRATION_ELEMENT_H
+#define _VIBRATION_ELEMENT_H
+
+#include <array>
+#include <chrono>
+#include <cstdint>
+#include <string>
+
+namespace android {
+
+// evdev FF_RUMBLE effect only supports two channels of vibration.
+constexpr size_t CHANNEL_SIZE = 2;
+/*
+ * Describes a rumble effect
+ */
+struct VibrationElement {
+ std::chrono::milliseconds duration;
+ // Channel amplitude range 0-255.
+ std::array<uint8_t, CHANNEL_SIZE> channels = {0, 0};
+
+ const std::string toString() const;
+ uint16_t getMagnitude(size_t channelIndex) const;
+ bool isOn() const;
+};
+
+} // namespace android
+
+#endif // _VIBRATION_ELEMENT_H
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 83a610f..0ccada9 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -81,4 +81,7 @@
export_header_lib_headers: [
"libinputreader_headers",
],
+ static_libs: [
+ "libc++fs"
+ ],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index a1514af..76b9419 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -34,45 +34,36 @@
#define LOG_TAG "EventHub"
// #define LOG_NDEBUG 0
-
-#include "EventHub.h"
-
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
+#include <input/KeyCharacterMap.h>
+#include <input/KeyLayoutMap.h>
+#include <input/VirtualKeyMap.h>
#include <openssl/sha.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Timers.h>
-#include <utils/threads.h>
-#include <input/KeyCharacterMap.h>
-#include <input/KeyLayoutMap.h>
-#include <input/VirtualKeyMap.h>
+#include <filesystem>
-/* this macro is used to tell if "bit" is set in "array"
- * it selects a byte from the array, and does a boolean AND
- * operation with a byte that only has the relevant bit set.
- * eg. to check for the 12th bit, we do (array[1] & 1<<4)
- */
-#define test_bit(bit, array) ((array)[(bit) / 8] & (1 << ((bit) % 8)))
-
-/* this macro computes the number of bytes needed to represent a bit array of the specified size */
-#define sizeof_bit_array(bits) (((bits) + 7) / 8)
+#include "EventHub.h"
#define INDENT " "
#define INDENT2 " "
#define INDENT3 " "
using android::base::StringPrintf;
+using namespace android::flag_operators;
namespace android {
-static constexpr bool DEBUG = false;
-
static const char* DEVICE_PATH = "/dev/input";
// v4l2 devices go directly into /dev
static const char* VIDEO_DEVICE_PATH = "/dev";
+static constexpr size_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
+static constexpr size_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
+
static inline const char* toString(bool value) {
return value ? "true" : "false";
}
@@ -94,8 +85,8 @@
/**
* Return true if name matches "v4l-touch*"
*/
-static bool isV4lTouchNode(const char* name) {
- return strstr(name, "v4l-touch") == name;
+static bool isV4lTouchNode(std::string name) {
+ return name.find("v4l-touch") != std::string::npos;
}
/**
@@ -138,9 +129,9 @@
// --- Global Functions ---
-uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
+Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, Flags<InputDeviceClass> deviceClasses) {
// Touch devices get dibs on touch-related axes.
- if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) {
+ if (deviceClasses.test(InputDeviceClass::TOUCH)) {
switch (axis) {
case ABS_X:
case ABS_Y:
@@ -162,27 +153,26 @@
case ABS_MT_TRACKING_ID:
case ABS_MT_PRESSURE:
case ABS_MT_DISTANCE:
- return INPUT_DEVICE_CLASS_TOUCH;
+ return InputDeviceClass::TOUCH;
}
}
// External stylus gets the pressure axis
- if (deviceClasses & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+ if (deviceClasses.test(InputDeviceClass::EXTERNAL_STYLUS)) {
if (axis == ABS_PRESSURE) {
- return INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
+ return InputDeviceClass::EXTERNAL_STYLUS;
}
}
// Joystick devices get the rest.
- return deviceClasses & INPUT_DEVICE_CLASS_JOYSTICK;
+ return deviceClasses & InputDeviceClass::JOYSTICK;
}
// --- EventHub::Device ---
EventHub::Device::Device(int fd, int32_t id, const std::string& path,
const InputDeviceIdentifier& identifier)
- : next(nullptr),
- fd(fd),
+ : fd(fd),
id(id),
path(path),
identifier(identifier),
@@ -193,19 +183,10 @@
ffEffectId(-1),
controllerNumber(0),
enabled(true),
- isVirtual(fd < 0) {
- memset(keyBitmask, 0, sizeof(keyBitmask));
- memset(absBitmask, 0, sizeof(absBitmask));
- memset(relBitmask, 0, sizeof(relBitmask));
- memset(swBitmask, 0, sizeof(swBitmask));
- memset(ledBitmask, 0, sizeof(ledBitmask));
- memset(ffBitmask, 0, sizeof(ffBitmask));
- memset(propBitmask, 0, sizeof(propBitmask));
-}
+ isVirtual(fd < 0) {}
EventHub::Device::~Device() {
close();
- delete configuration;
}
void EventHub::Device::close() {
@@ -231,10 +212,162 @@
return OK;
}
-bool EventHub::Device::hasValidFd() {
+bool EventHub::Device::hasValidFd() const {
return !isVirtual && enabled;
}
+const sp<KeyCharacterMap>& EventHub::Device::getKeyCharacterMap() const {
+ if (combinedKeyMap != nullptr) {
+ return combinedKeyMap;
+ }
+ return keyMap.keyCharacterMap;
+}
+
+template <std::size_t N>
+status_t EventHub::Device::readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray) {
+ if (!hasValidFd()) {
+ return BAD_VALUE;
+ }
+ if ((_IOC_SIZE(ioctlCode) == 0)) {
+ ioctlCode |= _IOC(0, 0, 0, bitArray.bytes());
+ }
+
+ typename BitArray<N>::Buffer buffer;
+ status_t ret = ioctl(fd, ioctlCode, buffer.data());
+ bitArray.loadFromBuffer(buffer);
+ return ret;
+}
+
+void EventHub::Device::configureFd() {
+ // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type
+ if (classes.test(InputDeviceClass::KEYBOARD)) {
+ // Disable kernel key repeat since we handle it ourselves
+ unsigned int repeatRate[] = {0, 0};
+ if (ioctl(fd, EVIOCSREP, repeatRate)) {
+ ALOGW("Unable to disable kernel key repeat for %s: %s", path.c_str(), strerror(errno));
+ }
+ }
+
+ // Tell the kernel that we want to use the monotonic clock for reporting timestamps
+ // associated with input events. This is important because the input system
+ // uses the timestamps extensively and assumes they were recorded using the monotonic
+ // clock.
+ int clockId = CLOCK_MONOTONIC;
+ bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
+ ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
+}
+
+bool EventHub::Device::hasKeycodeLocked(int keycode) const {
+ if (!keyMap.haveKeyLayout()) {
+ return false;
+ }
+
+ std::vector<int32_t> scanCodes;
+ keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
+ const size_t N = scanCodes.size();
+ for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
+ int32_t sc = scanCodes[i];
+ if (sc >= 0 && sc <= KEY_MAX && keyBitmask.test(sc)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void EventHub::Device::loadConfigurationLocked() {
+ configurationFile =
+ getInputDeviceConfigurationFilePathByDeviceIdentifier(identifier,
+ InputDeviceConfigurationFileType::
+ CONFIGURATION);
+ if (configurationFile.empty()) {
+ ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
+ } else {
+ PropertyMap* propertyMap;
+ status_t status = PropertyMap::load(String8(configurationFile.c_str()), &propertyMap);
+ if (status) {
+ ALOGE("Error loading input device configuration file for device '%s'. "
+ "Using default configuration.",
+ identifier.name.c_str());
+ } else {
+ configuration = std::unique_ptr<PropertyMap>(propertyMap);
+ }
+ }
+}
+
+bool EventHub::Device::loadVirtualKeyMapLocked() {
+ // The virtual key map is supplied by the kernel as a system board property file.
+ std::string propPath = "/sys/board_properties/virtualkeys.";
+ propPath += identifier.getCanonicalName();
+ if (access(propPath.c_str(), R_OK)) {
+ return false;
+ }
+ virtualKeyMap = VirtualKeyMap::load(propPath);
+ return virtualKeyMap != nullptr;
+}
+
+status_t EventHub::Device::loadKeyMapLocked() {
+ return keyMap.load(identifier, configuration.get());
+}
+
+bool EventHub::Device::isExternalDeviceLocked() {
+ if (configuration) {
+ bool value;
+ if (configuration->tryGetProperty(String8("device.internal"), value)) {
+ return !value;
+ }
+ }
+ return identifier.bus == BUS_USB || identifier.bus == BUS_BLUETOOTH;
+}
+
+bool EventHub::Device::deviceHasMicLocked() {
+ if (configuration) {
+ bool value;
+ if (configuration->tryGetProperty(String8("audio.mic"), value)) {
+ return value;
+ }
+ }
+ return false;
+}
+
+void EventHub::Device::setLedStateLocked(int32_t led, bool on) {
+ int32_t sc;
+ if (hasValidFd() && mapLed(led, &sc) != NAME_NOT_FOUND) {
+ struct input_event ev;
+ ev.time.tv_sec = 0;
+ ev.time.tv_usec = 0;
+ ev.type = EV_LED;
+ ev.code = sc;
+ ev.value = on ? 1 : 0;
+
+ ssize_t nWrite;
+ do {
+ nWrite = write(fd, &ev, sizeof(struct input_event));
+ } while (nWrite == -1 && errno == EINTR);
+ }
+}
+
+void EventHub::Device::setLedForControllerLocked() {
+ for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) {
+ setLedStateLocked(ALED_CONTROLLER_1 + i, controllerNumber == i + 1);
+ }
+}
+
+status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const {
+ if (!keyMap.haveKeyLayout()) {
+ return NAME_NOT_FOUND;
+ }
+
+ int32_t scanCode;
+ if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
+ if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) {
+ *outScanCode = scanCode;
+ return NO_ERROR;
+ }
+ }
+ return NAME_NOT_FOUND;
+}
+
/**
* Get the capabilities for the current process.
* Crashes the system if unable to create / check / destroy the capabilities object.
@@ -284,8 +417,6 @@
: mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
mNextDeviceId(1),
mControllerNumbers(),
- mOpeningDevices(nullptr),
- mClosingDevices(nullptr),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false),
mNeedToScanDevices(true),
@@ -340,12 +471,6 @@
EventHub::~EventHub(void) {
closeAllDevicesLocked();
- while (mClosingDevices) {
- Device* device = mClosingDevices;
- mClosingDevices = device->next;
- delete device;
- }
-
::close(mEpollFd);
::close(mINotifyFd);
::close(mWakeReadPipeFd);
@@ -355,28 +480,25 @@
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == nullptr) return InputDeviceIdentifier();
- return device->identifier;
+ return device != nullptr ? device->identifier : InputDeviceIdentifier();
}
-uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
+Flags<InputDeviceClass> EventHub::getDeviceClasses(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == nullptr) return 0;
- return device->classes;
+ return device != nullptr ? device->classes : Flags<InputDeviceClass>(0);
}
int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == nullptr) return 0;
- return device->controllerNumber;
+ return device != nullptr ? device->controllerNumber : 0;
}
void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->configuration) {
+ if (device != nullptr && device->configuration) {
*outConfiguration = *device->configuration;
} else {
outConfiguration->clear();
@@ -391,7 +513,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
+ if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
struct input_absinfo info;
if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
@@ -416,25 +538,19 @@
bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
if (axis >= 0 && axis <= REL_MAX) {
AutoMutex _l(mLock);
-
Device* device = getDeviceLocked(deviceId);
- if (device) {
- return test_bit(axis, device->relBitmask);
- }
+ return device != nullptr ? device->relBitmask.test(axis) : false;
}
return false;
}
bool EventHub::hasInputProperty(int32_t deviceId, int property) const {
- if (property >= 0 && property <= INPUT_PROP_MAX) {
- AutoMutex _l(mLock);
+ AutoMutex _l(mLock);
- Device* device = getDeviceLocked(deviceId);
- if (device) {
- return test_bit(property, device->propBitmask);
- }
- }
- return false;
+ Device* device = getDeviceLocked(deviceId);
+ return property >= 0 && property <= INPUT_PROP_MAX && device != nullptr
+ ? device->propBitmask.test(property)
+ : false;
}
int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
@@ -442,11 +558,9 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(scanCode, device->keyBitmask)) {
- uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
- memset(keyState, 0, sizeof(keyState));
- if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
- return test_bit(scanCode, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ if (device != nullptr && device->hasValidFd() && device->keyBitmask.test(scanCode)) {
+ if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
+ return device->keyState.test(scanCode) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
}
}
@@ -457,16 +571,14 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
+ if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
std::vector<int32_t> scanCodes;
device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
if (scanCodes.size() != 0) {
- uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
- memset(keyState, 0, sizeof(keyState));
- if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
+ if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
for (size_t i = 0; i < scanCodes.size(); i++) {
int32_t sc = scanCodes[i];
- if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, keyState)) {
+ if (sc >= 0 && sc <= KEY_MAX && device->keyState.test(sc)) {
return AKEY_STATE_DOWN;
}
}
@@ -482,11 +594,9 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(sw, device->swBitmask)) {
- uint8_t swState[sizeof_bit_array(SW_MAX + 1)];
- memset(swState, 0, sizeof(swState));
- if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) {
- return test_bit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ if (device != nullptr && device->hasValidFd() && device->swBitmask.test(sw)) {
+ if (device->readDeviceBitMask(EVIOCGSW(0), device->swState) >= 0) {
+ return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
}
}
@@ -500,7 +610,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
+ if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
struct input_absinfo info;
if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
@@ -520,7 +630,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->keyMap.haveKeyLayout()) {
+ if (device != nullptr && device->keyMap.haveKeyLayout()) {
std::vector<int32_t> scanCodes;
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
scanCodes.clear();
@@ -531,7 +641,7 @@
// check the possible scan codes identified by the layout map against the
// map of codes actually emitted by the driver
for (size_t sc = 0; sc < scanCodes.size(); sc++) {
- if (test_bit(scanCodes[sc], device->keyBitmask)) {
+ if (device->keyBitmask.test(scanCodes[sc])) {
outFlags[codeIndex] = 1;
break;
}
@@ -549,7 +659,7 @@
Device* device = getDeviceLocked(deviceId);
status_t status = NAME_NOT_FOUND;
- if (device) {
+ if (device != nullptr) {
// Check the key character map first.
sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
if (kcm != nullptr) {
@@ -588,7 +698,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->keyMap.haveKeyLayout()) {
+ if (device != nullptr && device->keyMap.haveKeyLayout()) {
status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo);
if (err == NO_ERROR) {
return NO_ERROR;
@@ -607,10 +717,8 @@
bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && scanCode >= 0 && scanCode <= KEY_MAX) {
- if (test_bit(scanCode, device->keyBitmask)) {
- return true;
- }
+ if (device != nullptr && scanCode >= 0 && scanCode <= KEY_MAX) {
+ return device->keyBitmask.test(scanCode);
}
return false;
}
@@ -619,10 +727,8 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
int32_t sc;
- if (device && mapLed(device, led, &sc) == NO_ERROR) {
- if (test_bit(sc, device->ledBitmask)) {
- return true;
- }
+ if (device != nullptr && device->mapLed(led, &sc) == NO_ERROR) {
+ return device->ledBitmask.test(sc);
}
return false;
}
@@ -630,23 +736,8 @@
void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- setLedStateLocked(device, led, on);
-}
-
-void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) {
- int32_t sc;
- if (device && device->hasValidFd() && mapLed(device, led, &sc) != NAME_NOT_FOUND) {
- struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
- ev.type = EV_LED;
- ev.code = sc;
- ev.value = on ? 1 : 0;
-
- ssize_t nWrite;
- do {
- nWrite = write(device->fd, &ev, sizeof(struct input_event));
- } while (nWrite == -1 && errno == EINTR);
+ if (device != nullptr && device->hasValidFd()) {
+ device->setLedStateLocked(led, on);
}
}
@@ -656,7 +747,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->virtualKeyMap) {
+ if (device != nullptr && device->virtualKeyMap) {
const std::vector<VirtualKeyDefinition> virtualKeys =
device->virtualKeyMap->getVirtualKeys();
outVirtualKeys.insert(outVirtualKeys.end(), virtualKeys.begin(), virtualKeys.end());
@@ -666,7 +757,7 @@
sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device) {
+ if (device != nullptr) {
return device->getKeyCharacterMap();
}
return nullptr;
@@ -675,7 +766,7 @@
bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device) {
+ if (device != nullptr) {
if (map != device->overlayKeyMap) {
device->overlayKeyMap = map;
device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map);
@@ -735,17 +826,18 @@
identifier.descriptor.c_str());
}
-void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
+void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd()) {
+ if (device != nullptr && device->hasValidFd()) {
ff_effect effect;
memset(&effect, 0, sizeof(effect));
effect.type = FF_RUMBLE;
effect.id = device->ffEffectId;
- effect.u.rumble.strong_magnitude = 0xc000;
- effect.u.rumble.weak_magnitude = 0xc000;
- effect.replay.length = (duration + 999999LL) / 1000000LL;
+ // evdev FF_RUMBLE effect only supports two channels of vibration.
+ effect.u.rumble.strong_magnitude = element.getMagnitude(FF_STRONG_MAGNITUDE_CHANNEL_IDX);
+ effect.u.rumble.weak_magnitude = element.getMagnitude(FF_WEAK_MAGNITUDE_CHANNEL_IDX);
+ effect.replay.length = element.duration.count();
effect.replay.delay = 0;
if (ioctl(device->fd, EVIOCSFF, &effect)) {
ALOGW("Could not upload force feedback effect to device %s due to error %d.",
@@ -772,7 +864,7 @@
void EventHub::cancelVibrate(int32_t deviceId) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd()) {
+ if (device != nullptr && device->hasValidFd()) {
if (device->ffEffectPlaying) {
device->ffEffectPlaying = false;
@@ -792,11 +884,9 @@
}
EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const {
- size_t size = mDevices.size();
- for (size_t i = 0; i < size; i++) {
- Device* device = mDevices.valueAt(i);
+ for (const auto& [id, device] : mDevices) {
if (descriptor == device->identifier.descriptor) {
- return device;
+ return device.get();
}
}
return nullptr;
@@ -806,15 +896,14 @@
if (deviceId == ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID) {
deviceId = mBuiltInKeyboardId;
}
- ssize_t index = mDevices.indexOfKey(deviceId);
- return index >= 0 ? mDevices.valueAt(index) : NULL;
+ const auto& it = mDevices.find(deviceId);
+ return it != mDevices.end() ? it->second.get() : nullptr;
}
-EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const {
- for (size_t i = 0; i < mDevices.size(); i++) {
- Device* device = mDevices.valueAt(i);
+EventHub::Device* EventHub::getDeviceByPathLocked(const std::string& devicePath) const {
+ for (const auto& [id, device] : mDevices) {
if (device->path == devicePath) {
- return device;
+ return device.get();
}
}
return nullptr;
@@ -828,15 +917,14 @@
* devices are ignored.
*/
EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const {
- for (size_t i = 0; i < mDevices.size(); i++) {
- Device* device = mDevices.valueAt(i);
+ for (const auto& [id, device] : mDevices) {
if (device->fd == fd) {
// This is an input device event
- return device;
+ return device.get();
}
if (device->videoDevice && device->videoDevice->getFd() == fd) {
// This is a video device event
- return device;
+ return device.get();
}
}
// We do not check mUnattachedVideoDevices here because they should not participate in epoll,
@@ -869,17 +957,16 @@
}
// Report any devices that had last been added/removed.
- while (mClosingDevices) {
- Device* device = mClosingDevices;
+ for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
+ std::unique_ptr<Device> device = std::move(*it);
ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
- mClosingDevices = device->next;
event->when = now;
event->deviceId = (device->id == mBuiltInKeyboardId)
? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
: device->id;
event->type = DEVICE_REMOVED;
event += 1;
- delete device;
+ it = mClosingDevices.erase(it);
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
@@ -892,14 +979,18 @@
mNeedToSendFinishedDeviceScan = true;
}
- while (mOpeningDevices != nullptr) {
- Device* device = mOpeningDevices;
+ for (auto it = mOpeningDevices.begin(); it != mOpeningDevices.end();) {
+ std::unique_ptr<Device> device = std::move(*it);
ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
- mOpeningDevices = device->next;
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) {
+ ALOGW("Device id %d exists, replaced.", device->id);
+ }
+ it = mOpeningDevices.erase(it);
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
@@ -933,11 +1024,11 @@
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true;
- char buffer[16];
+ char wakeReadBuffer[16];
ssize_t nRead;
do {
- nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
- } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+ nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer));
+ } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
@@ -946,7 +1037,7 @@
}
Device* device = getDeviceByFdLocked(eventItem.data.fd);
- if (!device) {
+ if (device == nullptr) {
ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,
eventItem.data.fd);
ALOG_ASSERT(!DEBUG);
@@ -982,7 +1073,7 @@
" bufferSize: %zu capacity: %zu errno: %d)\n",
device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
- closeDeviceLocked(device);
+ closeDeviceLocked(*device);
} else if (readSize < 0) {
if (errno != EAGAIN && errno != EINTR) {
ALOGW("could not get event (errno=%d)", errno);
@@ -1014,7 +1105,7 @@
ALOGI("Removing device %s due to epoll hang-up event.",
device->identifier.name.c_str());
deviceChanged = true;
- closeDeviceLocked(device);
+ closeDeviceLocked(*device);
} else {
ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
device->identifier.name.c_str());
@@ -1089,7 +1180,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (!device || !device->videoDevice) {
+ if (device == nullptr || !device->videoDevice) {
return {};
}
return device->videoDevice->consumeFrames();
@@ -1119,24 +1210,13 @@
ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH);
}
}
- if (mDevices.indexOfKey(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) < 0) {
+ if (mDevices.find(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) == mDevices.end()) {
createVirtualKeyboardLocked();
}
}
// ----------------------------------------------------------------------------
-static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
- const uint8_t* end = array + endIndex;
- array += startIndex;
- while (array != end) {
- if (*(array++) != 0) {
- return true;
- }
- }
- return false;
-}
-
static const int32_t GAMEPAD_KEYCODES[] = {
AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, //
AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, //
@@ -1166,20 +1246,14 @@
return OK;
}
-status_t EventHub::registerDeviceForEpollLocked(Device* device) {
- if (device == nullptr) {
- if (DEBUG) {
- LOG_ALWAYS_FATAL("Cannot call registerDeviceForEpollLocked with null Device");
- }
- return BAD_VALUE;
- }
- status_t result = registerFdForEpoll(device->fd);
+status_t EventHub::registerDeviceForEpollLocked(Device& device) {
+ status_t result = registerFdForEpoll(device.fd);
if (result != OK) {
- ALOGE("Could not add input device fd to epoll for device %" PRId32, device->id);
+ ALOGE("Could not add input device fd to epoll for device %" PRId32, device.id);
return result;
}
- if (device->videoDevice) {
- registerVideoDeviceForEpollLocked(*device->videoDevice);
+ if (device.videoDevice) {
+ registerVideoDeviceForEpollLocked(*device.videoDevice);
}
return result;
}
@@ -1191,16 +1265,16 @@
}
}
-status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) {
- if (device->hasValidFd()) {
- status_t result = unregisterFdFromEpoll(device->fd);
+status_t EventHub::unregisterDeviceFromEpollLocked(Device& device) {
+ if (device.hasValidFd()) {
+ status_t result = unregisterFdFromEpoll(device.fd);
if (result != OK) {
- ALOGW("Could not remove input device fd from epoll for device %" PRId32, device->id);
+ ALOGW("Could not remove input device fd from epoll for device %" PRId32, device.id);
return result;
}
}
- if (device->videoDevice) {
- unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
+ if (device.videoDevice) {
+ unregisterVideoDeviceFromEpollLocked(*device.videoDevice);
}
return OK;
}
@@ -1215,14 +1289,14 @@
}
}
-status_t EventHub::openDeviceLocked(const char* devicePath) {
+status_t EventHub::openDeviceLocked(const std::string& devicePath) {
char buffer[80];
- ALOGV("Opening device: %s", devicePath);
+ ALOGV("Opening device: %s", devicePath.c_str());
- int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
+ int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
- ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));
return -1;
}
@@ -1230,7 +1304,7 @@
// Get device name.
if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
- ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
+ ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.name = buffer;
@@ -1240,7 +1314,7 @@
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
const std::string& item = mExcludedDevices[i];
if (identifier.name == item) {
- ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str());
+ ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str());
close(fd);
return -1;
}
@@ -1249,7 +1323,7 @@
// Get device driver version.
int driverVersion;
if (ioctl(fd, EVIOCGVERSION, &driverVersion)) {
- ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not get driver version for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
return -1;
}
@@ -1257,7 +1331,7 @@
// Get device identifier.
struct input_id inputId;
if (ioctl(fd, EVIOCGID, &inputId)) {
- ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not get device input id for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
return -1;
}
@@ -1287,9 +1361,9 @@
// Allocate device. (The device object takes ownership of the fd at this point.)
int32_t deviceId = mNextDeviceId++;
- Device* device = new Device(fd, deviceId, devicePath, identifier);
+ std::unique_ptr<Device> device = std::make_unique<Device>(fd, deviceId, devicePath, identifier);
- ALOGV("add device %d: %s\n", deviceId, devicePath);
+ ALOGV("add device %d: %s\n", deviceId, devicePath.c_str());
ALOGV(" bus: %04x\n"
" vendor %04x\n"
" product %04x\n"
@@ -1303,35 +1377,31 @@
driverVersion & 0xff);
// Load the configuration file for the device.
- loadConfigurationLocked(device);
+ device->loadConfigurationLocked();
// Figure out the kinds of events the device reports.
- ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
- ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
- ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
- ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
- ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
- ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
- ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_REL, 0), device->relBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_SW, 0), device->swBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_LED, 0), device->ledBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_FF, 0), device->ffBitmask);
+ device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
// See if this is a keyboard. Ignore everything in the button range except for
// joystick and gamepad buttons which are handled like keyboards for the most part.
bool haveKeyboardKeys =
- containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) ||
- containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_WHEEL),
- sizeof_bit_array(KEY_MAX + 1));
- bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
- sizeof_bit_array(BTN_MOUSE)) ||
- containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
- sizeof_bit_array(BTN_DIGI));
+ device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
+ bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
+ device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
if (haveKeyboardKeys || haveGamepadButtons) {
- device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+ device->classes |= InputDeviceClass::KEYBOARD;
}
// See if this is a cursor device such as a trackball or mouse.
- if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) &&
- test_bit(REL_Y, device->relBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_CURSOR;
+ if (device->keyBitmask.test(BTN_MOUSE) && device->relBitmask.test(REL_X) &&
+ device->relBitmask.test(REL_Y)) {
+ device->classes |= InputDeviceClass::CURSOR;
}
// See if this is a rotary encoder type device.
@@ -1339,43 +1409,41 @@
if (device->configuration &&
device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
if (!deviceType.compare(String8("rotaryEncoder"))) {
- device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER;
+ device->classes |= InputDeviceClass::ROTARY_ENCODER;
}
}
// See if this is a touch pad.
// Is this a new modern multi-touch driver?
- if (test_bit(ABS_MT_POSITION_X, device->absBitmask) &&
- test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
+ if (device->absBitmask.test(ABS_MT_POSITION_X) && device->absBitmask.test(ABS_MT_POSITION_Y)) {
// Some joysticks such as the PS3 controller report axes that conflict
// with the ABS_MT range. Try to confirm that the device really is
// a touch screen.
- if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
- device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
+ if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
+ device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
}
// Is this an old style single-touch driver?
- } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) &&
- test_bit(ABS_Y, device->absBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_TOUCH;
+ } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
+ device->absBitmask.test(ABS_Y)) {
+ device->classes |= InputDeviceClass::TOUCH;
// Is this a BT stylus?
- } else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||
- test_bit(BTN_TOUCH, device->keyBitmask)) &&
- !test_bit(ABS_X, device->absBitmask) && !test_bit(ABS_Y, device->absBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
+ } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
+ !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
+ device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
// Keyboard will try to claim some of the buttons but we really want to reserve those so we
// can fuse it with the touch screen data, so just take them back. Note this means an
// external stylus cannot also be a keyboard device.
- device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD;
+ device->classes &= ~InputDeviceClass::KEYBOARD;
}
// See if this device is a joystick.
// Assumes that joysticks always have gamepad buttons in order to distinguish them
// from other devices such as accelerometers that also have absolute axes.
if (haveGamepadButtons) {
- uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
+ auto assumedClasses = device->classes | InputDeviceClass::JOYSTICK;
for (int i = 0; i <= ABS_MAX; i++) {
- if (test_bit(i, device->absBitmask) &&
- (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
+ if (device->absBitmask.test(i) &&
+ (getAbsAxisUsage(i, assumedClasses).test(InputDeviceClass::JOYSTICK))) {
device->classes = assumedClasses;
break;
}
@@ -1384,88 +1452,88 @@
// Check whether this device has switches.
for (int i = 0; i <= SW_MAX; i++) {
- if (test_bit(i, device->swBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_SWITCH;
+ if (device->swBitmask.test(i)) {
+ device->classes |= InputDeviceClass::SWITCH;
break;
}
}
// Check whether this device supports the vibrator.
- if (test_bit(FF_RUMBLE, device->ffBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
+ if (device->ffBitmask.test(FF_RUMBLE)) {
+ device->classes |= InputDeviceClass::VIBRATOR;
}
// Configure virtual keys.
- if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
+ if ((device->classes.test(InputDeviceClass::TOUCH))) {
// Load the virtual keys for the touch screen, if any.
// We do this now so that we can make sure to load the keymap if necessary.
- bool success = loadVirtualKeyMapLocked(device);
+ bool success = device->loadVirtualKeyMapLocked();
if (success) {
- device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+ device->classes |= InputDeviceClass::KEYBOARD;
}
}
// Load the key map.
// We need to do this for joysticks too because the key layout may specify axes.
status_t keyMapStatus = NAME_NOT_FOUND;
- if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
+ if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK)) {
// Load the keymap for the device.
- keyMapStatus = loadKeyMapLocked(device);
+ keyMapStatus = device->loadKeyMapLocked();
}
// Configure the keyboard, gamepad or virtual keyboard.
- if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+ if (device->classes.test(InputDeviceClass::KEYBOARD)) {
// Register the keyboard as a built-in keyboard if it is eligible.
if (!keyMapStatus && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD &&
- isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) {
+ isEligibleBuiltInKeyboard(device->identifier, device->configuration.get(),
+ &device->keyMap)) {
mBuiltInKeyboardId = device->id;
}
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycodeLocked(device, AKEYCODE_Q)) {
- device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
+ if (device->hasKeycodeLocked(AKEYCODE_Q)) {
+ device->classes |= InputDeviceClass::ALPHAKEY;
}
// See if this device has a DPAD.
- if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
- device->classes |= INPUT_DEVICE_CLASS_DPAD;
+ if (device->hasKeycodeLocked(AKEYCODE_DPAD_UP) &&
+ device->hasKeycodeLocked(AKEYCODE_DPAD_DOWN) &&
+ device->hasKeycodeLocked(AKEYCODE_DPAD_LEFT) &&
+ device->hasKeycodeLocked(AKEYCODE_DPAD_RIGHT) &&
+ device->hasKeycodeLocked(AKEYCODE_DPAD_CENTER)) {
+ device->classes |= InputDeviceClass::DPAD;
}
// See if this device has a gamepad.
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES) / sizeof(GAMEPAD_KEYCODES[0]); i++) {
- if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
- device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
+ if (device->hasKeycodeLocked(GAMEPAD_KEYCODES[i])) {
+ device->classes |= InputDeviceClass::GAMEPAD;
break;
}
}
}
// If the device isn't recognized as something we handle, don't monitor it.
- if (device->classes == 0) {
- ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath,
+ if (device->classes == Flags<InputDeviceClass>(0)) {
+ ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath.c_str(),
device->identifier.name.c_str());
- delete device;
return -1;
}
// Determine whether the device has a mic.
- if (deviceHasMicLocked(device)) {
- device->classes |= INPUT_DEVICE_CLASS_MIC;
+ if (device->deviceHasMicLocked()) {
+ device->classes |= InputDeviceClass::MIC;
}
// Determine whether the device is external or internal.
- if (isExternalDeviceLocked(device)) {
- device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
+ if (device->isExternalDeviceLocked()) {
+ device->classes |= InputDeviceClass::EXTERNAL;
}
- if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD) &&
- device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
- device->controllerNumber = getNextControllerNumberLocked(device);
- setLedForControllerLocked(device);
+ if (device->classes.any(InputDeviceClass::JOYSTICK | InputDeviceClass::DPAD) &&
+ device->classes.test(InputDeviceClass::GAMEPAD)) {
+ device->controllerNumber = getNextControllerNumberLocked(device->identifier.name);
+ device->setLedForControllerLocked();
}
// Find a matching video device by comparing device names
@@ -1483,43 +1551,23 @@
}),
mUnattachedVideoDevices.end());
- if (registerDeviceForEpollLocked(device) != OK) {
- delete device;
+ if (registerDeviceForEpollLocked(*device) != OK) {
return -1;
}
- configureFd(device);
+ device->configureFd();
- ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
+ ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=%s, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
- deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes,
- device->configurationFile.c_str(), device->keyMap.keyLayoutFile.c_str(),
- device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId));
+ deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(),
+ device->classes.string().c_str(), device->configurationFile.c_str(),
+ device->keyMap.keyLayoutFile.c_str(), device->keyMap.keyCharacterMapFile.c_str(),
+ toString(mBuiltInKeyboardId == deviceId));
- addDeviceLocked(device);
+ addDeviceLocked(std::move(device));
return OK;
}
-void EventHub::configureFd(Device* device) {
- // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type
- if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
- // Disable kernel key repeat since we handle it ourselves
- unsigned int repeatRate[] = {0, 0};
- if (ioctl(device->fd, EVIOCSREP, repeatRate)) {
- ALOGW("Unable to disable kernel key repeat for %s: %s", device->path.c_str(),
- strerror(errno));
- }
- }
-
- // Tell the kernel that we want to use the monotonic clock for reporting timestamps
- // associated with input events. This is important because the input system
- // uses the timestamps extensively and assumes they were recorded using the monotonic
- // clock.
- int clockId = CLOCK_MONOTONIC;
- bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId);
- ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
-}
-
void EventHub::openVideoDeviceLocked(const std::string& devicePath) {
std::unique_ptr<TouchVideoDevice> videoDevice = TouchVideoDevice::create(devicePath);
if (!videoDevice) {
@@ -1527,8 +1575,7 @@
return;
}
// Transfer ownership of this video device to a matching input device
- for (size_t i = 0; i < mDevices.size(); i++) {
- Device* device = mDevices.valueAt(i);
+ for (const auto& [id, device] : mDevices) {
if (videoDevice->getName() == device->identifier.name) {
device->videoDevice = std::move(videoDevice);
if (device->enabled) {
@@ -1572,9 +1619,9 @@
return result;
}
- configureFd(device);
+ device->configureFd();
- return registerDeviceForEpollLocked(device);
+ return registerDeviceForEpollLocked(*device);
}
status_t EventHub::disableDevice(int32_t deviceId) {
@@ -1588,7 +1635,7 @@
ALOGW("Duplicate call to %s, input device already disabled", __func__);
return OK;
}
- unregisterDeviceFromEpollLocked(device);
+ unregisterDeviceFromEpollLocked(*device);
return device->disable();
}
@@ -1598,77 +1645,23 @@
identifier.uniqueId = "<virtual>";
assignDescriptorLocked(identifier);
- Device* device =
- new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>", identifier);
- device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY |
- INPUT_DEVICE_CLASS_DPAD | INPUT_DEVICE_CLASS_VIRTUAL;
- loadKeyMapLocked(device);
- addDeviceLocked(device);
+ std::unique_ptr<Device> device =
+ std::make_unique<Device>(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>",
+ identifier);
+ device->classes = InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY |
+ InputDeviceClass::DPAD | InputDeviceClass::VIRTUAL;
+ device->loadKeyMapLocked();
+ addDeviceLocked(std::move(device));
}
-void EventHub::addDeviceLocked(Device* device) {
- mDevices.add(device->id, device);
- device->next = mOpeningDevices;
- mOpeningDevices = device;
+void EventHub::addDeviceLocked(std::unique_ptr<Device> device) {
+ mOpeningDevices.push_back(std::move(device));
}
-void EventHub::loadConfigurationLocked(Device* device) {
- device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
- device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
- if (device->configurationFile.empty()) {
- ALOGD("No input device configuration file found for device '%s'.",
- device->identifier.name.c_str());
- } else {
- status_t status = PropertyMap::load(String8(device->configurationFile.c_str()),
- &device->configuration);
- if (status) {
- ALOGE("Error loading input device configuration file for device '%s'. "
- "Using default configuration.",
- device->identifier.name.c_str());
- }
- }
-}
-
-bool EventHub::loadVirtualKeyMapLocked(Device* device) {
- // The virtual key map is supplied by the kernel as a system board property file.
- std::string path;
- path += "/sys/board_properties/virtualkeys.";
- path += device->identifier.getCanonicalName();
- if (access(path.c_str(), R_OK)) {
- return false;
- }
- device->virtualKeyMap = VirtualKeyMap::load(path);
- return device->virtualKeyMap != nullptr;
-}
-
-status_t EventHub::loadKeyMapLocked(Device* device) {
- return device->keyMap.load(device->identifier, device->configuration);
-}
-
-bool EventHub::isExternalDeviceLocked(Device* device) {
- if (device->configuration) {
- bool value;
- if (device->configuration->tryGetProperty(String8("device.internal"), value)) {
- return !value;
- }
- }
- return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH;
-}
-
-bool EventHub::deviceHasMicLocked(Device* device) {
- if (device->configuration) {
- bool value;
- if (device->configuration->tryGetProperty(String8("audio.mic"), value)) {
- return value;
- }
- }
- return false;
-}
-
-int32_t EventHub::getNextControllerNumberLocked(Device* device) {
+int32_t EventHub::getNextControllerNumberLocked(const std::string& name) {
if (mControllerNumbers.isFull()) {
ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s",
- device->identifier.name.c_str());
+ name.c_str());
return 0;
}
// Since the controller number 0 is reserved for non-controllers, translate all numbers up by
@@ -1676,61 +1669,19 @@
return static_cast<int32_t>(mControllerNumbers.markFirstUnmarkedBit() + 1);
}
-void EventHub::releaseControllerNumberLocked(Device* device) {
- int32_t num = device->controllerNumber;
- device->controllerNumber = 0;
- if (num == 0) {
- return;
- }
- mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1));
-}
-
-void EventHub::setLedForControllerLocked(Device* device) {
- for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) {
- setLedStateLocked(device, ALED_CONTROLLER_1 + i, device->controllerNumber == i + 1);
+void EventHub::releaseControllerNumberLocked(int32_t num) {
+ if (num > 0) {
+ mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1));
}
}
-bool EventHub::hasKeycodeLocked(Device* device, int keycode) const {
- if (!device->keyMap.haveKeyLayout()) {
- return false;
- }
-
- std::vector<int32_t> scanCodes;
- device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
- const size_t N = scanCodes.size();
- for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
- int32_t sc = scanCodes[i];
- if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) {
- return true;
- }
- }
-
- return false;
-}
-
-status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) const {
- if (!device->keyMap.haveKeyLayout()) {
- return NAME_NOT_FOUND;
- }
-
- int32_t scanCode;
- if (device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
- if (scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) {
- *outScanCode = scanCode;
- return NO_ERROR;
- }
- }
- return NAME_NOT_FOUND;
-}
-
-void EventHub::closeDeviceByPathLocked(const char* devicePath) {
+void EventHub::closeDeviceByPathLocked(const std::string& devicePath) {
Device* device = getDeviceByPathLocked(devicePath);
- if (device) {
- closeDeviceLocked(device);
+ if (device != nullptr) {
+ closeDeviceLocked(*device);
return;
}
- ALOGV("Remove device: %s not found, device may already have been removed.", devicePath);
+ ALOGV("Remove device: %s not found, device may already have been removed.", devicePath.c_str());
}
/**
@@ -1741,8 +1692,7 @@
void EventHub::closeVideoDeviceByPathLocked(const std::string& devicePath) {
// A video device may be owned by an existing input device, or it may be stored in
// the mUnattachedVideoDevices queue. Check both locations.
- for (size_t i = 0; i < mDevices.size(); i++) {
- Device* device = mDevices.valueAt(i);
+ for (const auto& [id, device] : mDevices) {
if (device->videoDevice && device->videoDevice->getPath() == devicePath) {
unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
device->videoDevice = nullptr;
@@ -1760,60 +1710,33 @@
void EventHub::closeAllDevicesLocked() {
mUnattachedVideoDevices.clear();
- while (mDevices.size() > 0) {
- closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1));
+ while (!mDevices.empty()) {
+ closeDeviceLocked(*(mDevices.begin()->second));
}
}
-void EventHub::closeDeviceLocked(Device* device) {
- ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", device->path.c_str(),
- device->identifier.name.c_str(), device->id, device->fd, device->classes);
+void EventHub::closeDeviceLocked(Device& device) {
+ ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=%s", device.path.c_str(),
+ device.identifier.name.c_str(), device.id, device.fd, device.classes.string().c_str());
- if (device->id == mBuiltInKeyboardId) {
+ if (device.id == mBuiltInKeyboardId) {
ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
- device->path.c_str(), mBuiltInKeyboardId);
+ device.path.c_str(), mBuiltInKeyboardId);
mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
}
unregisterDeviceFromEpollLocked(device);
- if (device->videoDevice) {
+ if (device.videoDevice) {
// This must be done after the video device is removed from epoll
- mUnattachedVideoDevices.push_back(std::move(device->videoDevice));
+ mUnattachedVideoDevices.push_back(std::move(device.videoDevice));
}
- releaseControllerNumberLocked(device);
+ releaseControllerNumberLocked(device.controllerNumber);
+ device.controllerNumber = 0;
+ device.close();
+ mClosingDevices.push_back(std::move(mDevices[device.id]));
- mDevices.removeItem(device->id);
- device->close();
-
- // Unlink for opening devices list if it is present.
- Device* pred = nullptr;
- bool found = false;
- for (Device* entry = mOpeningDevices; entry != nullptr;) {
- if (entry == device) {
- found = true;
- break;
- }
- pred = entry;
- entry = entry->next;
- }
- if (found) {
- // Unlink the device from the opening devices list then delete it.
- // We don't need to tell the client that the device was closed because
- // it does not even know it was opened in the first place.
- ALOGI("Device %s was immediately closed after opening.", device->path.c_str());
- if (pred) {
- pred->next = device->next;
- } else {
- mOpeningDevices = device->next;
- }
- delete device;
- } else {
- // Link into closing devices list.
- // The device will be deleted later after we have informed the client.
- device->next = mClosingDevices;
- mClosingDevices = device;
- }
+ mDevices.erase(device.id);
}
status_t EventHub::readNotifyLocked() {
@@ -1835,16 +1758,16 @@
event = (struct inotify_event*)(event_buf + event_pos);
if (event->len) {
if (event->wd == mInputWd) {
- std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
+ std::string filename = std::string(DEVICE_PATH) + "/" + event->name;
if (event->mask & IN_CREATE) {
- openDeviceLocked(filename.c_str());
+ openDeviceLocked(filename);
} else {
ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
- closeDeviceByPathLocked(filename.c_str());
+ closeDeviceByPathLocked(filename);
}
} else if (event->wd == mVideoWd) {
if (isV4lTouchNode(event->name)) {
- std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
+ std::string filename = std::string(VIDEO_DEVICE_PATH) + "/" + event->name;
if (event->mask & IN_CREATE) {
openVideoDeviceLocked(filename);
} else {
@@ -1863,24 +1786,10 @@
return 0;
}
-status_t EventHub::scanDirLocked(const char* dirname) {
- char devname[PATH_MAX];
- char* filename;
- DIR* dir;
- struct dirent* de;
- dir = opendir(dirname);
- if (dir == nullptr) return -1;
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = '/';
- while ((de = readdir(dir))) {
- if (de->d_name[0] == '.' &&
- (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
- continue;
- strcpy(filename, de->d_name);
- openDeviceLocked(devname);
+status_t EventHub::scanDirLocked(const std::string& dirname) {
+ for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
+ openDeviceLocked(entry.path());
}
- closedir(dir);
return 0;
}
@@ -1888,22 +1797,12 @@
* Look for all dirname/v4l-touch* devices, and open them.
*/
status_t EventHub::scanVideoDirLocked(const std::string& dirname) {
- DIR* dir;
- struct dirent* de;
- dir = opendir(dirname.c_str());
- if (!dir) {
- ALOGE("Could not open video directory %s", dirname.c_str());
- return BAD_VALUE;
- }
-
- while ((de = readdir(dir))) {
- const char* name = de->d_name;
- if (isV4lTouchNode(name)) {
- ALOGI("Found touch video device %s", name);
- openVideoDeviceLocked(dirname + "/" + name);
+ for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
+ if (isV4lTouchNode(entry.path())) {
+ ALOGI("Found touch video device %s", entry.path().c_str());
+ openVideoDeviceLocked(entry.path());
}
}
- closedir(dir);
return OK;
}
@@ -1924,8 +1823,7 @@
dump += INDENT "Devices:\n";
- for (size_t i = 0; i < mDevices.size(); i++) {
- const Device* device = mDevices.valueAt(i);
+ for (const auto& [id, device] : mDevices) {
if (mBuiltInKeyboardId == device->id) {
dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
device->id, device->identifier.name.c_str());
@@ -1933,7 +1831,7 @@
dump += StringPrintf(INDENT2 "%d: %s\n", device->id,
device->identifier.name.c_str());
}
- dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes);
+ dump += StringPrintf(INDENT3 "Classes: %s\n", device->classes.string().c_str());
dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str());
dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(device->enabled));
dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.c_str());
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 4b19e5e..14f922f 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -18,6 +18,7 @@
#include "InputDevice.h"
+#include <input/Flags.h>
#include <algorithm>
#include "CursorInputMapper.h"
@@ -131,7 +132,7 @@
return;
}
std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
- uint32_t classes = contextPtr->getDeviceClasses();
+ Flags<InputDeviceClass> classes = contextPtr->getDeviceClasses();
std::vector<std::unique_ptr<InputMapper>> mappers;
// Check if we should skip population
@@ -141,33 +142,33 @@
}
// Switch-like devices.
- if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+ if (classes.test(InputDeviceClass::SWITCH)) {
mappers.push_back(std::make_unique<SwitchInputMapper>(*contextPtr));
}
// Scroll wheel-like devices.
- if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
+ if (classes.test(InputDeviceClass::ROTARY_ENCODER)) {
mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(*contextPtr));
}
// Vibrator-like devices.
- if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+ if (classes.test(InputDeviceClass::VIBRATOR)) {
mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr));
}
// Keyboard-like devices.
uint32_t keyboardSource = 0;
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
- if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+ if (classes.test(InputDeviceClass::KEYBOARD)) {
keyboardSource |= AINPUT_SOURCE_KEYBOARD;
}
- if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+ if (classes.test(InputDeviceClass::ALPHAKEY)) {
keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
}
- if (classes & INPUT_DEVICE_CLASS_DPAD) {
+ if (classes.test(InputDeviceClass::DPAD)) {
keyboardSource |= AINPUT_SOURCE_DPAD;
}
- if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+ if (classes.test(InputDeviceClass::GAMEPAD)) {
keyboardSource |= AINPUT_SOURCE_GAMEPAD;
}
@@ -177,24 +178,24 @@
}
// Cursor-like devices.
- if (classes & INPUT_DEVICE_CLASS_CURSOR) {
+ if (classes.test(InputDeviceClass::CURSOR)) {
mappers.push_back(std::make_unique<CursorInputMapper>(*contextPtr));
}
// Touchscreens and touchpad devices.
- if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
+ if (classes.test(InputDeviceClass::TOUCH_MT)) {
mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
- } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
+ } else if (classes.test(InputDeviceClass::TOUCH)) {
mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
}
// Joystick-like devices.
- if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
+ if (classes.test(InputDeviceClass::JOYSTICK)) {
mappers.push_back(std::make_unique<JoystickInputMapper>(*contextPtr));
}
// External stylus-like devices.
- if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+ if (classes.test(InputDeviceClass::EXTERNAL_STYLUS)) {
mappers.push_back(std::make_unique<ExternalStylusInputMapper>(*contextPtr));
}
@@ -209,7 +210,7 @@
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
uint32_t changes) {
mSources = 0;
- mClasses = 0;
+ mClasses = Flags<InputDeviceClass>(0);
mControllerNumber = 0;
for_each_subdevice([this](InputDeviceContext& context) {
@@ -224,8 +225,8 @@
}
});
- mIsExternal = !!(mClasses & INPUT_DEVICE_CLASS_EXTERNAL);
- mHasMic = !!(mClasses & INPUT_DEVICE_CLASS_MIC);
+ mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
+ mHasMic = mClasses.test(InputDeviceClass::MIC);
if (!isIgnored()) {
if (!changes) { // first time only
@@ -238,7 +239,7 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
- if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
+ if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
sp<KeyCharacterMap> keyboardLayout =
mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
bool shouldBumpGeneration = false;
@@ -255,7 +256,7 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
- if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
+ if (!(mClasses.test(InputDeviceClass::VIRTUAL))) {
std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
if (mAlias != alias) {
mAlias = alias;
@@ -424,10 +425,10 @@
return result;
}
-void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+void InputDevice::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
int32_t token) {
- for_each_mapper([pattern, patternSize, repeat, token](InputMapper& mapper) {
- mapper.vibrate(pattern, patternSize, repeat, token);
+ for_each_mapper([pattern, repeat, token](InputMapper& mapper) {
+ mapper.vibrate(pattern, repeat, token);
});
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 06e3743..bb1ccbf 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -208,7 +208,7 @@
mDevices.emplace(eventHubId, device);
bumpGenerationLocked();
- if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+ if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
notifyExternalStylusPresenceChanged();
}
}
@@ -237,7 +237,7 @@
device->removeEventHubDevice(eventHubId);
- if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+ if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
notifyExternalStylusPresenceChanged();
}
@@ -360,7 +360,7 @@
void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
- if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
+ if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS) && !device->isIgnored()) {
InputDeviceInfo info;
device->getDeviceInfo(&info);
outDevices.push_back(info);
@@ -559,12 +559,12 @@
}
}
-void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+void InputReader::vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
ssize_t repeat, int32_t token) {
AutoMutex _l(mLock);
InputDevice* device = findInputDevice(deviceId);
if (device) {
- device->vibrate(pattern, patternSize, repeat, token);
+ device->vibrate(pattern, repeat, token);
}
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index c17f3a1..ff12d98 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -17,14 +17,20 @@
#ifndef _RUNTIME_EVENT_HUB_H
#define _RUNTIME_EVENT_HUB_H
+#include <bitset>
+#include <climits>
+#include <unordered_map>
#include <vector>
+#include <input/Flags.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
+#include <linux/input.h>
+#include <sys/epoll.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
@@ -33,15 +39,8 @@
#include <utils/Mutex.h>
#include <utils/PropertyMap.h>
-#include <linux/input.h>
-#include <sys/epoll.h>
-
#include "TouchVideoDevice.h"
-
-/* Convenience constants. */
-
-#define BTN_FIRST 0x100 // first button code
-#define BTN_LAST 0x15f // last button code
+#include "VibrationElement.h"
namespace android {
@@ -79,58 +78,58 @@
/*
* Input device classes.
*/
-enum {
+enum class InputDeviceClass : uint32_t {
/* The input device is a keyboard or has buttons. */
- INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
+ KEYBOARD = 0x00000001,
/* The input device is an alpha-numeric keyboard (not just a dial pad). */
- INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
+ ALPHAKEY = 0x00000002,
/* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
- INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
+ TOUCH = 0x00000004,
/* The input device is a cursor device such as a trackball or mouse. */
- INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
+ CURSOR = 0x00000008,
/* The input device is a multi-touch touchscreen. */
- INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
+ TOUCH_MT = 0x00000010,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
- INPUT_DEVICE_CLASS_DPAD = 0x00000020,
+ DPAD = 0x00000020,
/* The input device is a gamepad (implies keyboard, has BUTTON keys). */
- INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
+ GAMEPAD = 0x00000040,
/* The input device has switches. */
- INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
+ SWITCH = 0x00000080,
/* The input device is a joystick (implies gamepad, has joystick absolute axes). */
- INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
+ JOYSTICK = 0x00000100,
/* The input device has a vibrator (supports FF_RUMBLE). */
- INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
+ VIBRATOR = 0x00000200,
/* The input device has a microphone. */
- INPUT_DEVICE_CLASS_MIC = 0x00000400,
+ MIC = 0x00000400,
/* The input device is an external stylus (has data we want to fuse with touch data). */
- INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800,
+ EXTERNAL_STYLUS = 0x00000800,
/* The input device has a rotary encoder */
- INPUT_DEVICE_CLASS_ROTARY_ENCODER = 0x00001000,
+ ROTARY_ENCODER = 0x00001000,
/* The input device is virtual (not a real device, not part of UI configuration). */
- INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
+ VIRTUAL = 0x40000000,
/* The input device is external (not built-in). */
- INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
+ EXTERNAL = 0x80000000,
};
/*
* Gets the class that owns an axis, in cases where multiple classes might claim
* the same axis for different purposes.
*/
-extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses);
+extern Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, Flags<InputDeviceClass> deviceClasses);
/*
* Grand Central Station for events.
@@ -163,7 +162,7 @@
FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
};
- virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
+ virtual Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const = 0;
virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const = 0;
@@ -231,7 +230,7 @@
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
/* Control the vibrator. */
- virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
+ virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0;
virtual void cancelVibrate(int32_t deviceId) = 0;
/* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
@@ -256,73 +255,148 @@
virtual status_t disableDevice(int32_t deviceId) = 0;
};
+template <std::size_t BITS>
+class BitArray {
+ /* Array element type and vector of element type. */
+ using Element = std::uint32_t;
+ /* Number of bits in each BitArray element. */
+ static constexpr size_t WIDTH = sizeof(Element) * CHAR_BIT;
+ /* Number of elements to represent a bit array of the specified size of bits. */
+ static constexpr size_t COUNT = (BITS + WIDTH - 1) / WIDTH;
+
+public:
+ /* BUFFER type declaration for BitArray */
+ using Buffer = std::array<Element, COUNT>;
+ /* To tell if a bit is set in array, it selects an element from the array, and test
+ * if the relevant bit set.
+ * Note the parameter "bit" is an index to the bit, 0 <= bit < BITS.
+ */
+ inline bool test(size_t bit) const {
+ return (bit < BITS) ? mData[bit / WIDTH].test(bit % WIDTH) : false;
+ }
+ /* Returns total number of bytes needed for the array */
+ inline size_t bytes() { return (BITS + CHAR_BIT - 1) / CHAR_BIT; }
+ /* Returns true if array contains any non-zero bit from the range defined by start and end
+ * bit index [startIndex, endIndex).
+ */
+ bool any(size_t startIndex, size_t endIndex) {
+ if (startIndex >= endIndex || startIndex > BITS || endIndex > BITS + 1) {
+ ALOGE("Invalid start/end index. start = %zu, end = %zu, total bits = %zu", startIndex,
+ endIndex, BITS);
+ return false;
+ }
+ size_t se = startIndex / WIDTH; // Start of element
+ size_t ee = endIndex / WIDTH; // End of element
+ size_t si = startIndex % WIDTH; // Start index in start element
+ size_t ei = endIndex % WIDTH; // End index in end element
+ // Need to check first unaligned bitset for any non zero bit
+ if (si > 0) {
+ size_t nBits = se == ee ? ei - si : WIDTH - si;
+ // Generate the mask of interested bit range
+ Element mask = ((1 << nBits) - 1) << si;
+ if (mData[se++].to_ulong() & mask) {
+ return true;
+ }
+ }
+ // Check whole bitset for any bit set
+ for (; se < ee; se++) {
+ if (mData[se].any()) {
+ return true;
+ }
+ }
+ // Need to check last unaligned bitset for any non zero bit
+ if (ei > 0 && se <= ee) {
+ // Generate the mask of interested bit range
+ Element mask = (1 << ei) - 1;
+ if (mData[se].to_ulong() & mask) {
+ return true;
+ }
+ }
+ return false;
+ }
+ /* Load bit array values from buffer */
+ void loadFromBuffer(const Buffer& buffer) {
+ for (size_t i = 0; i < COUNT; i++) {
+ mData[i] = std::bitset<WIDTH>(buffer[i]);
+ }
+ }
+
+private:
+ std::array<std::bitset<WIDTH>, COUNT> mData;
+};
+
class EventHub : public EventHubInterface {
public:
EventHub();
- virtual uint32_t getDeviceClasses(int32_t deviceId) const override;
+ Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override final;
- virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
+ InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override final;
- virtual int32_t getDeviceControllerNumber(int32_t deviceId) const override;
+ int32_t getDeviceControllerNumber(int32_t deviceId) const override final;
- virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
+ void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override final;
- virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override;
+ status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const override final;
- virtual bool hasRelativeAxis(int32_t deviceId, int axis) const override;
+ bool hasRelativeAxis(int32_t deviceId, int axis) const override final;
- virtual bool hasInputProperty(int32_t deviceId, int property) const override;
+ bool hasInputProperty(int32_t deviceId, int property) const override final;
- 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 override;
+ status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState,
+ uint32_t* outFlags) const override final;
- virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
- AxisInfo* outAxisInfo) const override;
+ status_t mapAxis(int32_t deviceId, int32_t scanCode,
+ AxisInfo* outAxisInfo) const override final;
- virtual void setExcludedDevices(const std::vector<std::string>& devices) override;
+ void setExcludedDevices(const std::vector<std::string>& devices) override final;
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
- virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
- virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const override;
+ int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final;
+ int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override final;
+ int32_t getSwitchState(int32_t deviceId, int32_t sw) const override final;
+ status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+ int32_t* outValue) const override final;
- virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
- uint8_t* outFlags) const override;
+ bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) const override final;
- virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override;
- virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
+ size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override final;
+ std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final;
- virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const override;
- virtual bool hasLed(int32_t deviceId, int32_t led) const override;
- virtual void setLedState(int32_t deviceId, int32_t led, bool on) override;
+ bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final;
+ bool hasLed(int32_t deviceId, int32_t led) const override final;
+ void setLedState(int32_t deviceId, int32_t led, bool on) override final;
- virtual void getVirtualKeyDefinitions(
- int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override;
+ void getVirtualKeyDefinitions(
+ int32_t deviceId,
+ std::vector<VirtualKeyDefinition>& outVirtualKeys) const override final;
- virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override;
- virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
- const sp<KeyCharacterMap>& map) override;
+ sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override final;
+ bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) override final;
- virtual void vibrate(int32_t deviceId, nsecs_t duration) override;
- virtual void cancelVibrate(int32_t deviceId) override;
+ void vibrate(int32_t deviceId, const VibrationElement& effect) override final;
+ void cancelVibrate(int32_t deviceId) override final;
- virtual void requestReopenDevices() override;
+ void requestReopenDevices() override final;
- virtual void wake() override;
+ void wake() override final;
- virtual void dump(std::string& dump) override;
- virtual void monitor() override;
+ void dump(std::string& dump) override final;
- virtual ~EventHub() override;
+ void monitor() override final;
+
+ bool isDeviceEnabled(int32_t deviceId) override final;
+
+ status_t enableDevice(int32_t deviceId) override final;
+
+ status_t disableDevice(int32_t deviceId) override final;
+
+ ~EventHub() override;
private:
struct Device {
- Device* next;
-
int fd; // may be -1 if device is closed
const int32_t id;
const std::string path;
@@ -330,18 +404,20 @@
std::unique_ptr<TouchVideoDevice> videoDevice;
- uint32_t classes;
+ Flags<InputDeviceClass> classes;
- uint8_t keyBitmask[(KEY_MAX + 1) / 8];
- uint8_t absBitmask[(ABS_MAX + 1) / 8];
- uint8_t relBitmask[(REL_MAX + 1) / 8];
- uint8_t swBitmask[(SW_MAX + 1) / 8];
- uint8_t ledBitmask[(LED_MAX + 1) / 8];
- uint8_t ffBitmask[(FF_MAX + 1) / 8];
- uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
+ BitArray<KEY_MAX> keyBitmask;
+ BitArray<KEY_MAX> keyState;
+ BitArray<ABS_MAX> absBitmask;
+ BitArray<REL_MAX> relBitmask;
+ BitArray<SW_MAX> swBitmask;
+ BitArray<SW_MAX> swState;
+ BitArray<LED_MAX> ledBitmask;
+ BitArray<FF_MAX> ffBitmask;
+ BitArray<INPUT_PROP_MAX> propBitmask;
std::string configurationFile;
- PropertyMap* configuration;
+ std::unique_ptr<PropertyMap> configuration;
std::unique_ptr<VirtualKeyMap> virtualKeyMap;
KeyMap keyMap;
@@ -362,69 +438,60 @@
bool enabled; // initially true
status_t enable();
status_t disable();
- bool hasValidFd();
+ bool hasValidFd() const;
const bool isVirtual; // set if fd < 0 is passed to constructor
- const sp<KeyCharacterMap>& getKeyCharacterMap() const {
- if (combinedKeyMap != nullptr) {
- return combinedKeyMap;
- }
- return keyMap.keyCharacterMap;
- }
+ const sp<KeyCharacterMap>& getKeyCharacterMap() const;
+
+ template <std::size_t N>
+ status_t readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray);
+
+ void configureFd();
+ bool hasKeycodeLocked(int keycode) const;
+ void loadConfigurationLocked();
+ bool loadVirtualKeyMapLocked();
+ status_t loadKeyMapLocked();
+ bool isExternalDeviceLocked();
+ bool deviceHasMicLocked();
+ void setLedForControllerLocked();
+ status_t mapLed(int32_t led, int32_t* outScanCode) const;
+ void setLedStateLocked(int32_t led, bool on);
};
- status_t openDeviceLocked(const char* devicePath);
+ status_t openDeviceLocked(const std::string& devicePath);
void openVideoDeviceLocked(const std::string& devicePath);
void createVirtualKeyboardLocked();
- void addDeviceLocked(Device* device);
+ void addDeviceLocked(std::unique_ptr<Device> device);
void assignDescriptorLocked(InputDeviceIdentifier& identifier);
- void closeDeviceByPathLocked(const char* devicePath);
+ void closeDeviceByPathLocked(const std::string& devicePath);
void closeVideoDeviceByPathLocked(const std::string& devicePath);
- void closeDeviceLocked(Device* device);
+ void closeDeviceLocked(Device& device);
void closeAllDevicesLocked();
- void configureFd(Device* device);
-
- bool isDeviceEnabled(int32_t deviceId) override;
- status_t enableDevice(int32_t deviceId) override;
- status_t disableDevice(int32_t deviceId) override;
status_t registerFdForEpoll(int fd);
status_t unregisterFdFromEpoll(int fd);
- status_t registerDeviceForEpollLocked(Device* device);
+ status_t registerDeviceForEpollLocked(Device& device);
void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice);
- status_t unregisterDeviceFromEpollLocked(Device* device);
+ status_t unregisterDeviceFromEpollLocked(Device& device);
void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
- status_t scanDirLocked(const char* dirname);
+ status_t scanDirLocked(const std::string& dirname);
status_t scanVideoDirLocked(const std::string& dirname);
void scanDevicesLocked();
status_t readNotifyLocked();
Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
Device* getDeviceLocked(int32_t deviceId) const;
- Device* getDeviceByPathLocked(const char* devicePath) const;
+ Device* getDeviceByPathLocked(const std::string& devicePath) const;
/**
* Look through all available fd's (both for input devices and for video devices),
* and return the device pointer.
*/
Device* getDeviceByFdLocked(int fd) const;
- bool hasKeycodeLocked(Device* device, int keycode) const;
-
- void loadConfigurationLocked(Device* device);
- bool loadVirtualKeyMapLocked(Device* device);
- status_t loadKeyMapLocked(Device* device);
-
- bool isExternalDeviceLocked(Device* device);
- bool deviceHasMicLocked(Device* device);
-
- int32_t getNextControllerNumberLocked(Device* device);
- void releaseControllerNumberLocked(Device* device);
- void setLedForControllerLocked(Device* device);
-
- status_t mapLed(Device* device, int32_t led, int32_t* outScanCode) const;
- void setLedStateLocked(Device* device, int32_t led, bool on);
+ int32_t getNextControllerNumberLocked(const std::string& name);
+ void releaseControllerNumberLocked(int32_t num);
// Protect all internal state.
mutable Mutex mLock;
@@ -442,7 +509,7 @@
BitSet32 mControllerNumbers;
- KeyedVector<int32_t, Device*> mDevices;
+ std::unordered_map<int32_t, std::unique_ptr<Device>> mDevices;
/**
* Video devices that report touchscreen heatmap, but have not (yet) been paired
* with a specific input device. Video device discovery is independent from input device
@@ -452,8 +519,8 @@
*/
std::vector<std::unique_ptr<TouchVideoDevice>> mUnattachedVideoDevices;
- Device* mOpeningDevices;
- Device* mClosingDevices;
+ std::vector<std::unique_ptr<Device>> mOpeningDevices;
+ std::vector<std::unique_ptr<Device>> mClosingDevices;
bool mNeedToSendFinishedDeviceScan;
bool mNeedToReopenDevices;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 71313fc..da36a48 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -18,6 +18,7 @@
#define _UI_INPUTREADER_INPUT_DEVICE_H
#include <input/DisplayViewport.h>
+#include <input/Flags.h>
#include <input/InputDevice.h>
#include <stdint.h>
#include <utils/PropertyMap.h>
@@ -48,7 +49,7 @@
inline int32_t getGeneration() const { return mGeneration; }
inline const std::string getName() const { return mIdentifier.name; }
inline const std::string getDescriptor() { return mIdentifier.descriptor; }
- inline uint32_t getClasses() const { return mClasses; }
+ inline Flags<InputDeviceClass> getClasses() const { return mClasses; }
inline uint32_t getSources() const { return mSources; }
inline bool hasEventHubDevices() const { return !mDevices.empty(); }
@@ -81,7 +82,7 @@
int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags);
- void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+ void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token);
void cancelVibrate(int32_t token);
void cancelTouch(nsecs_t when);
@@ -121,7 +122,7 @@
int32_t mControllerNumber;
InputDeviceIdentifier mIdentifier;
std::string mAlias;
- uint32_t mClasses;
+ Flags<InputDeviceClass> mClasses;
// map from eventHubId to device context and mappers
using MapperVector = std::vector<std::unique_ptr<InputMapper>>;
@@ -206,7 +207,9 @@
inline int32_t getId() { return mDeviceId; }
inline int32_t getEventHubId() { return mId; }
- inline uint32_t getDeviceClasses() const { return mEventHub->getDeviceClasses(mId); }
+ inline Flags<InputDeviceClass> getDeviceClasses() const {
+ return mEventHub->getDeviceClasses(mId);
+ }
inline InputDeviceIdentifier getDeviceIdentifier() const {
return mEventHub->getDeviceIdentifier(mId);
}
@@ -262,7 +265,9 @@
inline bool setKeyboardLayoutOverlay(const sp<KeyCharacterMap>& map) {
return mEventHub->setKeyboardLayoutOverlay(mId, map);
}
- inline void vibrate(nsecs_t duration) { return mEventHub->vibrate(mId, duration); }
+ inline void vibrate(const VibrationElement& element) {
+ return mEventHub->vibrate(mId, element);
+ }
inline void cancelVibrate() { return mEventHub->cancelVibrate(mId); }
inline bool hasAbsoluteAxis(int32_t code) const {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 108b9c2..9cb2052 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -78,7 +78,7 @@
virtual void requestRefreshConfiguration(uint32_t changes) override;
- virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+ 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;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 1a4d551..254b64b 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "CursorInputMapper.h"
@@ -80,6 +82,10 @@
} else {
info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -1.0f, 1.0f, 0.0f, mXScale,
+ 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale,
+ 0.0f);
}
info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
@@ -184,7 +190,7 @@
mOrientation = DISPLAY_ORIENTATION_0;
if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ config->getDisplayViewportByType(ViewportType::INTERNAL);
if (internalViewport) {
mOrientation = internalViewport->orientation;
}
@@ -312,7 +318,7 @@
mPointerVelocityControl.move(when, &deltaX, &deltaY);
- int32_t displayId;
+ int32_t displayId = ADISPLAY_ID_NONE;
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mSource == AINPUT_SOURCE_MOUSE) {
@@ -336,10 +342,12 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
displayId = mPointerController->getDisplayId();
- } else {
+ } else if (mSource == AINPUT_SOURCE_MOUSE_RELATIVE) {
+ // Pointer capture mode
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
- displayId = ADISPLAY_ID_NONE;
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
}
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index a8fe39a..1db829f 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -56,7 +56,7 @@
return false;
}
-void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+void InputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
int32_t token) {}
void InputMapper::cancelVibrate(int32_t token) {}
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 949c7ea..d9fc5cc 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -22,6 +22,7 @@
#include "InputListener.h"
#include "InputReaderContext.h"
#include "StylusState.h"
+#include "VibrationElement.h"
namespace android {
@@ -62,7 +63,8 @@
virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags);
- virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+ virtual void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
+ int32_t token);
virtual void cancelVibrate(int32_t token);
virtual void cancelTouch(nsecs_t when);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 030a846..57acba5 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -32,8 +32,8 @@
void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- for (size_t i = 0; i < mAxes.size(); i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ const Axis& axis = pair.second;
addMotionRange(axis.axisInfo.axis, axis, info);
if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
@@ -72,9 +72,7 @@
dump += INDENT2 "Joystick Input Mapper:\n";
dump += INDENT3 "Axes:\n";
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (const auto& [rawAxis, axis] : mAxes) {
const char* label = getAxisLabel(axis.axisInfo.axis);
if (label) {
dump += StringPrintf(INDENT4 "%s", label);
@@ -100,7 +98,7 @@
axis.scale, axis.offset, axis.highScale, axis.highOffset);
dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, "
"rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
- mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
+ rawAxis, axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz,
axis.rawAxisInfo.resolution);
}
@@ -113,8 +111,8 @@
if (!changes) { // first time only
// Collect all axes.
for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
- if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses()) &
- INPUT_DEVICE_CLASS_JOYSTICK)) {
+ if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses())
+ .test(InputDeviceClass::JOYSTICK))) {
continue; // axis must be claimed by a different device
}
@@ -123,43 +121,14 @@
if (rawAxisInfo.valid) {
// Map axis.
AxisInfo axisInfo;
- bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
+ const bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
+
if (!explicitlyMapped) {
// Axis is not explicitly mapped, will choose a generic axis later.
axisInfo.mode = AxisInfo::MODE_NORMAL;
axisInfo.axis = -1;
}
-
- // Apply flat override.
- int32_t rawFlat =
- axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
-
- // Calculate scaling factors and limits.
- Axis axis;
- if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
- float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
- float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale,
- 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- } else if (isCenteredAxis(axisInfo.axis)) {
- float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale,
- offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- } else {
- float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale,
- 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- }
-
- // To eliminate noise while the joystick is at rest, filter out small variations
- // in axis values up front.
- axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f;
-
- mAxes.add(abs, axis);
+ mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo, explicitlyMapped)});
}
}
@@ -174,9 +143,8 @@
// Assign generic axis ids to remaining axes.
int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
+ for (auto it = mAxes.begin(); it != mAxes.end(); /*increment it inside loop*/) {
+ Axis& axis = it->second;
if (axis.axisInfo.axis < 0) {
while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 &&
haveAxis(nextGenericAxisId)) {
@@ -189,19 +157,57 @@
} else {
ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
"have already been assigned to other axes.",
- getDeviceName().c_str(), mAxes.keyAt(i));
- mAxes.removeItemsAt(i--);
- numAxes -= 1;
+ getDeviceName().c_str(), it->first);
+ it = mAxes.erase(it);
+ continue;
}
}
+ it++;
}
}
}
+JoystickInputMapper::Axis JoystickInputMapper::createAxis(const AxisInfo& axisInfo,
+ const RawAbsoluteAxisInfo& rawAxisInfo,
+ bool explicitlyMapped) {
+ // Apply flat override.
+ int32_t rawFlat = axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+ float scale = std::numeric_limits<float>::signaling_NaN();
+ float highScale = std::numeric_limits<float>::signaling_NaN();
+ float highOffset = 0;
+ float offset = 0;
+ float min = 0;
+ // Calculate scaling factors and limits.
+ if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
+ highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
+ } else if (isCenteredAxis(axisInfo.axis)) {
+ scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+ offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
+ highOffset = offset;
+ highScale = scale;
+ min = -1.0f;
+ } else {
+ scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+ highScale = scale;
+ }
+
+ constexpr float max = 1.0;
+ const float flat = rawFlat * scale;
+ const float fuzz = rawAxisInfo.fuzz * scale;
+ const float resolution = rawAxisInfo.resolution * scale;
+
+ // To eliminate noise while the joystick is at rest, filter out small variations
+ // in axis values up front.
+ const float filter = fuzz ? fuzz : flat * 0.25f;
+ return Axis(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, highScale, highOffset, min,
+ max, flat, fuzz, resolution, filter);
+}
+
bool JoystickInputMapper::haveAxis(int32_t axisId) {
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (const std::pair<int32_t, Axis>& pair : mAxes) {
+ const Axis& axis = pair.second;
if (axis.axisInfo.axis == axisId ||
(axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) {
return true;
@@ -211,14 +217,14 @@
}
void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
- size_t i = mAxes.size();
- while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
- if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
+ while (mAxes.size() > PointerCoords::MAX_AXES) {
+ auto it = mAxes.begin();
+ if (ignoreExplicitlyMappedAxes && it->second.explicitlyMapped) {
continue;
}
ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
- getDeviceName().c_str(), mAxes.keyAt(i));
- mAxes.removeItemsAt(i);
+ getDeviceName().c_str(), it->first);
+ mAxes.erase(it);
}
}
@@ -243,9 +249,8 @@
void JoystickInputMapper::reset(nsecs_t when) {
// Recenter all axes.
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ Axis& axis = pair.second;
axis.resetValue();
}
@@ -255,9 +260,9 @@
void JoystickInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_ABS: {
- ssize_t index = mAxes.indexOfKey(rawEvent->code);
- if (index >= 0) {
- Axis& axis = mAxes.editValueAt(index);
+ auto it = mAxes.find(rawEvent->code);
+ if (it != mAxes.end()) {
+ Axis& axis = it->second;
float newValue, highNewValue;
switch (axis.axisInfo.mode) {
case AxisInfo::MODE_INVERT:
@@ -317,9 +322,8 @@
PointerCoords pointerCoords;
pointerCoords.clear();
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ const Axis& axis = pair.second;
setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue);
if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis,
@@ -357,9 +361,8 @@
bool JoystickInputMapper::filterAxes(bool force) {
bool atLeastOneSignificantChange = force;
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ Axis& axis = pair.second;
if (force ||
hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min,
axis.max)) {
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 823a096..0cf60a2 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -36,6 +36,26 @@
private:
struct Axis {
+ explicit Axis(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
+ bool explicitlyMapped, float scale, float offset, float highScale,
+ float highOffset, float min, float max, float flat, float fuzz,
+ float resolution, float filter)
+ : rawAxisInfo(rawAxisInfo),
+ axisInfo(axisInfo),
+ explicitlyMapped(explicitlyMapped),
+ scale(scale),
+ offset(offset),
+ highScale(highScale),
+ highOffset(highOffset),
+ min(min),
+ max(max),
+ flat(flat),
+ fuzz(fuzz),
+ resolution(resolution),
+ filter(filter) {
+ resetValue();
+ }
+
RawAbsoluteAxisInfo rawAxisInfo;
AxisInfo axisInfo;
@@ -58,26 +78,6 @@
float highCurrentValue; // current value of high split
float highNewValue; // most recent value of high split
- void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
- bool explicitlyMapped, float scale, float offset, float highScale,
- float highOffset, float min, float max, float flat, float fuzz,
- float resolution) {
- this->rawAxisInfo = rawAxisInfo;
- this->axisInfo = axisInfo;
- this->explicitlyMapped = explicitlyMapped;
- this->scale = scale;
- this->offset = offset;
- this->highScale = highScale;
- this->highOffset = highOffset;
- this->min = min;
- this->max = max;
- this->flat = flat;
- this->fuzz = fuzz;
- this->resolution = resolution;
- this->filter = 0;
- resetValue();
- }
-
void resetValue() {
this->currentValue = 0;
this->newValue = 0;
@@ -86,8 +86,11 @@
}
};
+ static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo,
+ bool explicitlyMapped);
+
// Axes indexed by raw ABS_* axis index.
- KeyedVector<int32_t, Axis> mAxes;
+ std::unordered_map<int32_t, Axis> mAxes;
void sync(nsecs_t when, bool force);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index e009221..bd4232d 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "KeyboardInputMapper.h"
@@ -138,7 +140,7 @@
// No associated display defined, try to find default display if orientationAware.
if (mParameters.orientationAware) {
- return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ return config->getDisplayViewportByType(ViewportType::INTERNAL);
}
return std::nullopt;
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 43bd9f1..0440f49 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -236,6 +236,20 @@
mMultiTouchMotionAccumulator.process(rawEvent);
}
+std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
+ const MultiTouchMotionAccumulator::Slot& inSlot) {
+ if (mHavePointerIds) {
+ int32_t trackingId = inSlot.getTrackingId();
+ for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
+ int32_t n = idBits.clearFirstMarkedBit();
+ if (mPointerTrackingIdMap[n] == trackingId) {
+ return std::make_optional(n);
+ }
+ }
+ }
+ return std::nullopt;
+}
+
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
size_t outCount = 0;
@@ -250,10 +264,9 @@
}
if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
- if (!mCurrentMotionAborted) {
- ALOGI("Canceling touch gesture from device %s because the palm event was detected",
- getDeviceName().c_str());
- cancelTouch(when);
+ std::optional<int32_t> id = getActiveBitId(*inSlot);
+ if (id) {
+ outState->rawPointerData.canceledIdBits.markBit(id.value());
}
continue;
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 89ef41d..ea6f207 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -92,17 +92,19 @@
class MultiTouchInputMapper : public TouchInputMapper {
public:
explicit MultiTouchInputMapper(InputDeviceContext& deviceContext);
- virtual ~MultiTouchInputMapper();
+ ~MultiTouchInputMapper() override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ void reset(nsecs_t when) override;
+ void process(const RawEvent* rawEvent) override;
protected:
- virtual void syncTouch(nsecs_t when, RawState* outState);
- virtual void configureRawPointerAxes();
- virtual bool hasStylus() const;
+ void syncTouch(nsecs_t when, RawState* outState) override;
+ void configureRawPointerAxes() override;
+ bool hasStylus() const override;
private:
+ // If the slot is in use, return the bit id. Return std::nullopt otherwise.
+ std::optional<int32_t> getActiveBitId(const MultiTouchMotionAccumulator::Slot& inSlot);
MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
// Specifies the pointer id bits that are in use, and their associated tracking id.
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 9885889..594ff42 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "RotaryEncoderInputMapper.h"
@@ -66,7 +68,7 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ config->getDisplayViewportByType(ViewportType::INTERNAL);
if (internalViewport) {
mOrientation = internalViewport->orientation;
} else {
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index f5befb3..9cb3f67 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -25,15 +25,15 @@
class SingleTouchInputMapper : public TouchInputMapper {
public:
explicit SingleTouchInputMapper(InputDeviceContext& deviceContext);
- virtual ~SingleTouchInputMapper();
+ ~SingleTouchInputMapper() override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ void reset(nsecs_t when) override;
+ void process(const RawEvent* rawEvent) override;
protected:
- virtual void syncTouch(nsecs_t when, RawState* outState);
- virtual void configureRawPointerAxes();
- virtual bool hasStylus() const;
+ void syncTouch(nsecs_t when, RawState* outState) override;
+ void configureRawPointerAxes() override;
+ bool hasStylus() const override;
private:
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index efdc84f..e867c6f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "TouchInputMapper.h"
@@ -102,6 +104,7 @@
pointerCount = other.pointerCount;
hoveringIdBits = other.hoveringIdBits;
touchingIdBits = other.touchingIdBits;
+ canceledIdBits = other.canceledIdBits;
for (uint32_t i = 0; i < pointerCount; i++) {
pointers[i] = other.pointers[i];
@@ -138,12 +141,15 @@
pointerCount = 0;
hoveringIdBits.clear();
touchingIdBits.clear();
+ canceledIdBits.clear();
+ validIdBits.clear();
}
void CookedPointerData::copyFrom(const CookedPointerData& other) {
pointerCount = other.pointerCount;
hoveringIdBits = other.hoveringIdBits;
touchingIdBits = other.touchingIdBits;
+ validIdBits = other.validIdBits;
for (uint32_t i = 0; i < pointerCount; i++) {
pointerProperties[i].copyFrom(other.pointerProperties[i]);
@@ -159,11 +165,13 @@
TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
: InputMapper(deviceContext),
mSource(0),
- mDeviceMode(DEVICE_MODE_DISABLED),
+ mDeviceMode(DeviceMode::DISABLED),
mRawSurfaceWidth(-1),
mRawSurfaceHeight(-1),
mSurfaceLeft(0),
mSurfaceTop(0),
+ mSurfaceRight(0),
+ mSurfaceBottom(0),
mPhysicalWidth(-1),
mPhysicalHeight(-1),
mPhysicalLeft(0),
@@ -179,7 +187,7 @@
void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- if (mDeviceMode != DEVICE_MODE_DISABLED) {
+ if (mDeviceMode != DeviceMode::DISABLED) {
info->addMotionRange(mOrientedRanges.x);
info->addMotionRange(mOrientedRanges.y);
info->addMotionRange(mOrientedRanges.pressure);
@@ -218,7 +226,7 @@
info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
0.0f);
}
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
+ if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {
const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat,
@@ -284,12 +292,14 @@
const PointerProperties& pointerProperties =
mLastCookedState.cookedPointerData.pointerProperties[i];
const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i];
- dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, "
- "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, "
- "toolMinor=%0.3f, "
+ dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, dx=%0.3f, dy=%0.3f, "
+ "pressure=%0.3f, touchMajor=%0.3f, touchMinor=%0.3f, "
+ "toolMajor=%0.3f, toolMinor=%0.3f, "
"orientation=%0.3f, tilt=%0.3f, distance=%0.3f, "
"toolType=%d, isHovering=%s\n",
i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y),
pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
@@ -311,7 +321,7 @@
dump += INDENT3 "External Stylus State:\n";
dumpStylusState(dump, mExternalStylusState);
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n");
dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale);
dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale);
@@ -323,15 +333,15 @@
const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
switch (deviceMode) {
- case DEVICE_MODE_DISABLED:
+ case DeviceMode::DISABLED:
return "disabled";
- case DEVICE_MODE_DIRECT:
+ case DeviceMode::DIRECT:
return "direct";
- case DEVICE_MODE_UNSCALED:
+ case DeviceMode::UNSCALED:
return "unscaled";
- case DEVICE_MODE_NAVIGATION:
+ case DeviceMode::NAVIGATION:
return "navigation";
- case DEVICE_MODE_POINTER:
+ case DeviceMode::POINTER:
return "pointer";
}
return "unknown";
@@ -375,6 +385,7 @@
if (!changes ||
(changes &
(InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+ InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
@@ -406,16 +417,16 @@
// multitouch. The spot-based presentation relies on being able to accurately
// locate two or more fingers on the touch pad.
mParameters.gestureMode = getDeviceContext().hasInputProperty(INPUT_PROP_SEMI_MT)
- ? Parameters::GESTURE_MODE_SINGLE_TOUCH
- : Parameters::GESTURE_MODE_MULTI_TOUCH;
+ ? Parameters::GestureMode::SINGLE_TOUCH
+ : Parameters::GestureMode::MULTI_TOUCH;
String8 gestureModeString;
if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.gestureMode"),
gestureModeString)) {
if (gestureModeString == "single-touch") {
- mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH;
+ mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
} else if (gestureModeString == "multi-touch") {
- mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH;
+ mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
} else if (gestureModeString != "default") {
ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
}
@@ -423,18 +434,18 @@
if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
// The device is a touch screen.
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
} else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
// The device is a pointing device like a track pad.
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
} else if (getDeviceContext().hasRelativeAxis(REL_X) ||
getDeviceContext().hasRelativeAxis(REL_Y)) {
// The device is a cursor device with a touch pad attached.
// By default don't use the touch pad to move the pointer.
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD;
} else {
// The device is a touch pad of unknown purpose.
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
}
mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
@@ -443,29 +454,29 @@
if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.deviceType"),
deviceTypeString)) {
if (deviceTypeString == "touchScreen") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
} else if (deviceTypeString == "touchPad") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD;
} else if (deviceTypeString == "touchNavigation") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION;
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
} else if (deviceTypeString == "pointer") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
} else if (deviceTypeString != "default") {
ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
}
}
- mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
mParameters.orientationAware);
mParameters.hasAssociatedDisplay = false;
mParameters.associatedDisplayIsExternal = false;
if (mParameters.orientationAware ||
- mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN ||
- mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
+ mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
+ mParameters.deviceType == Parameters::DeviceType::POINTER) {
mParameters.hasAssociatedDisplay = true;
- if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
+ if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
String8 uniqueDisplayId;
getDeviceContext().getConfiguration().tryGetProperty(String8("touch.displayId"),
@@ -488,10 +499,10 @@
dump += INDENT3 "Parameters:\n";
switch (mParameters.gestureMode) {
- case Parameters::GESTURE_MODE_SINGLE_TOUCH:
+ case Parameters::GestureMode::SINGLE_TOUCH:
dump += INDENT4 "GestureMode: single-touch\n";
break;
- case Parameters::GESTURE_MODE_MULTI_TOUCH:
+ case Parameters::GestureMode::MULTI_TOUCH:
dump += INDENT4 "GestureMode: multi-touch\n";
break;
default:
@@ -499,16 +510,16 @@
}
switch (mParameters.deviceType) {
- case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+ case Parameters::DeviceType::TOUCH_SCREEN:
dump += INDENT4 "DeviceType: touchScreen\n";
break;
- case Parameters::DEVICE_TYPE_TOUCH_PAD:
+ case Parameters::DeviceType::TOUCH_PAD:
dump += INDENT4 "DeviceType: touchPad\n";
break;
- case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
+ case Parameters::DeviceType::TOUCH_NAVIGATION:
dump += INDENT4 "DeviceType: touchNavigation\n";
break;
- case Parameters::DEVICE_TYPE_POINTER:
+ case Parameters::DeviceType::POINTER:
dump += INDENT4 "DeviceType: pointer\n";
break;
default:
@@ -558,14 +569,14 @@
* 4. Otherwise, use a non-display viewport.
*/
std::optional<DisplayViewport> TouchInputMapper::findViewport() {
- if (mParameters.hasAssociatedDisplay) {
+ if (mParameters.hasAssociatedDisplay && mDeviceMode != DeviceMode::UNSCALED) {
const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
if (displayPort) {
// Find the viewport that contains the same port
return getDeviceContext().getAssociatedViewport();
}
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
std::optional<DisplayViewport> viewport =
mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
if (viewport) {
@@ -583,18 +594,18 @@
ViewportType viewportTypeToUse;
if (mParameters.associatedDisplayIsExternal) {
- viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+ viewportTypeToUse = ViewportType::EXTERNAL;
} else {
- viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+ viewportTypeToUse = ViewportType::INTERNAL;
}
std::optional<DisplayViewport> viewport =
mConfig.getDisplayViewportByType(viewportTypeToUse);
- if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
+ if (!viewport && viewportTypeToUse == ViewportType::EXTERNAL) {
ALOGW("Input device %s should be associated with external display, "
"fallback to internal one for the external viewport is not found.",
getDeviceName().c_str());
- viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ viewport = mConfig.getDisplayViewportByType(ViewportType::INTERNAL);
}
return viewport;
@@ -610,34 +621,34 @@
}
void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
- int32_t oldDeviceMode = mDeviceMode;
+ DeviceMode oldDeviceMode = mDeviceMode;
resolveExternalStylusPresence();
// Determine device mode.
- if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER &&
- mConfig.pointerGesturesEnabled) {
+ if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
+ mConfig.pointerGesturesEnabled && !mConfig.pointerCapture) {
mSource = AINPUT_SOURCE_MOUSE;
- mDeviceMode = DEVICE_MODE_POINTER;
+ mDeviceMode = DeviceMode::POINTER;
if (hasStylus()) {
mSource |= AINPUT_SOURCE_STYLUS;
}
- } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN &&
+ } else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN &&
mParameters.hasAssociatedDisplay) {
mSource = AINPUT_SOURCE_TOUCHSCREEN;
- mDeviceMode = DEVICE_MODE_DIRECT;
+ mDeviceMode = DeviceMode::DIRECT;
if (hasStylus()) {
mSource |= AINPUT_SOURCE_STYLUS;
}
if (hasExternalStylus()) {
mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
}
- } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
+ } else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION) {
mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
- mDeviceMode = DEVICE_MODE_NAVIGATION;
+ mDeviceMode = DeviceMode::NAVIGATION;
} else {
mSource = AINPUT_SOURCE_TOUCHPAD;
- mDeviceMode = DEVICE_MODE_UNSCALED;
+ mDeviceMode = DeviceMode::UNSCALED;
}
// Ensure we have valid X and Y axes.
@@ -645,7 +656,7 @@
ALOGW("Touch device '%s' did not report support for X or Y axis! "
"The device will be inoperable.",
getDeviceName().c_str());
- mDeviceMode = DEVICE_MODE_DISABLED;
+ mDeviceMode = DeviceMode::DISABLED;
return;
}
@@ -656,7 +667,7 @@
"display. The device will be inoperable until the display size "
"becomes available.",
getDeviceName().c_str());
- mDeviceMode = DEVICE_MODE_DISABLED;
+ mDeviceMode = DeviceMode::DISABLED;
return;
}
@@ -668,7 +679,7 @@
if (viewportChanged) {
mViewport = *newViewport;
- if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
// Convert rotated viewport to natural surface coordinates.
int32_t naturalLogicalWidth, naturalLogicalHeight;
int32_t naturalPhysicalWidth, naturalPhysicalHeight;
@@ -759,8 +770,8 @@
}
// Create pointer controller if needed.
- if (mDeviceMode == DEVICE_MODE_POINTER ||
- (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
+ if (mDeviceMode == DeviceMode::POINTER ||
+ (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches)) {
if (mPointerController == nullptr) {
mPointerController = getContext()->getPointerController(getDeviceId());
}
@@ -798,7 +809,7 @@
float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
// Size factors.
- if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
+ if (mCalibration.sizeCalibration != Calibration::SizeCalibration::NONE) {
if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
} else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
@@ -847,8 +858,8 @@
// Pressure factors.
mPressureScale = 0;
float pressureMax = 1.0;
- if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL ||
- mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
+ if (mCalibration.pressureCalibration == Calibration::PressureCalibration::PHYSICAL ||
+ mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) {
if (mCalibration.havePressureScale) {
mPressureScale = mCalibration.pressureScale;
pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
@@ -901,9 +912,9 @@
mOrientedRanges.orientation.fuzz = 0;
mOrientedRanges.orientation.resolution = 0;
} else if (mCalibration.orientationCalibration !=
- Calibration::ORIENTATION_CALIBRATION_NONE) {
+ Calibration::OrientationCalibration::NONE) {
if (mCalibration.orientationCalibration ==
- Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
+ Calibration::OrientationCalibration::INTERPOLATED) {
if (mRawPointerAxes.orientation.valid) {
if (mRawPointerAxes.orientation.maxValue > 0) {
mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
@@ -928,8 +939,8 @@
// Distance
mDistanceScale = 0;
- if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) {
- if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) {
+ if (mCalibration.distanceCalibration != Calibration::DistanceCalibration::NONE) {
+ if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::SCALED) {
if (mCalibration.haveDistanceScale) {
mDistanceScale = mCalibration.distanceScale;
} else {
@@ -991,7 +1002,7 @@
// Location
updateAffineTransformation();
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
// Compute pointer gesture detection parameters.
float rawDiagonal = hypotf(rawWidth, rawHeight);
float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
@@ -1112,19 +1123,19 @@
Calibration& out = mCalibration;
// Size
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
+ out.sizeCalibration = Calibration::SizeCalibration::DEFAULT;
String8 sizeCalibrationString;
if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
if (sizeCalibrationString == "none") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+ out.sizeCalibration = Calibration::SizeCalibration::NONE;
} else if (sizeCalibrationString == "geometric") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
+ out.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
} else if (sizeCalibrationString == "diameter") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER;
+ out.sizeCalibration = Calibration::SizeCalibration::DIAMETER;
} else if (sizeCalibrationString == "box") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX;
+ out.sizeCalibration = Calibration::SizeCalibration::BOX;
} else if (sizeCalibrationString == "area") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA;
+ out.sizeCalibration = Calibration::SizeCalibration::AREA;
} else if (sizeCalibrationString != "default") {
ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string());
}
@@ -1135,15 +1146,15 @@
out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed);
// Pressure
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
+ out.pressureCalibration = Calibration::PressureCalibration::DEFAULT;
String8 pressureCalibrationString;
if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
if (pressureCalibrationString == "none") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+ out.pressureCalibration = Calibration::PressureCalibration::NONE;
} else if (pressureCalibrationString == "physical") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+ out.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
} else if (pressureCalibrationString == "amplitude") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
+ out.pressureCalibration = Calibration::PressureCalibration::AMPLITUDE;
} else if (pressureCalibrationString != "default") {
ALOGW("Invalid value for touch.pressure.calibration: '%s'",
pressureCalibrationString.string());
@@ -1153,15 +1164,15 @@
out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale);
// Orientation
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
+ out.orientationCalibration = Calibration::OrientationCalibration::DEFAULT;
String8 orientationCalibrationString;
if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
if (orientationCalibrationString == "none") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+ out.orientationCalibration = Calibration::OrientationCalibration::NONE;
} else if (orientationCalibrationString == "interpolated") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+ out.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
} else if (orientationCalibrationString == "vector") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR;
+ out.orientationCalibration = Calibration::OrientationCalibration::VECTOR;
} else if (orientationCalibrationString != "default") {
ALOGW("Invalid value for touch.orientation.calibration: '%s'",
orientationCalibrationString.string());
@@ -1169,13 +1180,13 @@
}
// Distance
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT;
+ out.distanceCalibration = Calibration::DistanceCalibration::DEFAULT;
String8 distanceCalibrationString;
if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
if (distanceCalibrationString == "none") {
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+ out.distanceCalibration = Calibration::DistanceCalibration::NONE;
} else if (distanceCalibrationString == "scaled") {
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+ out.distanceCalibration = Calibration::DistanceCalibration::SCALED;
} else if (distanceCalibrationString != "default") {
ALOGW("Invalid value for touch.distance.calibration: '%s'",
distanceCalibrationString.string());
@@ -1184,13 +1195,13 @@
out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale);
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT;
+ out.coverageCalibration = Calibration::CoverageCalibration::DEFAULT;
String8 coverageCalibrationString;
if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
if (coverageCalibrationString == "none") {
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
+ out.coverageCalibration = Calibration::CoverageCalibration::NONE;
} else if (coverageCalibrationString == "box") {
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX;
+ out.coverageCalibration = Calibration::CoverageCalibration::BOX;
} else if (coverageCalibrationString != "default") {
ALOGW("Invalid value for touch.coverage.calibration: '%s'",
coverageCalibrationString.string());
@@ -1201,43 +1212,43 @@
void TouchInputMapper::resolveCalibration() {
// Size
if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
- if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) {
- mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
+ if (mCalibration.sizeCalibration == Calibration::SizeCalibration::DEFAULT) {
+ mCalibration.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
}
} else {
- mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+ mCalibration.sizeCalibration = Calibration::SizeCalibration::NONE;
}
// Pressure
if (mRawPointerAxes.pressure.valid) {
- if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) {
- mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+ if (mCalibration.pressureCalibration == Calibration::PressureCalibration::DEFAULT) {
+ mCalibration.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
}
} else {
- mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+ mCalibration.pressureCalibration = Calibration::PressureCalibration::NONE;
}
// Orientation
if (mRawPointerAxes.orientation.valid) {
- if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) {
- mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+ if (mCalibration.orientationCalibration == Calibration::OrientationCalibration::DEFAULT) {
+ mCalibration.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
}
} else {
- mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+ mCalibration.orientationCalibration = Calibration::OrientationCalibration::NONE;
}
// Distance
if (mRawPointerAxes.distance.valid) {
- if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) {
- mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+ if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::DEFAULT) {
+ mCalibration.distanceCalibration = Calibration::DistanceCalibration::SCALED;
}
} else {
- mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+ mCalibration.distanceCalibration = Calibration::DistanceCalibration::NONE;
}
// Coverage
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) {
- mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
+ if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::DEFAULT) {
+ mCalibration.coverageCalibration = Calibration::CoverageCalibration::NONE;
}
}
@@ -1246,19 +1257,19 @@
// Size
switch (mCalibration.sizeCalibration) {
- case Calibration::SIZE_CALIBRATION_NONE:
+ case Calibration::SizeCalibration::NONE:
dump += INDENT4 "touch.size.calibration: none\n";
break;
- case Calibration::SIZE_CALIBRATION_GEOMETRIC:
+ case Calibration::SizeCalibration::GEOMETRIC:
dump += INDENT4 "touch.size.calibration: geometric\n";
break;
- case Calibration::SIZE_CALIBRATION_DIAMETER:
+ case Calibration::SizeCalibration::DIAMETER:
dump += INDENT4 "touch.size.calibration: diameter\n";
break;
- case Calibration::SIZE_CALIBRATION_BOX:
+ case Calibration::SizeCalibration::BOX:
dump += INDENT4 "touch.size.calibration: box\n";
break;
- case Calibration::SIZE_CALIBRATION_AREA:
+ case Calibration::SizeCalibration::AREA:
dump += INDENT4 "touch.size.calibration: area\n";
break;
default:
@@ -1280,13 +1291,13 @@
// Pressure
switch (mCalibration.pressureCalibration) {
- case Calibration::PRESSURE_CALIBRATION_NONE:
+ case Calibration::PressureCalibration::NONE:
dump += INDENT4 "touch.pressure.calibration: none\n";
break;
- case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+ case Calibration::PressureCalibration::PHYSICAL:
dump += INDENT4 "touch.pressure.calibration: physical\n";
break;
- case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ case Calibration::PressureCalibration::AMPLITUDE:
dump += INDENT4 "touch.pressure.calibration: amplitude\n";
break;
default:
@@ -1299,13 +1310,13 @@
// Orientation
switch (mCalibration.orientationCalibration) {
- case Calibration::ORIENTATION_CALIBRATION_NONE:
+ case Calibration::OrientationCalibration::NONE:
dump += INDENT4 "touch.orientation.calibration: none\n";
break;
- case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ case Calibration::OrientationCalibration::INTERPOLATED:
dump += INDENT4 "touch.orientation.calibration: interpolated\n";
break;
- case Calibration::ORIENTATION_CALIBRATION_VECTOR:
+ case Calibration::OrientationCalibration::VECTOR:
dump += INDENT4 "touch.orientation.calibration: vector\n";
break;
default:
@@ -1314,10 +1325,10 @@
// Distance
switch (mCalibration.distanceCalibration) {
- case Calibration::DISTANCE_CALIBRATION_NONE:
+ case Calibration::DistanceCalibration::NONE:
dump += INDENT4 "touch.distance.calibration: none\n";
break;
- case Calibration::DISTANCE_CALIBRATION_SCALED:
+ case Calibration::DistanceCalibration::SCALED:
dump += INDENT4 "touch.distance.calibration: scaled\n";
break;
default:
@@ -1329,10 +1340,10 @@
}
switch (mCalibration.coverageCalibration) {
- case Calibration::COVERAGE_CALIBRATION_NONE:
+ case Calibration::CoverageCalibration::NONE:
dump += INDENT4 "touch.coverage.calibration: none\n";
break;
- case Calibration::COVERAGE_CALIBRATION_BOX:
+ case Calibration::CoverageCalibration::BOX:
dump += INDENT4 "touch.coverage.calibration: box\n";
break;
default:
@@ -1370,7 +1381,7 @@
mCurrentCookedState.clear();
mLastRawState.clear();
mLastCookedState.clear();
- mPointerUsage = POINTER_USAGE_NONE;
+ mPointerUsage = PointerUsage::NONE;
mSentHoverEnter = false;
mHavePointerIds = false;
mCurrentMotionAborted = false;
@@ -1442,17 +1453,18 @@
#if DEBUG_RAW_EVENTS
ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
- "hovering ids 0x%08x -> 0x%08x",
+ "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
last->rawPointerData.pointerCount, next->rawPointerData.pointerCount,
last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value,
- last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value);
+ last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value,
+ next->rawPointerData.canceledIdBits.value);
#endif
processRawTouches(false /*timeout*/);
}
void TouchInputMapper::processRawTouches(bool timeout) {
- if (mDeviceMode == DEVICE_MODE_DISABLED) {
+ if (mDeviceMode == DeviceMode::DISABLED) {
// Drop all input if the device is disabled.
mCurrentRawState.clear();
mRawStatesPending.clear();
@@ -1517,7 +1529,7 @@
bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
if (initialDown || buttonsPressed) {
// If this is a touch screen, hide the pointer on an initial down.
- if (mDeviceMode == DEVICE_MODE_DIRECT) {
+ if (mDeviceMode == DeviceMode::DIRECT) {
getContext()->fadePointer();
}
@@ -1546,7 +1558,7 @@
mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
@@ -1576,18 +1588,18 @@
if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
mCurrentCookedState.mouseIdBits.clear();
mCurrentCookedState.fingerIdBits.clear();
- pointerUsage = POINTER_USAGE_STYLUS;
+ pointerUsage = PointerUsage::STYLUS;
} else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
mCurrentCookedState.fingerIdBits.clear();
- pointerUsage = POINTER_USAGE_MOUSE;
+ pointerUsage = PointerUsage::MOUSE;
} else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
isPointerDown(mCurrentRawState.buttonState)) {
- pointerUsage = POINTER_USAGE_GESTURES;
+ pointerUsage = PointerUsage::GESTURES;
}
dispatchPointerUsage(when, policyFlags, pointerUsage);
} else {
- if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches &&
+ if (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches &&
mPointerController != nullptr) {
mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT);
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
@@ -1627,7 +1639,7 @@
}
void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
- if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
+ if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
mCurrentRawState.buttonState |= mExternalStylusState.buttons;
}
}
@@ -1654,7 +1666,7 @@
}
bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
- if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) {
+ if (mDeviceMode != DeviceMode::DIRECT || !hasExternalStylus()) {
return false;
}
@@ -1697,11 +1709,11 @@
}
void TouchInputMapper::timeoutExpired(nsecs_t when) {
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- if (mPointerUsage == POINTER_USAGE_GESTURES) {
+ if (mDeviceMode == DeviceMode::POINTER) {
+ if (mPointerUsage == PointerUsage::GESTURES) {
dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
}
- } else if (mDeviceMode == DEVICE_MODE_DIRECT) {
+ } else if (mDeviceMode == DeviceMode::DIRECT) {
if (mExternalStylusFusionTimeout < when) {
processRawTouches(true /*timeout*/);
} else if (mExternalStylusFusionTimeout != LLONG_MAX) {
@@ -1770,7 +1782,8 @@
// Pointer just went down. Check for virtual key press or off-screen touches.
uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
- if (!isPointInsideSurface(pointer.x, pointer.y)) {
+ // Exclude unscaled device for inside surface checking.
+ if (!isPointInsideSurface(pointer.x, pointer.y) && mDeviceMode != DeviceMode::UNSCALED) {
// If exactly one pointer went down, check for virtual key hit.
// Otherwise we will drop the entire stroke.
if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
@@ -1890,14 +1903,15 @@
// Dispatch pointer up events.
while (!upIdBits.isEmpty()) {
uint32_t upId = upIdBits.clearFirstMarkedBit();
-
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
- metaState, buttonState, 0,
+ bool isCanceled = mCurrentCookedState.cookedPointerData.canceledIdBits.hasBit(upId);
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
+ isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,
mLastCookedState.cookedPointerData.pointerProperties,
mLastCookedState.cookedPointerData.pointerCoords,
mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
dispatchedIdBits.clearBit(upId);
+ mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);
}
// Dispatch move events if any of the remaining pointers moved from their old locations.
@@ -2023,6 +2037,8 @@
mCurrentRawState.rawPointerData.hoveringIdBits;
mCurrentCookedState.cookedPointerData.touchingIdBits =
mCurrentRawState.rawPointerData.touchingIdBits;
+ mCurrentCookedState.cookedPointerData.canceledIdBits =
+ mCurrentRawState.rawPointerData.canceledIdBits;
if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
mCurrentCookedState.buttonState = 0;
@@ -2038,10 +2054,10 @@
// Size
float touchMajor, touchMinor, toolMajor, toolMinor, size;
switch (mCalibration.sizeCalibration) {
- case Calibration::SIZE_CALIBRATION_GEOMETRIC:
- case Calibration::SIZE_CALIBRATION_DIAMETER:
- case Calibration::SIZE_CALIBRATION_BOX:
- case Calibration::SIZE_CALIBRATION_AREA:
+ case Calibration::SizeCalibration::GEOMETRIC:
+ case Calibration::SizeCalibration::DIAMETER:
+ case Calibration::SizeCalibration::BOX:
+ case Calibration::SizeCalibration::AREA:
if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
touchMajor = in.touchMajor;
touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
@@ -2083,17 +2099,17 @@
}
}
- if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {
+ if (mCalibration.sizeCalibration == Calibration::SizeCalibration::GEOMETRIC) {
touchMajor *= mGeometricScale;
touchMinor *= mGeometricScale;
toolMajor *= mGeometricScale;
toolMinor *= mGeometricScale;
- } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {
+ } else if (mCalibration.sizeCalibration == Calibration::SizeCalibration::AREA) {
touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;
touchMinor = touchMajor;
toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;
toolMinor = toolMajor;
- } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {
+ } else if (mCalibration.sizeCalibration == Calibration::SizeCalibration::DIAMETER) {
touchMinor = touchMajor;
toolMinor = toolMajor;
}
@@ -2116,8 +2132,8 @@
// Pressure
float pressure;
switch (mCalibration.pressureCalibration) {
- case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
- case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ case Calibration::PressureCalibration::PHYSICAL:
+ case Calibration::PressureCalibration::AMPLITUDE:
pressure = in.pressure * mPressureScale;
break;
default:
@@ -2137,10 +2153,10 @@
tilt = 0;
switch (mCalibration.orientationCalibration) {
- case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ case Calibration::OrientationCalibration::INTERPOLATED:
orientation = in.orientation * mOrientationScale;
break;
- case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
+ case Calibration::OrientationCalibration::VECTOR: {
int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
int32_t c2 = signExtendNybble(in.orientation & 0x0f);
if (c1 != 0 || c2 != 0) {
@@ -2164,7 +2180,7 @@
// Distance
float distance;
switch (mCalibration.distanceCalibration) {
- case Calibration::DISTANCE_CALIBRATION_SCALED:
+ case Calibration::DistanceCalibration::SCALED:
distance = in.distance * mDistanceScale;
break;
default:
@@ -2174,7 +2190,7 @@
// Coverage
int32_t rawLeft, rawTop, rawRight, rawBottom;
switch (mCalibration.coverageCalibration) {
- case Calibration::COVERAGE_CALIBRATION_BOX:
+ case Calibration::CoverageCalibration::BOX:
rawLeft = (in.toolMinor & 0xffff0000) >> 16;
rawRight = in.toolMinor & 0x0000ffff;
rawBottom = in.toolMajor & 0x0000ffff;
@@ -2251,7 +2267,7 @@
out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);
out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
+ if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {
out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);
out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);
out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);
@@ -2261,15 +2277,26 @@
out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
}
+ // Write output relative fields if applicable.
+ uint32_t id = in.id;
+ if (mSource == AINPUT_SOURCE_TOUCHPAD &&
+ mLastCookedState.cookedPointerData.hasPointerCoordsForId(id)) {
+ const PointerCoords& p = mLastCookedState.cookedPointerData.pointerCoordsForId(id);
+ float dx = xTransformed - p.getAxisValue(AMOTION_EVENT_AXIS_X);
+ float dy = yTransformed - p.getAxisValue(AMOTION_EVENT_AXIS_Y);
+ out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, dx);
+ out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
+ }
+
// Write output properties.
PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i];
- uint32_t id = in.id;
properties.clear();
properties.id = id;
properties.toolType = in.toolType;
- // Write id index.
+ // Write id index and mark id as valid.
mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
+ mCurrentCookedState.cookedPointerData.validIdBits.markBit(id);
}
}
@@ -2281,36 +2308,36 @@
}
switch (mPointerUsage) {
- case POINTER_USAGE_GESTURES:
+ case PointerUsage::GESTURES:
dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
break;
- case POINTER_USAGE_STYLUS:
+ case PointerUsage::STYLUS:
dispatchPointerStylus(when, policyFlags);
break;
- case POINTER_USAGE_MOUSE:
+ case PointerUsage::MOUSE:
dispatchPointerMouse(when, policyFlags);
break;
- default:
+ case PointerUsage::NONE:
break;
}
}
void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
switch (mPointerUsage) {
- case POINTER_USAGE_GESTURES:
+ case PointerUsage::GESTURES:
abortPointerGestures(when, policyFlags);
break;
- case POINTER_USAGE_STYLUS:
+ case PointerUsage::STYLUS:
abortPointerStylus(when, policyFlags);
break;
- case POINTER_USAGE_MOUSE:
+ case PointerUsage::MOUSE:
abortPointerMouse(when, policyFlags);
break;
- default:
+ case PointerUsage::NONE:
break;
}
- mPointerUsage = POINTER_USAGE_NONE;
+ mPointerUsage = PointerUsage::NONE;
}
void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) {
@@ -2326,13 +2353,13 @@
}
// Update the pointer presentation and spots.
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
+ if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH) {
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
if (finishPreviousGesture || cancelPreviousGesture) {
mPointerController->clearSpots();
}
- if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
mPointerController->setSpots(mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex,
mPointerGesture.currentGestureIdBits,
@@ -2344,28 +2371,28 @@
// Show or hide the pointer if needed.
switch (mPointerGesture.currentGestureMode) {
- case PointerGesture::NEUTRAL:
- case PointerGesture::QUIET:
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH &&
- mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) {
+ case PointerGesture::Mode::NEUTRAL:
+ case PointerGesture::Mode::QUIET:
+ if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH &&
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) {
// Remind the user of where the pointer is after finishing a gesture with spots.
mPointerController->unfade(PointerControllerInterface::Transition::GRADUAL);
}
break;
- case PointerGesture::TAP:
- case PointerGesture::TAP_DRAG:
- case PointerGesture::BUTTON_CLICK_OR_DRAG:
- case PointerGesture::HOVER:
- case PointerGesture::PRESS:
- case PointerGesture::SWIPE:
+ case PointerGesture::Mode::TAP:
+ case PointerGesture::Mode::TAP_DRAG:
+ case PointerGesture::Mode::BUTTON_CLICK_OR_DRAG:
+ case PointerGesture::Mode::HOVER:
+ case PointerGesture::Mode::PRESS:
+ case PointerGesture::Mode::SWIPE:
// Unfade the pointer when the current gesture manipulates the
// area directly under the pointer.
mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
break;
- case PointerGesture::FREEFORM:
+ case PointerGesture::Mode::FREEFORM:
// Fade the pointer when the current gesture manipulates a different
// area and there are spots to guide the user experience.
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
+ if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH) {
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
} else {
mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
@@ -2379,12 +2406,12 @@
// Update last coordinates of pointers that have moved so that we observe the new
// pointer positions at the same time as other pointers that have just gone up.
- bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP ||
- mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG ||
- mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG ||
- mPointerGesture.currentGestureMode == PointerGesture::PRESS ||
- mPointerGesture.currentGestureMode == PointerGesture::SWIPE ||
- mPointerGesture.currentGestureMode == PointerGesture::FREEFORM;
+ bool down = mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::BUTTON_CLICK_OR_DRAG ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM;
bool moveNeeded = false;
if (down && !cancelPreviousGesture && !finishPreviousGesture &&
!mPointerGesture.lastGestureIdBits.isEmpty() &&
@@ -2467,7 +2494,7 @@
}
// Send motion events for hover.
- if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
@@ -2553,7 +2580,7 @@
ALOGD("Gestures: Processing timeout");
#endif
- if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+ if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
// The tap/drag timeout has not yet expired.
getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime +
@@ -2566,7 +2593,7 @@
*outFinishPreviousGesture = true;
mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
mPointerGesture.currentGestureIdBits.clear();
mPointerVelocityControl.reset();
@@ -2598,9 +2625,9 @@
// If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
// to NEUTRAL, then we should not generate tap event.
- if (mPointerGesture.lastGestureMode != PointerGesture::HOVER &&
- mPointerGesture.lastGestureMode != PointerGesture::TAP &&
- mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::TAP &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::TAP_DRAG) {
mPointerGesture.resetTap();
}
@@ -2633,15 +2660,16 @@
} else {
isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
if (!isQuietTime) {
- if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS ||
- mPointerGesture.lastGestureMode == PointerGesture::SWIPE ||
- mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) &&
+ if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS ||
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE ||
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) &&
currentFingerCount < 2) {
// Enter quiet time when exiting swipe or freeform state.
// This is to prevent accidentally entering the hover state and flinging the
// pointer when finishing a swipe and there is still one pointer left onscreen.
isQuietTime = true;
- } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG &&
+ } else if (mPointerGesture.lastGestureMode ==
+ PointerGesture::Mode::BUTTON_CLICK_OR_DRAG &&
currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
// Enter quiet time when releasing the button and there are still two or more
// fingers down. This may indicate that one finger was used to press the button
@@ -2661,12 +2689,12 @@
ALOGD("Gestures: QUIET for next %0.3fms",
(mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
#endif
- if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::QUIET) {
*outFinishPreviousGesture = true;
}
mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::QUIET;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::QUIET;
mPointerGesture.currentGestureIdBits.clear();
mPointerVelocityControl.reset();
@@ -2690,7 +2718,7 @@
activeTouchId, currentFingerCount);
#endif
// Reset state when just starting.
- if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::BUTTON_CLICK_OR_DRAG) {
*outFinishPreviousGesture = true;
mPointerGesture.activeGestureId = 0;
}
@@ -2744,7 +2772,7 @@
float x, y;
mPointerController->getPosition(&x, &y);
- mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
@@ -2757,15 +2785,15 @@
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
} else if (currentFingerCount == 0) {
// Case 3. No fingers down and button is not pressed. (NEUTRAL)
- if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::NEUTRAL) {
*outFinishPreviousGesture = true;
}
// Watch for taps coming out of HOVER or TAP_DRAG mode.
// Checking for taps after TAP_DRAG allows us to detect double-taps.
bool tapped = false;
- if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER ||
- mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) &&
+ if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::HOVER ||
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
lastFingerCount == 1) {
if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
float x, y;
@@ -2781,7 +2809,7 @@
mConfig.pointerGestureTapDragInterval);
mPointerGesture.activeGestureId = 0;
- mPointerGesture.currentGestureMode = PointerGesture::TAP;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP;
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
@@ -2824,7 +2852,7 @@
ALOGD("Gestures: NEUTRAL");
#endif
mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
mPointerGesture.currentGestureIdBits.clear();
}
} else if (currentFingerCount == 1) {
@@ -2834,14 +2862,14 @@
// When in TAP_DRAG, emit MOVE events at the pointer location.
ALOG_ASSERT(activeTouchId >= 0);
- mPointerGesture.currentGestureMode = PointerGesture::HOVER;
- if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
+ if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
float x, y;
mPointerController->getPosition(&x, &y);
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
- mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
} else {
#if DEBUG_GESTURES
ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
@@ -2854,8 +2882,8 @@
(when - mPointerGesture.tapUpTime) * 0.000001f);
#endif
}
- } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) {
- mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+ } else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) {
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
}
float deltaX = 0, deltaY = 0;
@@ -2878,7 +2906,7 @@
}
bool down;
- if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) {
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG) {
#if DEBUG_GESTURES
ALOGD("Gestures: TAP_DRAG");
#endif
@@ -2887,7 +2915,7 @@
#if DEBUG_GESTURES
ALOGD("Gestures: HOVER");
#endif
- if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER) {
*outFinishPreviousGesture = true;
}
mPointerGesture.activeGestureId = 0;
@@ -2933,9 +2961,9 @@
bool settled = when >=
mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
- if (mPointerGesture.lastGestureMode != PointerGesture::PRESS &&
- mPointerGesture.lastGestureMode != PointerGesture::SWIPE &&
- mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::PRESS &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::SWIPE &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
*outFinishPreviousGesture = true;
} else if (!settled && currentFingerCount > lastFingerCount) {
// Additional pointers have gone down but not yet settled.
@@ -2953,7 +2981,7 @@
}
if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
- mPointerGesture.currentGestureMode = PointerGesture::PRESS;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS;
mPointerGesture.activeGestureId = 0;
mPointerGesture.referenceIdBits.clear();
mPointerVelocityControl.reset();
@@ -3005,7 +3033,7 @@
}
// Consider transitions from PRESS to SWIPE or MULTITOUCH.
- if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS) {
float dist[MAX_POINTER_ID + 1];
int32_t distOverThreshold = 0;
for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
@@ -3027,7 +3055,7 @@
currentFingerCount);
#endif
*outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
} else {
// There are exactly two pointers.
BitSet32 idBits(mCurrentCookedState.fingerIdBits);
@@ -3046,7 +3074,7 @@
mutualDistance, mPointerGestureMaxSwipeWidth);
#endif
*outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
} else {
// There are two pointers. Wait for both pointers to start moving
// before deciding whether this is a SWIPE or FREEFORM gesture.
@@ -3077,7 +3105,7 @@
mConfig.pointerGestureMultitouchMinDistance, cosine,
mConfig.pointerGestureSwipeTransitionAngleCosine);
#endif
- mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE;
} else {
// Pointers are moving in different directions. Switch to FREEFORM.
#if DEBUG_GESTURES
@@ -3089,13 +3117,13 @@
mConfig.pointerGestureSwipeTransitionAngleCosine);
#endif
*outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
}
}
}
}
}
- } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
// Switch from SWIPE to FREEFORM if additional pointers go down.
// Cancel previous gesture.
if (currentFingerCount > 2) {
@@ -3104,13 +3132,13 @@
currentFingerCount);
#endif
*outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
}
}
// Move the reference points based on the overall group motion of the fingers
// except in PRESS mode while waiting for a transition to occur.
- if (mPointerGesture.currentGestureMode != PointerGesture::PRESS &&
+ if (mPointerGesture.currentGestureMode != PointerGesture::Mode::PRESS &&
(commonDeltaX || commonDeltaY)) {
for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
@@ -3133,8 +3161,8 @@
}
// Report gestures.
- if (mPointerGesture.currentGestureMode == PointerGesture::PRESS ||
- mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
// PRESS or SWIPE mode.
#if DEBUG_GESTURES
ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
@@ -3155,7 +3183,7 @@
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
mPointerGesture.referenceGestureY);
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
// FREEFORM mode.
#if DEBUG_GESTURES
ALOGD("Gestures: FREEFORM activeTouchId=%d,"
@@ -3168,7 +3196,7 @@
BitSet32 mappedTouchIdBits;
BitSet32 usedGestureIdBits;
- if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
// Initially, assign the active gesture id to the active touch point
// if there is one. No other touch id bits are mapped yet.
if (!*outCancelPreviousGesture) {
@@ -3560,7 +3588,11 @@
if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
action = AMOTION_EVENT_ACTION_DOWN;
} else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
- action = AMOTION_EVENT_ACTION_UP;
+ if ((flags & AMOTION_EVENT_FLAG_CANCELED) != 0) {
+ action = AMOTION_EVENT_ACTION_CANCEL;
+ } else {
+ action = AMOTION_EVENT_ACTION_UP;
+ }
} else {
// Can't happen.
ALOG_ASSERT(false);
@@ -3568,7 +3600,7 @@
}
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
}
const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
@@ -3813,9 +3845,9 @@
#if DEBUG_POINTER_ASSIGNMENT
ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
- heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+ for (size_t j = 0; j < heapSize; j++) {
+ ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, j,
+ heap[j].currentPointerIndex, heap[j].lastPointerIndex, heap[j].distance);
}
#endif
}
@@ -3907,7 +3939,7 @@
std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
if (mParameters.hasAssociatedDisplay) {
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
return std::make_optional(mPointerController->getDisplayId());
} else {
return std::make_optional(mViewport.displayId);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 7f811a0..df6581d 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -17,6 +17,8 @@
#ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
+#include <stdint.h>
+
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
#include "EventHub.h"
@@ -24,8 +26,6 @@
#include "InputReaderBase.h"
#include "TouchButtonAccumulator.h"
-#include <stdint.h>
-
namespace android {
/* Raw axis information from the driver. */
@@ -71,7 +71,7 @@
uint32_t pointerCount;
Pointer pointers[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits;
+ BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits;
uint32_t idToIndex[MAX_POINTER_ID + 1];
RawPointerData();
@@ -90,6 +90,7 @@
inline void clearIdBits() {
hoveringIdBits.clear();
touchingIdBits.clear();
+ canceledIdBits.clear();
}
inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; }
@@ -102,7 +103,7 @@
uint32_t pointerCount;
PointerProperties pointerProperties[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits;
+ BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits, validIdBits;
uint32_t idToIndex[MAX_POINTER_ID + 1];
CookedPointerData();
@@ -128,30 +129,31 @@
inline bool isTouching(uint32_t pointerIndex) const {
return touchingIdBits.hasBit(pointerProperties[pointerIndex].id);
}
+
+ inline bool hasPointerCoordsForId(uint32_t id) const { return validIdBits.hasBit(id); }
};
class TouchInputMapper : public InputMapper {
public:
explicit TouchInputMapper(InputDeviceContext& deviceContext);
- virtual ~TouchInputMapper();
+ ~TouchInputMapper() override;
- virtual uint32_t getSources() override;
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
- virtual void dump(std::string& dump) override;
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ uint32_t getSources() override;
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ void dump(std::string& dump) override;
+ void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
+ void reset(nsecs_t when) override;
+ void process(const RawEvent* rawEvent) override;
- virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
- virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) override;
+ int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+ int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+ bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) override;
- virtual void cancelTouch(nsecs_t when) override;
- virtual void timeoutExpired(nsecs_t when) override;
- virtual void updateExternalStylusState(const StylusState& state) override;
- virtual std::optional<int32_t> getAssociatedDisplayId() override;
+ void cancelTouch(nsecs_t when) override;
+ void timeoutExpired(nsecs_t when) override;
+ void updateExternalStylusState(const StylusState& state) override;
+ std::optional<int32_t> getAssociatedDisplayId() override;
protected:
CursorButtonAccumulator mCursorButtonAccumulator;
@@ -177,12 +179,12 @@
// Input sources and device mode.
uint32_t mSource;
- enum DeviceMode {
- DEVICE_MODE_DISABLED, // input is disabled
- DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
- DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
- DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
- DEVICE_MODE_POINTER, // pointer mapping (pointer)
+ enum class DeviceMode {
+ DISABLED, // input is disabled
+ DIRECT, // direct mapping (touchscreen)
+ UNSCALED, // unscaled mapping (touchpad)
+ NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
+ POINTER, // pointer mapping (pointer)
};
DeviceMode mDeviceMode;
@@ -191,11 +193,11 @@
// Immutable configuration parameters.
struct Parameters {
- enum DeviceType {
- DEVICE_TYPE_TOUCH_SCREEN,
- DEVICE_TYPE_TOUCH_PAD,
- DEVICE_TYPE_TOUCH_NAVIGATION,
- DEVICE_TYPE_POINTER,
+ enum class DeviceType {
+ TOUCH_SCREEN,
+ TOUCH_PAD,
+ TOUCH_NAVIGATION,
+ POINTER,
};
DeviceType deviceType;
@@ -205,9 +207,9 @@
bool hasButtonUnderPad;
std::string uniqueDisplayId;
- enum GestureMode {
- GESTURE_MODE_SINGLE_TOUCH,
- GESTURE_MODE_MULTI_TOUCH,
+ enum class GestureMode {
+ SINGLE_TOUCH,
+ MULTI_TOUCH,
};
GestureMode gestureMode;
@@ -217,13 +219,13 @@
// Immutable calibration parameters in parsed form.
struct Calibration {
// Size
- enum SizeCalibration {
- SIZE_CALIBRATION_DEFAULT,
- SIZE_CALIBRATION_NONE,
- SIZE_CALIBRATION_GEOMETRIC,
- SIZE_CALIBRATION_DIAMETER,
- SIZE_CALIBRATION_BOX,
- SIZE_CALIBRATION_AREA,
+ enum class SizeCalibration {
+ DEFAULT,
+ NONE,
+ GEOMETRIC,
+ DIAMETER,
+ BOX,
+ AREA,
};
SizeCalibration sizeCalibration;
@@ -236,11 +238,11 @@
bool sizeIsSummed;
// Pressure
- enum PressureCalibration {
- PRESSURE_CALIBRATION_DEFAULT,
- PRESSURE_CALIBRATION_NONE,
- PRESSURE_CALIBRATION_PHYSICAL,
- PRESSURE_CALIBRATION_AMPLITUDE,
+ enum class PressureCalibration {
+ DEFAULT,
+ NONE,
+ PHYSICAL,
+ AMPLITUDE,
};
PressureCalibration pressureCalibration;
@@ -248,30 +250,30 @@
float pressureScale;
// Orientation
- enum OrientationCalibration {
- ORIENTATION_CALIBRATION_DEFAULT,
- ORIENTATION_CALIBRATION_NONE,
- ORIENTATION_CALIBRATION_INTERPOLATED,
- ORIENTATION_CALIBRATION_VECTOR,
+ enum class OrientationCalibration {
+ DEFAULT,
+ NONE,
+ INTERPOLATED,
+ VECTOR,
};
OrientationCalibration orientationCalibration;
// Distance
- enum DistanceCalibration {
- DISTANCE_CALIBRATION_DEFAULT,
- DISTANCE_CALIBRATION_NONE,
- DISTANCE_CALIBRATION_SCALED,
+ enum class DistanceCalibration {
+ DEFAULT,
+ NONE,
+ SCALED,
};
DistanceCalibration distanceCalibration;
bool haveDistanceScale;
float distanceScale;
- enum CoverageCalibration {
- COVERAGE_CALIBRATION_DEFAULT,
- COVERAGE_CALIBRATION_NONE,
- COVERAGE_CALIBRATION_BOX,
+ enum class CoverageCalibration {
+ DEFAULT,
+ NONE,
+ BOX,
};
CoverageCalibration coverageCalibration;
@@ -524,16 +526,16 @@
uint64_t distance : 48; // squared distance
};
- enum PointerUsage {
- POINTER_USAGE_NONE,
- POINTER_USAGE_GESTURES,
- POINTER_USAGE_STYLUS,
- POINTER_USAGE_MOUSE,
+ enum class PointerUsage {
+ NONE,
+ GESTURES,
+ STYLUS,
+ MOUSE,
};
PointerUsage mPointerUsage;
struct PointerGesture {
- enum Mode {
+ enum class Mode {
// No fingers, button is not pressed.
// Nothing happening.
NEUTRAL,
@@ -646,9 +648,9 @@
firstTouchTime = LLONG_MIN;
activeTouchId = -1;
activeGestureId = -1;
- currentGestureMode = NEUTRAL;
+ currentGestureMode = Mode::NEUTRAL;
currentGestureIdBits.clear();
- lastGestureMode = NEUTRAL;
+ lastGestureMode = Mode::NEUTRAL;
lastGestureIdBits.clear();
downTime = 0;
velocityTracker.clear();
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 7665680..ac7c266 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -39,23 +39,17 @@
// TODO: Handle FF_STATUS, although it does not seem to be widely supported.
}
-void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+void VibratorInputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
int32_t token) {
#if DEBUG_VIBRATOR
std::string patternStr;
- for (size_t i = 0; i < patternSize; i++) {
- if (i != 0) {
- patternStr += ", ";
- }
- patternStr += StringPrintf("%" PRId64, pattern[i]);
- }
+ dumpPattern(patternStr);
ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
patternStr.c_str(), repeat, token);
#endif
mVibrating = true;
- memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
- mPatternSize = patternSize;
+ mPattern = pattern;
mRepeat = repeat;
mToken = token;
mIndex = -1;
@@ -85,7 +79,7 @@
void VibratorInputMapper::nextStep() {
mIndex += 1;
- if (size_t(mIndex) >= mPatternSize) {
+ if (size_t(mIndex) >= mPattern.size()) {
if (mRepeat < 0) {
// We are done.
stopVibrating();
@@ -94,13 +88,14 @@
mIndex = mRepeat;
}
- bool vibratorOn = mIndex & 1;
- nsecs_t duration = mPattern[mIndex];
- if (vibratorOn) {
+ const VibrationElement& element = mPattern[mIndex];
+ if (element.isOn()) {
#if DEBUG_VIBRATOR
- ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration);
+ std::string description = element.toString();
+ ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
+ description.c_str());
#endif
- getDeviceContext().vibrate(duration);
+ getDeviceContext().vibrate(element);
} else {
#if DEBUG_VIBRATOR
ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
@@ -108,10 +103,12 @@
getDeviceContext().cancelVibrate();
}
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- mNextStepTime = now + duration;
+ std::chrono::nanoseconds duration =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration);
+ mNextStepTime = now + duration.count();
getContext()->requestTimeoutAtTime(mNextStepTime);
#if DEBUG_VIBRATOR
- ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
+ ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
#endif
}
@@ -126,6 +123,25 @@
void VibratorInputMapper::dump(std::string& dump) {
dump += INDENT2 "Vibrator Input Mapper:\n";
dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating));
+ if (mVibrating) {
+ dump += INDENT3 "Pattern: ";
+ dumpPattern(dump);
+ dump += "\n";
+ dump += StringPrintf(INDENT3 "Repeat Index: %zd\n", mRepeat);
+ }
+}
+
+void VibratorInputMapper::dumpPattern(std::string& dump) const {
+ dump += "[";
+
+ for (auto it = mPattern.begin(); it != mPattern.end(); ++it) {
+ dump += it->toString();
+ if (std::next(it) != mPattern.end()) {
+ dump += ", ";
+ }
+ }
+
+ dump += "]";
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index f69fdde..bfa5ec1 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -30,7 +30,7 @@
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
virtual void process(const RawEvent* rawEvent) override;
- virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ virtual void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
int32_t token) override;
virtual void cancelVibrate(int32_t token) override;
virtual void timeoutExpired(nsecs_t when) override;
@@ -38,13 +38,13 @@
private:
bool mVibrating;
- nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
- size_t mPatternSize;
+ std::vector<VibrationElement> mPattern;
ssize_t mRepeat;
int32_t mToken;
ssize_t mIndex;
nsecs_t mNextStepTime;
+ void dumpPattern(std::string& dump) const;
void nextStep();
void stopVibrating();
};
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index a0d2f4f..6465cc9 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -30,12 +30,22 @@
"AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
"EventHub_test.cpp",
- "TestInputListener.cpp",
+ "IInputFlingerQuery.aidl",
"InputClassifier_test.cpp",
"InputClassifierConverter_test.cpp",
"InputDispatcher_test.cpp",
"InputReader_test.cpp",
+ "InputFlingerService_test.cpp",
+ "TestInputListener.cpp",
"UinputDevice.cpp",
],
+ aidl: {
+ include_dirs: [
+ "frameworks/native/libs/input",
+ ],
+ },
+ static_libs: [
+ "libc++fs"
+ ],
require_root: true,
}
diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/services/inputflinger/tests/BlockingQueue_test.cpp
index 0dea8d7..fd9d9d5 100644
--- a/services/inputflinger/tests/BlockingQueue_test.cpp
+++ b/services/inputflinger/tests/BlockingQueue_test.cpp
@@ -26,7 +26,7 @@
// --- BlockingQueueTest ---
/**
- * Sanity check of basic pop and push operation.
+ * Validate basic pop and push operation.
*/
TEST(BlockingQueueTest, Queue_AddAndRemove) {
constexpr size_t capacity = 10;
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 71731b0..ef68a84 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -199,3 +199,76 @@
lastEventTime = event.when; // Ensure all returned events are monotonic
}
}
+
+// --- BitArrayTest ---
+class BitArrayTest : public testing::Test {
+protected:
+ static constexpr size_t SINGLE_ELE_BITS = 32UL;
+ static constexpr size_t MULTI_ELE_BITS = 256UL;
+
+ virtual void SetUp() override {
+ mBitmaskSingle.loadFromBuffer(mBufferSingle);
+ mBitmaskMulti.loadFromBuffer(mBufferMulti);
+ }
+
+ android::BitArray<SINGLE_ELE_BITS> mBitmaskSingle;
+ android::BitArray<MULTI_ELE_BITS> mBitmaskMulti;
+
+private:
+ const typename android::BitArray<SINGLE_ELE_BITS>::Buffer mBufferSingle = {
+ 0x800F0F0FUL // bit 0 - 31
+ };
+ const typename android::BitArray<MULTI_ELE_BITS>::Buffer mBufferMulti = {
+ 0xFFFFFFFFUL, // bit 0 - 31
+ 0x01000001UL, // bit 32 - 63
+ 0x00000000UL, // bit 64 - 95
+ 0x80000000UL, // bit 96 - 127
+ 0x00000000UL, // bit 128 - 159
+ 0x00000000UL, // bit 160 - 191
+ 0x80000008UL, // bit 192 - 223
+ 0x00000000UL, // bit 224 - 255
+ };
+};
+
+TEST_F(BitArrayTest, SetBit) {
+ ASSERT_TRUE(mBitmaskSingle.test(0));
+ ASSERT_TRUE(mBitmaskSingle.test(31));
+ ASSERT_FALSE(mBitmaskSingle.test(7));
+
+ ASSERT_TRUE(mBitmaskMulti.test(32));
+ ASSERT_TRUE(mBitmaskMulti.test(56));
+ ASSERT_FALSE(mBitmaskMulti.test(192));
+ ASSERT_TRUE(mBitmaskMulti.test(223));
+ ASSERT_FALSE(mBitmaskMulti.test(255));
+}
+
+TEST_F(BitArrayTest, AnyBit) {
+ ASSERT_TRUE(mBitmaskSingle.any(31, 32));
+ ASSERT_FALSE(mBitmaskSingle.any(12, 16));
+
+ ASSERT_TRUE(mBitmaskMulti.any(31, 32));
+ ASSERT_FALSE(mBitmaskMulti.any(33, 33));
+ ASSERT_TRUE(mBitmaskMulti.any(32, 55));
+ ASSERT_TRUE(mBitmaskMulti.any(33, 57));
+ ASSERT_FALSE(mBitmaskMulti.any(33, 55));
+ ASSERT_FALSE(mBitmaskMulti.any(130, 190));
+
+ ASSERT_FALSE(mBitmaskMulti.any(128, 195));
+ ASSERT_TRUE(mBitmaskMulti.any(128, 196));
+ ASSERT_TRUE(mBitmaskMulti.any(128, 224));
+ ASSERT_FALSE(mBitmaskMulti.any(255, 256));
+}
+
+TEST_F(BitArrayTest, SetBit_InvalidBitIndex) {
+ ASSERT_FALSE(mBitmaskSingle.test(32));
+ ASSERT_FALSE(mBitmaskMulti.test(256));
+}
+
+TEST_F(BitArrayTest, AnyBit_InvalidBitIndex) {
+ ASSERT_FALSE(mBitmaskSingle.any(32, 32));
+ ASSERT_FALSE(mBitmaskSingle.any(33, 34));
+
+ ASSERT_FALSE(mBitmaskMulti.any(256, 256));
+ ASSERT_FALSE(mBitmaskMulti.any(257, 258));
+ ASSERT_FALSE(mBitmaskMulti.any(0, 0));
+}
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
new file mode 100644
index 0000000..b5c5c9e
--- /dev/null
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.FocusRequest;
+import android.InputChannel;
+import android.InputWindowInfo;
+import android.os.ISetInputWindowsListener;
+
+/** @hide */
+interface IInputFlingerQuery
+{
+ /* Test interfaces */
+ void getInputWindows(out InputWindowInfo[] inputHandles);
+ void getInputChannels(out InputChannel[] channels);
+ void getLastFocusRequest(out FocusRequest request);
+}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 1a133dc..c7bb2ac 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -29,6 +29,7 @@
#include <vector>
using android::base::StringPrintf;
+using namespace android::flag_operators;
namespace android::inputdispatcher {
@@ -123,16 +124,17 @@
// This function must be called soon after the expected ANR timer starts,
// because we are also checking how much time has passed.
- void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout,
- const sp<InputApplicationHandle>& expectedApplication,
- const sp<IBinder>& expectedToken) {
- std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData;
+ void assertNotifyAnrWasCalled(
+ std::chrono::nanoseconds timeout,
+ const std::shared_ptr<InputApplicationHandle>& expectedApplication,
+ const sp<IBinder>& expectedToken) {
+ std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData;
ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout));
ASSERT_EQ(expectedApplication, anrData.first);
ASSERT_EQ(expectedToken, anrData.second);
}
- std::pair<sp<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData(
+ std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData(
std::chrono::nanoseconds timeout) {
const std::chrono::time_point start = std::chrono::steady_clock::now();
std::unique_lock lock(mLock);
@@ -160,7 +162,7 @@
<< std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
<< "ms instead";
}
- std::pair<sp<InputApplicationHandle>, sp<IBinder>> result =
+ std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> result =
std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front());
mAnrApplications.pop();
mAnrWindowTokens.pop();
@@ -188,7 +190,7 @@
std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
// ANR handling
- std::queue<sp<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
+ std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
std::condition_variable mNotifyAnr;
std::chrono::nanoseconds mAnrTimeout = 0ms;
@@ -198,13 +200,14 @@
mConfigurationChangedTime = when;
}
- virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& application,
- const sp<IBinder>& windowToken, const std::string&) override {
+ std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>& application,
+ const sp<IBinder>& windowToken,
+ const std::string&) override {
std::scoped_lock lock(mLock);
mAnrApplications.push(application);
mAnrWindowTokens.push(windowToken);
mNotifyAnr.notify_all();
- return mAnrTimeout.count();
+ return mAnrTimeout;
}
virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
@@ -431,10 +434,11 @@
constexpr int32_t metaState = AMETA_NONE;
constexpr MotionClassification classification = MotionClassification::NONE;
+ ui::Transform identityTransform;
// Rejects undefined motion actions.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
- /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
@@ -446,10 +450,10 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
@@ -458,10 +462,10 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
@@ -471,10 +475,10 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_UP |
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
@@ -483,10 +487,10 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_UP |
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
@@ -495,9 +499,8 @@
// Rejects motion events with invalid number of pointers.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -506,9 +509,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -519,9 +521,8 @@
pointerProperties[0].id = -1;
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -531,9 +532,8 @@
pointerProperties[0].id = MAX_POINTER_ID + 1;
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -545,9 +545,8 @@
pointerProperties[1].id = 1;
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -585,7 +584,8 @@
FakeApplicationHandle() {
mInfo.name = "Fake Application";
mInfo.token = new BBinder();
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+ mInfo.dispatchingTimeoutMillis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
}
virtual ~FakeApplicationHandle() {}
@@ -593,14 +593,15 @@
return true;
}
- void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
- mInfo.dispatchingTimeout = timeout.count();
+ void setDispatchingTimeout(std::chrono::milliseconds timeout) {
+ mInfo.dispatchingTimeoutMillis = timeout.count();
}
};
class FakeInputReceiver {
public:
- explicit FakeInputReceiver(const sp<InputChannel>& clientChannel, const std::string name)
+ explicit FakeInputReceiver(const std::shared_ptr<InputChannel>& clientChannel,
+ const std::string name)
: mName(name) {
mConsumer = std::make_unique<InputConsumer>(clientChannel);
}
@@ -747,31 +748,31 @@
static const int32_t WIDTH = 600;
static const int32_t HEIGHT = 800;
- FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+ FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
const sp<InputDispatcher>& dispatcher, const std::string name,
- int32_t displayId, sp<IBinder> token = nullptr)
+ int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt)
: mName(name) {
- if (token == nullptr) {
- sp<InputChannel> serverChannel, clientChannel;
+ if (token == std::nullopt) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
- dispatcher->registerInputChannel(serverChannel);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
token = serverChannel->getConnectionToken();
+ dispatcher->registerInputChannel(std::move(serverChannel));
}
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *inputApplicationHandle->getInfo();
- mInfo.token = token;
+ mInfo.token = *token;
mInfo.id = sId++;
mInfo.name = name;
- mInfo.layoutParamsFlags = 0;
- mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+ mInfo.type = InputWindowInfo::Type::APPLICATION;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
mInfo.frameLeft = 0;
mInfo.frameTop = 0;
mInfo.frameRight = WIDTH;
mInfo.frameBottom = HEIGHT;
+ mInfo.transform.set(0, 0);
mInfo.globalScaleFactor = 1.0;
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
@@ -782,7 +783,6 @@
mInfo.paused = false;
mInfo.ownerPid = INJECTOR_PID;
mInfo.ownerUid = INJECTOR_UID;
- mInfo.inputFeatures = 0;
mInfo.displayId = displayId;
}
@@ -791,7 +791,7 @@
void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; }
void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
- mInfo.dispatchingTimeout = timeout.count();
+ mInfo.dispatchingTimeout = timeout;
}
void setPaused(bool paused) { mInfo.paused = paused; }
@@ -801,17 +801,21 @@
mInfo.frameTop = frame.top;
mInfo.frameRight = frame.right;
mInfo.frameBottom = frame.bottom;
+ mInfo.transform.set(frame.left, frame.top);
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(frame);
}
- void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; }
+ void setFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags = flags; }
- void setWindowScale(float xScale, float yScale) {
- mInfo.windowXScale = xScale;
- mInfo.windowYScale = yScale;
+ void setInputFeatures(InputWindowInfo::Feature features) { mInfo.inputFeatures = features; }
+
+ void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
+ mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
}
+ void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); }
+
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
expectedFlags);
@@ -893,8 +897,12 @@
}
void assertNoEvents() {
- ASSERT_NE(mInputReceiver, nullptr)
- << "Call 'assertNoEvents' on a window with an InputReceiver";
+ if (mInputReceiver == nullptr &&
+ mInfo.inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL)) {
+ return; // Can't receive events if the window does not have input channel
+ }
+ ASSERT_NE(nullptr, mInputReceiver)
+ << "Window without InputReceiver must specify feature NO_INPUT_CHANNEL";
mInputReceiver->assertNoEvents();
}
@@ -938,6 +946,126 @@
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
}
+class PointerBuilder {
+public:
+ PointerBuilder(int32_t id, int32_t toolType) {
+ mProperties.clear();
+ mProperties.id = id;
+ mProperties.toolType = toolType;
+ mCoords.clear();
+ }
+
+ PointerBuilder& x(float x) { return axis(AMOTION_EVENT_AXIS_X, x); }
+
+ PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
+
+ PointerBuilder& axis(int32_t axis, float value) {
+ mCoords.setAxisValue(axis, value);
+ return *this;
+ }
+
+ PointerProperties buildProperties() const { return mProperties; }
+
+ PointerCoords buildCoords() const { return mCoords; }
+
+private:
+ PointerProperties mProperties;
+ PointerCoords mCoords;
+};
+
+class MotionEventBuilder {
+public:
+ MotionEventBuilder(int32_t action, int32_t source) {
+ mAction = action;
+ mSource = source;
+ mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
+ MotionEventBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ MotionEventBuilder& displayId(int32_t displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ MotionEventBuilder& actionButton(int32_t actionButton) {
+ mActionButton = actionButton;
+ return *this;
+ }
+
+ MotionEventBuilder& buttonState(int32_t actionButton) {
+ mActionButton = actionButton;
+ return *this;
+ }
+
+ MotionEventBuilder& rawXCursorPosition(float rawXCursorPosition) {
+ mRawXCursorPosition = rawXCursorPosition;
+ return *this;
+ }
+
+ MotionEventBuilder& rawYCursorPosition(float rawYCursorPosition) {
+ mRawYCursorPosition = rawYCursorPosition;
+ return *this;
+ }
+
+ MotionEventBuilder& pointer(PointerBuilder pointer) {
+ mPointers.push_back(pointer);
+ return *this;
+ }
+
+ MotionEvent build() {
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+ for (const PointerBuilder& pointer : mPointers) {
+ pointerProperties.push_back(pointer.buildProperties());
+ pointerCoords.push_back(pointer.buildCoords());
+ }
+
+ // Set mouse cursor position for the most common cases to avoid boilerplate.
+ if (mSource == AINPUT_SOURCE_MOUSE &&
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
+ mPointers.size() == 1) {
+ mRawXCursorPosition = pointerCoords[0].getX();
+ mRawYCursorPosition = pointerCoords[0].getY();
+ }
+
+ MotionEvent event;
+ ui::Transform identityTransform;
+ event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC,
+ mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+ mButtonState, MotionClassification::NONE, identityTransform,
+ /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
+ mRawYCursorPosition, mEventTime, mEventTime, mPointers.size(),
+ pointerProperties.data(), pointerCoords.data());
+
+ return event;
+ }
+
+private:
+ int32_t mAction;
+ int32_t mSource;
+ nsecs_t mEventTime;
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ int32_t mActionButton{0};
+ int32_t mButtonState{0};
+ float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+
+ std::vector<PointerBuilder> mPointers;
+};
+
+static int32_t injectMotionEvent(
+ const sp<InputDispatcher>& dispatcher, const MotionEvent& event,
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+ int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT) {
+ return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
+ injectionTimeout,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+}
+
static int32_t injectMotionEvent(
const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
const PointF& position,
@@ -946,32 +1074,18 @@
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
- MotionEvent event;
- PointerProperties pointerProperties[1];
- PointerCoords pointerCoords[1];
-
- pointerProperties[0].clear();
- pointerProperties[0].id = 0;
- pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
-
- // Define a valid motion down event.
- event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action,
- /* actionButton */ 0,
- /* flags */ 0,
- /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
- /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y,
- eventTime, eventTime,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ MotionEvent event = MotionEventBuilder(action, source)
+ .displayId(displayId)
+ .eventTime(eventTime)
+ .rawXCursorPosition(cursorPosition.x)
+ .rawYCursorPosition(cursorPosition.y)
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(position.x)
+ .y(position.y))
+ .build();
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
- injectionTimeout,
- POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ return injectMotionEvent(dispatcher, event);
}
static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
@@ -1032,7 +1146,7 @@
}
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
ADISPLAY_ID_DEFAULT);
@@ -1054,11 +1168,11 @@
* called twice.
*/
TEST_F(InputDispatcherTest, SetInputWindowOnce_SingleWindowTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
@@ -1077,11 +1191,11 @@
* when finding touched windows.
*/
TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
- window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -1096,7 +1210,7 @@
// The foreground window should receive the first touch down event.
TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ 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",
@@ -1113,7 +1227,7 @@
}
TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ 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",
@@ -1136,7 +1250,7 @@
}
TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ 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",
@@ -1160,7 +1274,7 @@
}
TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
ADISPLAY_ID_DEFAULT);
@@ -1186,17 +1300,209 @@
windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
}
+TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowLeft =
+ new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ windowLeft->setFrame(Rect(0, 0, 600, 800));
+ windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+ sp<FakeWindowHandle> windowRight =
+ new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ windowRight->setFrame(Rect(600, 0, 1200, 800));
+ windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
+
+ // Start cursor position in right window so that we can move the cursor to left window.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(900)
+ .y(400))
+ .build()));
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ // Move cursor into left window
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ // Inject a series of mouse events for a mouse click
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ // Move mouse cursor back to right window
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(900)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+}
+
+// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
+// directly in this test.
+TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 1200, 800));
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ // Inject a series of mouse events for a mouse click
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+}
+
TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowLeft =
new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
windowLeft->setFrame(Rect(0, 0, 600, 800));
- windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
sp<FakeWindowHandle> windowRight =
new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
windowRight->setFrame(Rect(600, 0, 1200, 800));
- windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -1212,7 +1518,7 @@
}
TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocus(true);
@@ -1235,7 +1541,7 @@
}
TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
@@ -1258,7 +1564,7 @@
}
TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
// Create a couple of windows
sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
@@ -1293,7 +1599,7 @@
}
TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
PointF touchPoint = {10, 10};
@@ -1349,21 +1655,21 @@
}
TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
// Create a non touch modal window that supports split touch
sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
"First Window", ADISPLAY_ID_DEFAULT);
firstWindow->setFrame(Rect(0, 0, 600, 400));
- firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
- | InputWindowInfo::FLAG_SPLIT_TOUCH);
+ firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
// Create a non touch modal window that supports split touch
sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
"Second Window", ADISPLAY_ID_DEFAULT);
secondWindow->setFrame(Rect(0, 400, 600, 800));
- secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
- | InputWindowInfo::FLAG_SPLIT_TOUCH);
+ secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
// Add the windows to the dispatcher
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -1413,7 +1719,7 @@
}
TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
@@ -1430,7 +1736,7 @@
}
TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
@@ -1445,7 +1751,7 @@
// If a window is touchable, but does not have focus, it should receive motion events, but not keys
TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
@@ -1469,10 +1775,10 @@
public:
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
int32_t displayId, bool isGestureMonitor = false) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
- dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
+ dispatcher->registerInputMonitor(std::move(serverChannel), displayId, isGestureMonitor);
}
sp<IBinder> getToken() { return mInputReceiver->getToken(); }
@@ -1504,7 +1810,7 @@
// Tests for gesture monitors
TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -1520,7 +1826,7 @@
}
TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
@@ -1540,7 +1846,7 @@
}
TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -1580,7 +1886,7 @@
}
TEST_F(InputDispatcherTest, TestMoveEvent) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
@@ -1611,7 +1917,7 @@
* and the action of enabling / disabling.
*/
TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
@@ -1649,7 +1955,7 @@
}
TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
@@ -1686,7 +1992,7 @@
}
TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
@@ -1727,7 +2033,7 @@
static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
static constexpr nsecs_t KEY_REPEAT_DELAY = 40 * 1000000; // 40 ms
- sp<FakeApplicationHandle> mApp;
+ std::shared_ptr<FakeApplicationHandle> mApp;
sp<FakeWindowHandle> mWindow;
virtual void SetUp() override {
@@ -1741,7 +2047,7 @@
}
void setUpWindow() {
- mApp = new FakeApplicationHandle();
+ mApp = std::make_shared<FakeApplicationHandle>();
mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mWindow->setFocus(true);
@@ -1828,7 +2134,7 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- application1 = new FakeApplicationHandle();
+ application1 = std::make_shared<FakeApplicationHandle>();
windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
ADISPLAY_ID_DEFAULT);
@@ -1838,7 +2144,7 @@
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}});
windowInPrimary->consumeFocusEvent(true);
- application2 = new FakeApplicationHandle();
+ application2 = std::make_shared<FakeApplicationHandle>();
windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
SECOND_DISPLAY_ID);
// Set focus to second display window.
@@ -1854,16 +2160,16 @@
virtual void TearDown() override {
InputDispatcherTest::TearDown();
- application1.clear();
+ application1.reset();
windowInPrimary.clear();
- application2.clear();
+ application2.reset();
windowInSecondary.clear();
}
protected:
- sp<FakeApplicationHandle> application1;
+ std::shared_ptr<FakeApplicationHandle> application1;
sp<FakeWindowHandle> windowInPrimary;
- sp<FakeApplicationHandle> application2;
+ std::shared_ptr<FakeApplicationHandle> application2;
sp<FakeWindowHandle> windowInSecondary;
};
@@ -2042,18 +2348,19 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application =
+ std::make_shared<FakeApplicationHandle>();
mUnfocusedWindow = new FakeWindowHandle(application, mDispatcher, "Top",
ADISPLAY_ID_DEFAULT);
mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
// Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
// window.
- mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mFocusedWindow =
new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
- mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -2136,19 +2443,20 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ std::shared_ptr<FakeApplicationHandle> application =
+ std::make_shared<FakeApplicationHandle>();
mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1",
ADISPLAY_ID_DEFAULT);
// Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window.
// We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows.
- mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
- InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mWindow1->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
mWindow1->setFrame(Rect(0, 0, 100, 100));
mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
ADISPLAY_ID_DEFAULT, mWindow1->getToken());
- mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
- InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mWindow2->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
mWindow2->setFrame(Rect(100, 100, 200, 200));
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
@@ -2160,9 +2468,8 @@
// Helper function to convert the point from screen coordinates into the window's space
static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) {
- float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft);
- float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop);
- return {x, y};
+ vec2 vals = windowInfo->transform.transform(point.x, point.y);
+ return {vals.x, vals.y};
}
void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
@@ -2192,133 +2499,123 @@
<< ", got " << motionEvent.getY(i);
}
}
+
+ void touchAndAssertPositions(int32_t action, std::vector<PointF> touchedPoints,
+ std::vector<PointF> expectedPoints) {
+ NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Always consume from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, action, expectedPoints);
+ }
};
TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) {
// Touch Window 1
PointF touchedPoint = {10, 10};
PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
// Release touch on Window 1
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- // consume the UP event
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
// Touch Window 2
touchedPoint = {150, 150};
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
-
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
}
-TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) {
+ // Set scale value for window2
mWindow2->setWindowScale(0.5f, 0.5f);
// Touch Window 1
PointF touchedPoint = {10, 10};
PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
-
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
// Release touch on Window 1
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- // consume the UP event
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
// Touch Window 2
touchedPoint = {150, 150};
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+ // Update the transform so rotation is set
+ mWindow2->setWindowTransform(0, -1, 1, 0);
+ expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
}
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform) {
mWindow2->setWindowScale(0.5f, 0.5f);
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
int32_t actionPointerDown =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchedPoints.push_back(PointF{150, 150});
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
+ // Release Window 2
+ int32_t actionPointerUp =
+ AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+ expectedPoints.pop_back();
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+ // Update the transform so rotation is set for Window 2
+ mWindow2->setWindowTransform(0, -1, 1, 0);
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
}
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) {
mWindow2->setWindowScale(0.5f, 0.5f);
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
int32_t actionPointerDown =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchedPoints.push_back(PointF{150, 150});
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+ // Release Window 2
+ int32_t actionPointerUp =
+ AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+ expectedPoints.pop_back();
+
+ // Touch Window 2
+ mWindow2->setWindowTransform(0, -1, 1, 0);
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
@@ -2327,52 +2624,38 @@
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
int32_t actionPointerDown =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchedPoints.push_back(PointF{150, 150});
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
}
class InputDispatcherSingleWindowAnr : public InputDispatcherTest {
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- mApplication = new FakeApplicationHandle();
+ mApplication = std::make_shared<FakeApplicationHandle>();
mApplication->setDispatchingTimeout(20ms);
mWindow =
new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
mWindow->setFrame(Rect(0, 0, 30, 30));
- mWindow->setDispatchingTimeout(10ms);
+ mWindow->setDispatchingTimeout(30ms);
mWindow->setFocus(true);
// Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
// window.
- mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
@@ -2387,7 +2670,7 @@
}
protected:
- sp<FakeApplicationHandle> mApplication;
+ std::shared_ptr<FakeApplicationHandle> mApplication;
sp<FakeWindowHandle> mWindow;
static constexpr PointF WINDOW_LOCATION = {20, 20};
@@ -2746,7 +3029,7 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- mApplication = new FakeApplicationHandle();
+ mApplication = std::make_shared<FakeApplicationHandle>();
mApplication->setDispatchingTimeout(10ms);
mUnfocusedWindow =
new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT);
@@ -2754,16 +3037,16 @@
// Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
// window.
// Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped
- mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
- InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH |
- InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
mFocusedWindow =
new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT);
- mFocusedWindow->setDispatchingTimeout(10ms);
+ mFocusedWindow->setDispatchingTimeout(30ms);
mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
- mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
- InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
@@ -2782,7 +3065,7 @@
}
protected:
- sp<FakeApplicationHandle> mApplication;
+ std::shared_ptr<FakeApplicationHandle> mApplication;
sp<FakeWindowHandle> mUnfocusedWindow;
sp<FakeWindowHandle> mFocusedWindow;
static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20};
@@ -2846,9 +3129,9 @@
tapOnFocusedWindow();
// we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
- std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData1 =
+ std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData1 =
mFakePolicy->getNotifyAnrData(10ms);
- std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData2 =
+ std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData2 =
mFakePolicy->getNotifyAnrData(0ms);
// We don't know which window will ANR first. But both of them should happen eventually.
@@ -3031,4 +3314,77 @@
mFocusedWindow->assertNoEvents();
}
+// These tests ensure we cannot send touch events to a window that's positioned behind a window
+// that has feature NO_INPUT_CHANNEL.
+// Layout:
+// Top (closest to user)
+// mNoInputWindow (above all windows)
+// mBottomWindow
+// Bottom (furthest from user)
+class InputDispatcherMultiWindowOcclusionTests : public InputDispatcherTest {
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ mApplication = std::make_shared<FakeApplicationHandle>();
+ mNoInputWindow = new FakeWindowHandle(mApplication, mDispatcher,
+ "Window without input channel", ADISPLAY_ID_DEFAULT,
+ std::make_optional<sp<IBinder>>(nullptr) /*token*/);
+
+ mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
+ // It's perfectly valid for this window to not have an associated input channel
+
+ mBottomWindow = new FakeWindowHandle(mApplication, mDispatcher, "Bottom window",
+ ADISPLAY_ID_DEFAULT);
+ mBottomWindow->setFrame(Rect(0, 0, 100, 100));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
+ }
+
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApplication;
+ sp<FakeWindowHandle> mNoInputWindow;
+ sp<FakeWindowHandle> mBottomWindow;
+};
+
+TEST_F(InputDispatcherMultiWindowOcclusionTests, NoInputChannelFeature_DropsTouches) {
+ PointF touchedPoint = {10, 10};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ mNoInputWindow->assertNoEvents();
+ // Even though the window 'mNoInputWindow' positioned above 'mBottomWindow' does not have
+ // an input channel, it is not marked as FLAG_NOT_TOUCHABLE,
+ // and therefore should prevent mBottomWindow from receiving touches
+ mBottomWindow->assertNoEvents();
+}
+
+/**
+ * If a window has feature NO_INPUT_CHANNEL, and somehow (by mistake) still has an input channel,
+ * ensure that this window does not receive any touches, and blocks touches to windows underneath.
+ */
+TEST_F(InputDispatcherMultiWindowOcclusionTests,
+ NoInputChannelFeature_DropsTouchesWithValidChannel) {
+ mNoInputWindow = new FakeWindowHandle(mApplication, mDispatcher,
+ "Window with input channel and NO_INPUT_CHANNEL",
+ ADISPLAY_ID_DEFAULT);
+
+ mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
+
+ PointF touchedPoint = {10, 10};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ mNoInputWindow->assertNoEvents();
+ mBottomWindow->assertNoEvents();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
new file mode 100644
index 0000000..a4922fa
--- /dev/null
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <BnInputFlingerQuery.h>
+#include <IInputFlingerQuery.h>
+
+#include <android/os/BnInputFlinger.h>
+#include <android/os/BnSetInputWindowsListener.h>
+#include <android/os/IInputFlinger.h>
+#include <android/os/ISetInputWindowsListener.h>
+
+#include <binder/Binder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
+
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <chrono>
+#include <thread>
+#include <unordered_map>
+
+#define TAG "InputFlingerServiceTest"
+
+using android::os::BnInputFlinger;
+using android::os::BnSetInputWindowsListener;
+using android::os::IInputFlinger;
+using android::os::ISetInputWindowsListener;
+
+using std::chrono_literals::operator""ms;
+using std::chrono_literals::operator""s;
+
+namespace android {
+
+static const sp<IBinder> TestInfoToken = new BBinder();
+static const sp<IBinder> FocusedTestInfoToken = new BBinder();
+static constexpr int32_t TestInfoId = 1;
+static const std::string TestInfoName = "InputFlingerServiceTestInputWindowInfo";
+static constexpr Flags<InputWindowInfo::Flag> TestInfoFlags = InputWindowInfo::Flag::NOT_FOCUSABLE;
+static constexpr InputWindowInfo::Type TestInfoType = InputWindowInfo::Type::INPUT_METHOD;
+static constexpr std::chrono::duration TestInfoDispatchingTimeout = 2532ms;
+static constexpr int32_t TestInfoFrameLeft = 93;
+static constexpr int32_t TestInfoFrameTop = 34;
+static constexpr int32_t TestInfoFrameRight = 16;
+static constexpr int32_t TestInfoFrameBottom = 19;
+static constexpr int32_t TestInfoSurfaceInset = 17;
+static constexpr float TestInfoGlobalScaleFactor = 0.3;
+static constexpr float TestInfoWindowXScale = 0.4;
+static constexpr float TestInfoWindowYScale = 0.5;
+static const Rect TestInfoTouchableRegionRect = {100 /* left */, 150 /* top */, 400 /* right */,
+ 450 /* bottom */};
+static const Region TestInfoTouchableRegion(TestInfoTouchableRegionRect);
+static constexpr bool TestInfoVisible = false;
+static constexpr bool TestInfoCanReceiveKeys = false;
+static constexpr bool TestInfoTrustedOverlay = true;
+static constexpr bool TestInfoHasFocus = false;
+static constexpr bool TestInfoHasWallpaper = false;
+static constexpr bool TestInfoPaused = false;
+static constexpr int32_t TestInfoOwnerPid = 19;
+static constexpr int32_t TestInfoOwnerUid = 24;
+static constexpr InputWindowInfo::Feature TestInfoInputFeatures =
+ InputWindowInfo::Feature::NO_INPUT_CHANNEL;
+static constexpr int32_t TestInfoDisplayId = 34;
+static constexpr int32_t TestInfoPortalToDisplayId = 2;
+static constexpr bool TestInfoReplaceTouchableRegionWithCrop = true;
+static const sp<IBinder> TestInfoTouchableRegionCropHandle = new BBinder();
+
+static const std::string TestAppInfoName = "InputFlingerServiceTestInputApplicationInfo";
+static const sp<IBinder> TestAppInfoToken = new BBinder();
+static constexpr std::chrono::duration TestAppInfoDispatchingTimeout = 12345678ms;
+
+static const String16 kTestServiceName = String16("InputFlingerService");
+static const String16 kQueryServiceName = String16("InputFlingerQueryService");
+
+struct SetInputWindowsListener;
+// --- InputFlingerServiceTest ---
+class InputFlingerServiceTest : public testing::Test {
+public:
+ void SetUp() override;
+ void TearDown() override;
+
+protected:
+ void InitializeInputFlinger();
+ void setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos);
+ void setFocusedWindow(const sp<IBinder> token, const sp<IBinder> focusedToken,
+ nsecs_t timestampNanos);
+
+ void setInputWindowsFinished();
+ void verifyInputWindowInfo(const InputWindowInfo& info) const;
+ InputWindowInfo& getInfo() const { return const_cast<InputWindowInfo&>(mInfo); }
+
+ sp<IInputFlinger> mService;
+ sp<IInputFlingerQuery> mQuery;
+
+private:
+ sp<SetInputWindowsListener> mSetInputWindowsListener;
+ std::unique_ptr<InputChannel> mServerChannel, mClientChannel;
+ InputWindowInfo mInfo;
+ std::mutex mLock;
+ std::condition_variable mSetInputWindowsFinishedCondition;
+};
+
+struct SetInputWindowsListener : BnSetInputWindowsListener {
+ explicit SetInputWindowsListener(std::function<void()> cbFunc) : mCbFunc(cbFunc) {}
+
+ binder::Status onSetInputWindowsFinished() override;
+
+ std::function<void()> mCbFunc;
+};
+
+class TestInputManager : public BnInputFlinger {
+protected:
+ virtual ~TestInputManager(){};
+
+public:
+ TestInputManager(){};
+ void checkFdFlags(const android::base::unique_fd& fd);
+
+ binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles);
+ binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
+ binder::Status getLastFocusRequest(FocusRequest*);
+
+ status_t dump(int fd, const Vector<String16>& args) override;
+
+ binder::Status setInputWindows(
+ const std::vector<InputWindowInfo>& handles,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
+
+ binder::Status registerInputChannel(const InputChannel& channel) override;
+ binder::Status unregisterInputChannel(const InputChannel& channel) override;
+ binder::Status setFocusedWindow(const FocusRequest&) override;
+
+private:
+ mutable Mutex mLock;
+ std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay;
+ std::vector<std::shared_ptr<InputChannel>> mInputChannels;
+ FocusRequest mFocusRequest;
+};
+
+class TestInputQuery : public BnInputFlingerQuery {
+public:
+ TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){};
+ binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override;
+ binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
+ binder::Status getLastFocusRequest(FocusRequest*) override;
+
+private:
+ sp<android::TestInputManager> mManager;
+};
+
+binder::Status TestInputQuery::getInputWindows(
+ std::vector<::android::InputWindowInfo>* inputHandles) {
+ return mManager->getInputWindows(inputHandles);
+}
+
+binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) {
+ return mManager->getInputChannels(channels);
+}
+
+binder::Status TestInputQuery::getLastFocusRequest(FocusRequest* request) {
+ return mManager->getLastFocusRequest(request);
+}
+
+binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
+ if (mCbFunc != nullptr) {
+ mCbFunc();
+ }
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::setInputWindows(
+ const std::vector<InputWindowInfo>& infos,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) {
+ AutoMutex _l(mLock);
+
+ for (const auto& info : infos) {
+ mHandlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>());
+ mHandlesPerDisplay[info.displayId].push_back(new InputWindowHandle(info));
+ }
+ if (setInputWindowsListener) {
+ setInputWindowsListener->onSetInputWindowsFinished();
+ }
+ return binder::Status::ok();
+}
+
+void TestInputManager::checkFdFlags(const android::base::unique_fd& fd) {
+ const int result = fcntl(fd, F_GETFL);
+ EXPECT_NE(result, -1);
+ EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
+}
+
+binder::Status TestInputManager::registerInputChannel(const InputChannel& channel) {
+ AutoMutex _l(mLock);
+ // check Fd flags
+ checkFdFlags(channel.getFd());
+
+ mInputChannels.push_back(channel.dup());
+
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::unregisterInputChannel(const InputChannel& channel) {
+ 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; });
+ if (it != mInputChannels.end()) {
+ mInputChannels.erase(it);
+ }
+
+ return binder::Status::ok();
+}
+
+status_t TestInputManager::dump(int fd, const Vector<String16>& args) {
+ std::string dump;
+
+ dump += " InputFlinger dump\n";
+
+ ::write(fd, dump.c_str(), dump.size());
+ return NO_ERROR;
+}
+
+binder::Status TestInputManager::getInputWindows(
+ std::vector<::android::InputWindowInfo>* inputInfos) {
+ for (auto& [displayId, inputHandles] : mHandlesPerDisplay) {
+ for (auto& inputHandle : inputHandles) {
+ inputInfos->push_back(*inputHandle->getInfo());
+ }
+ }
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) {
+ channels->clear();
+ for (std::shared_ptr<InputChannel>& channel : mInputChannels) {
+ channels->push_back(*channel);
+ }
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::getLastFocusRequest(FocusRequest* request) {
+ *request = mFocusRequest;
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) {
+ mFocusRequest = request;
+ return binder::Status::ok();
+}
+
+void InputFlingerServiceTest::SetUp() {
+ mSetInputWindowsListener = new SetInputWindowsListener([&]() {
+ std::unique_lock<std::mutex> lock(mLock);
+ mSetInputWindowsFinishedCondition.notify_all();
+ });
+ InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
+
+ mInfo.token = TestInfoToken;
+ mInfo.id = TestInfoId;
+ mInfo.name = TestInfoName;
+ mInfo.flags = TestInfoFlags;
+ mInfo.type = TestInfoType;
+ mInfo.dispatchingTimeout = TestInfoDispatchingTimeout;
+ mInfo.frameLeft = TestInfoFrameLeft;
+ mInfo.frameTop = TestInfoFrameTop;
+ mInfo.frameRight = TestInfoFrameRight;
+ mInfo.frameBottom = TestInfoFrameBottom;
+ mInfo.surfaceInset = TestInfoSurfaceInset;
+ mInfo.globalScaleFactor = TestInfoGlobalScaleFactor;
+ mInfo.transform.set({TestInfoWindowXScale, 0, TestInfoFrameLeft, 0, TestInfoWindowYScale,
+ TestInfoFrameTop, 0, 0, 1});
+ mInfo.touchableRegion = TestInfoTouchableRegion;
+ mInfo.visible = TestInfoVisible;
+ mInfo.canReceiveKeys = TestInfoCanReceiveKeys;
+ mInfo.trustedOverlay = TestInfoTrustedOverlay;
+ mInfo.hasFocus = TestInfoHasFocus;
+ mInfo.hasWallpaper = TestInfoHasWallpaper;
+ mInfo.paused = TestInfoPaused;
+ mInfo.ownerPid = TestInfoOwnerPid;
+ mInfo.ownerUid = TestInfoOwnerUid;
+ mInfo.inputFeatures = TestInfoInputFeatures;
+ mInfo.displayId = TestInfoDisplayId;
+ mInfo.portalToDisplayId = TestInfoPortalToDisplayId;
+ mInfo.replaceTouchableRegionWithCrop = TestInfoReplaceTouchableRegionWithCrop;
+ mInfo.touchableRegionCropHandle = TestInfoTouchableRegionCropHandle;
+
+ mInfo.applicationInfo.name = TestAppInfoName;
+ mInfo.applicationInfo.token = TestAppInfoToken;
+ mInfo.applicationInfo.dispatchingTimeoutMillis =
+ std::chrono::duration_cast<std::chrono::milliseconds>(TestAppInfoDispatchingTimeout)
+ .count();
+
+ InitializeInputFlinger();
+}
+
+void InputFlingerServiceTest::TearDown() {}
+
+void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const {
+ EXPECT_EQ(mInfo, info);
+}
+
+void InputFlingerServiceTest::InitializeInputFlinger() {
+ sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName));
+ ASSERT_TRUE(input != nullptr);
+ mService = interface_cast<IInputFlinger>(input);
+
+ input = defaultServiceManager()->waitForService(kQueryServiceName);
+ ASSERT_TRUE(input != nullptr);
+ mQuery = interface_cast<IInputFlingerQuery>(input);
+}
+
+void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos) {
+ std::unique_lock<std::mutex> lock(mLock);
+ mService->setInputWindows(infos, mSetInputWindowsListener);
+ // Verify listener call
+ EXPECT_NE(mSetInputWindowsFinishedCondition.wait_for(lock, 1s), std::cv_status::timeout);
+}
+
+void InputFlingerServiceTest::setFocusedWindow(const sp<IBinder> token,
+ const sp<IBinder> focusedToken,
+ nsecs_t timestampNanos) {
+ FocusRequest request;
+ request.token = TestInfoToken;
+ request.focusedToken = focusedToken;
+ request.timestamp = timestampNanos;
+ mService->setFocusedWindow(request);
+ // call set input windows and wait for the callback to drain the queue.
+ setInputWindowsByInfos(std::vector<InputWindowInfo>());
+}
+
+/**
+ * Test InputFlinger service interface SetInputWindows
+ */
+TEST_F(InputFlingerServiceTest, InputWindow_SetInputWindows) {
+ std::vector<InputWindowInfo> infos = {getInfo()};
+ setInputWindowsByInfos(infos);
+
+ // Verify input windows from service
+ std::vector<::android::InputWindowInfo> windowInfos;
+ mQuery->getInputWindows(&windowInfos);
+ for (const ::android::InputWindowInfo& windowInfo : windowInfos) {
+ verifyInputWindowInfo(windowInfo);
+ }
+}
+
+/**
+ * Test InputFlinger service interface registerInputChannel
+ */
+TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannel) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+ InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
+ mService->registerInputChannel(*serverChannel);
+
+ std::vector<::android::InputChannel> channels;
+ mQuery->getInputChannels(&channels);
+ ASSERT_EQ(channels.size(), 1UL);
+ EXPECT_EQ(channels[0], *serverChannel);
+
+ mService->unregisterInputChannel(*serverChannel);
+ mQuery->getInputChannels(&channels);
+ EXPECT_EQ(channels.size(), 0UL);
+}
+
+/**
+ * Test InputFlinger service interface registerInputChannel with invalid cases
+ */
+TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannelInvalid) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+ InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
+
+ std::vector<::android::InputChannel> channels;
+ mQuery->getInputChannels(&channels);
+ EXPECT_EQ(channels.size(), 0UL);
+
+ mService->registerInputChannel(InputChannel());
+ mService->unregisterInputChannel(*clientChannel);
+
+ mService->registerInputChannel(*serverChannel);
+ mService->registerInputChannel(*clientChannel);
+ mQuery->getInputChannels(&channels);
+ EXPECT_EQ(channels.size(), 2UL);
+
+ mService->unregisterInputChannel(*clientChannel);
+ mService->unregisterInputChannel(*serverChannel);
+ mQuery->getInputChannels(&channels);
+ EXPECT_EQ(channels.size(), 0UL);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindow) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ setFocusedWindow(TestInfoToken, nullptr /* focusedToken */, now);
+
+ FocusRequest request;
+ mQuery->getLastFocusRequest(&request);
+
+ EXPECT_EQ(request.token, TestInfoToken);
+ EXPECT_EQ(request.focusedToken, nullptr);
+ EXPECT_EQ(request.timestamp, now);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindowWithFocusedToken) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ setFocusedWindow(TestInfoToken, FocusedTestInfoToken, now);
+
+ FocusRequest request;
+ mQuery->getLastFocusRequest(&request);
+
+ EXPECT_EQ(request.token, TestInfoToken);
+ EXPECT_EQ(request.focusedToken, FocusedTestInfoToken);
+ EXPECT_EQ(request.timestamp, now);
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ pid_t forkPid = fork();
+
+ if (forkPid == 0) {
+ // Server process
+ android::sp<android::TestInputManager> manager = new android::TestInputManager();
+ android::sp<android::TestInputQuery> query = new android::TestInputQuery(manager);
+
+ android::defaultServiceManager()->addService(android::kTestServiceName, manager,
+ false /*allowIsolated*/);
+ android::defaultServiceManager()->addService(android::kQueryServiceName, query,
+ false /*allowIsolated*/);
+ android::ProcessState::self()->startThreadPool();
+ android::IPCThreadState::self()->joinThreadPool();
+ } else {
+ android::ProcessState::self()->startThreadPool();
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ kill(forkPid, SIGKILL);
+ return result;
+ }
+ return 0;
+}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 18bd3d0..3872de5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -37,6 +37,7 @@
namespace android {
using std::chrono_literals::operator""ms;
+using namespace android::flag_operators;
// Timeout for waiting for an expected event
static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
@@ -45,16 +46,24 @@
static const nsecs_t ARBITRARY_TIME = 1234;
// Arbitrary display properties.
-static const int32_t DISPLAY_ID = 0;
-static const int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
-static const int32_t DISPLAY_WIDTH = 480;
-static const int32_t DISPLAY_HEIGHT = 800;
-static const int32_t VIRTUAL_DISPLAY_ID = 1;
-static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
-static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
+static constexpr int32_t DISPLAY_ID = 0;
+static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+static constexpr int32_t DISPLAY_WIDTH = 480;
+static constexpr int32_t DISPLAY_HEIGHT = 800;
+static constexpr int32_t VIRTUAL_DISPLAY_ID = 1;
+static constexpr int32_t VIRTUAL_DISPLAY_WIDTH = 400;
+static constexpr int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
+static constexpr int32_t FIRST_SLOT = 0;
+static constexpr int32_t SECOND_SLOT = 1;
+static constexpr int32_t THIRD_SLOT = 2;
+static constexpr int32_t INVALID_TRACKING_ID = -1;
+static constexpr int32_t FIRST_TRACKING_ID = 0;
+static constexpr int32_t SECOND_TRACKING_ID = 1;
+static constexpr int32_t THIRD_TRACKING_ID = 2;
+
// Error tolerance for floating point assertions.
static const float EPSILON = 0.001f;
@@ -289,6 +298,8 @@
mConfig.defaultPointerDisplayId = pointerDisplayId;
}
+ float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
+
private:
DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
@@ -360,7 +371,7 @@
struct Device {
InputDeviceIdentifier identifier;
- uint32_t classes;
+ Flags<InputDeviceClass> classes;
PropertyMap configuration;
KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
KeyedVector<int, bool> relativeAxes;
@@ -384,9 +395,7 @@
return OK;
}
- explicit Device(uint32_t classes) :
- classes(classes), enabled(true) {
- }
+ explicit Device(Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {}
};
std::mutex mLock;
@@ -406,7 +415,7 @@
FakeEventHub() { }
- void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) {
+ void addDevice(int32_t deviceId, const std::string& name, Flags<InputDeviceClass> classes) {
Device* device = new Device(classes);
device->identifier.name = name;
mDevices.add(deviceId, device);
@@ -582,9 +591,9 @@
return index >= 0 ? mDevices.valueAt(index) : nullptr;
}
- virtual uint32_t getDeviceClasses(int32_t deviceId) const {
+ virtual Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const {
Device* device = getDevice(deviceId);
- return device ? device->classes : 0;
+ return device ? device->classes : Flags<InputDeviceClass>(0);
}
virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const {
@@ -813,8 +822,7 @@
return false;
}
- virtual void vibrate(int32_t, nsecs_t) {
- }
+ virtual void vibrate(int32_t, const VibrationElement&) {}
virtual void cancelVibrate(int32_t) {
}
@@ -1129,17 +1137,17 @@
// --- InstrumentedInputReader ---
class InstrumentedInputReader : public InputReader {
- std::shared_ptr<InputDevice> mNextDevice;
+ std::queue<std::shared_ptr<InputDevice>> mNextDevices;
public:
InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener)
- : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {}
+ : InputReader(eventHub, policy, listener) {}
virtual ~InstrumentedInputReader() {}
- void setNextDevice(std::shared_ptr<InputDevice> device) { mNextDevice = device; }
+ void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); }
std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
const std::string& location = "") {
@@ -1156,9 +1164,9 @@
protected:
virtual std::shared_ptr<InputDevice> createDeviceLocked(
int32_t eventHubId, const InputDeviceIdentifier& identifier) {
- if (mNextDevice) {
- std::shared_ptr<InputDevice> device(mNextDevice);
- mNextDevice = nullptr;
+ if (!mNextDevices.empty()) {
+ std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
+ mNextDevices.pop();
return device;
}
return InputReader::createDeviceLocked(eventHubId, identifier);
@@ -1188,20 +1196,21 @@
// We didn't add any viewports yet, so there shouldn't be any.
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_FALSE(internalViewport);
// Add an internal viewport, then clear it
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, uniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, uniqueId, NO_PORT,
+ ViewportType::INTERNAL);
// Check matching by uniqueId
internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
ASSERT_TRUE(internalViewport);
- ASSERT_EQ(ViewportType::VIEWPORT_INTERNAL, internalViewport->type);
+ ASSERT_EQ(ViewportType::INTERNAL, internalViewport->type);
// Check matching by viewport type
- internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_TRUE(internalViewport);
ASSERT_EQ(uniqueId, internalViewport->uniqueId);
@@ -1209,7 +1218,7 @@
// Make sure nothing is found after clear
internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
ASSERT_FALSE(internalViewport);
- internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_FALSE(internalViewport);
}
@@ -1223,26 +1232,30 @@
// Add an internal viewport
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT,
+ ViewportType::INTERNAL);
// Add an external viewport
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, ViewportType::VIEWPORT_EXTERNAL);
+ DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT,
+ ViewportType::EXTERNAL);
// Add an virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+ DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT,
+ ViewportType::VIRTUAL);
// Add another virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+ DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT,
+ ViewportType::VIRTUAL);
// Check matching by type for internal
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_TRUE(internalViewport);
ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
// Check matching by type for external
std::optional<DisplayViewport> externalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_EXTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::EXTERNAL);
ASSERT_TRUE(externalViewport);
ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
@@ -1250,7 +1263,7 @@
std::optional<DisplayViewport> virtualViewport1 =
mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1);
ASSERT_TRUE(virtualViewport1);
- ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport1->type);
+ ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport1->type);
ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
@@ -1258,7 +1271,7 @@
std::optional<DisplayViewport> virtualViewport2 =
mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2);
ASSERT_TRUE(virtualViewport2);
- ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport2->type);
+ ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport2->type);
ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
}
@@ -1275,8 +1288,8 @@
constexpr int32_t displayId1 = 2;
constexpr int32_t displayId2 = 3;
- std::vector<ViewportType> types = {ViewportType::VIEWPORT_INTERNAL,
- ViewportType::VIEWPORT_EXTERNAL, ViewportType::VIEWPORT_VIRTUAL};
+ std::vector<ViewportType> types = {ViewportType::INTERNAL, ViewportType::EXTERNAL,
+ ViewportType::VIRTUAL};
for (const ViewportType& type : types) {
mFakePolicy->clearViewports();
// Add a viewport
@@ -1314,7 +1327,7 @@
* Check getDisplayViewportByPort
*/
TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
- constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ constexpr ViewportType type = ViewportType::EXTERNAL;
const std::string uniqueId1 = "uniqueId1";
const std::string uniqueId2 = "uniqueId2";
constexpr int32_t displayId1 = 1;
@@ -1372,7 +1385,7 @@
mFakePolicy.clear();
}
- void addDevice(int32_t eventHubId, const std::string& name, uint32_t classes,
+ void addDevice(int32_t eventHubId, const std::string& name, Flags<InputDeviceClass> classes,
const PropertyMap* configuration) {
mFakeEventHub->addDevice(eventHubId, name, classes);
@@ -1397,22 +1410,21 @@
}
FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t eventHubId,
- const std::string& name, uint32_t classes,
- uint32_t sources,
+ const std::string& name,
+ Flags<InputDeviceClass> classes, uint32_t sources,
const PropertyMap* configuration) {
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, name);
FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(eventHubId, sources);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
addDevice(eventHubId, name, classes, configuration);
return mapper;
}
};
TEST_F(InputReaderTest, GetInputDevices) {
- ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard",
- INPUT_DEVICE_CLASS_KEYBOARD, nullptr));
- ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored",
- 0, nullptr)); // no classes so device will be ignored
+ ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0),
+ nullptr)); // no classes so device will be ignored
std::vector<InputDeviceInfo> inputDevices;
mReader->getInputDevices(inputDevices);
@@ -1435,12 +1447,12 @@
TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass(InputDeviceClass::KEYBOARD);
constexpr int32_t eventHubId = 1;
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
// Must add at least one mapper or the device will be ignored!
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
@@ -1472,7 +1484,7 @@
TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1505,7 +1517,7 @@
TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1538,7 +1550,7 @@
TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1571,7 +1583,7 @@
TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1613,7 +1625,7 @@
TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
constexpr int32_t eventHubId = 1;
- addDevice(eventHubId, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
+ addDevice(eventHubId, "ignored", InputDeviceClass::KEYBOARD, nullptr);
NotifyConfigurationChangedArgs args;
@@ -1623,7 +1635,7 @@
TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
@@ -1644,12 +1656,12 @@
TEST_F(InputReaderTest, DeviceReset_RandomId) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
// Must add at least one mapper or the device will be ignored!
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
NotifyDeviceResetArgs resetArgs;
@@ -1677,12 +1689,12 @@
TEST_F(InputReaderTest, DeviceReset_GenerateIdWithInputReaderSource) {
constexpr int32_t deviceId = 1;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
// Must add at least one mapper or the device will be ignored!
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
NotifyDeviceResetArgs resetArgs;
@@ -1692,13 +1704,13 @@
TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
- constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
constexpr int32_t eventHubId = 1;
const char* DEVICE_LOCATION = "USB1";
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
FakeInputMapper& mapper =
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_TOUCHSCREEN);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
const uint8_t hdmi1 = 1;
@@ -1708,9 +1720,11 @@
// Add default and second display.
mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, "local:0", NO_PORT,
+ ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
+ DISPLAY_ORIENTATION_0, "local:1", hdmi1,
+ ViewportType::EXTERNAL);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
mReader->loopOnce();
@@ -1733,6 +1747,73 @@
ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
}
+TEST_F(InputReaderTest, WhenEnabledChanges_AllSubdevicesAreUpdated) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+ // Must add at least one mapper or the device will be ignored!
+ device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+ device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+ mReader->pushNextDevice(device);
+ mReader->pushNextDevice(device);
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
+
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_TRUE(device->isEnabled());
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+
+ disableDevice(deviceId);
+ mReader->loopOnce();
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_FALSE(device->isEnabled());
+ ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+ ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+
+ enableDevice(deviceId);
+ mReader->loopOnce();
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_TRUE(device->isEnabled());
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+}
+
+TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToSubdeviceMappers) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+ // Add two subdevices to device
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+ FakeInputMapper& mapperDevice1 =
+ device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+ FakeInputMapper& mapperDevice2 =
+ device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+ mReader->pushNextDevice(device);
+ mReader->pushNextDevice(device);
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr));
+
+ mapperDevice1.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+ mapperDevice2.setKeyCodeState(AKEYCODE_B, AKEY_STATE_DOWN);
+
+ ASSERT_EQ(AKEY_STATE_DOWN,
+ mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_A));
+ ASSERT_EQ(AKEY_STATE_DOWN,
+ mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_B));
+ ASSERT_EQ(AKEY_STATE_UNKNOWN,
+ mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_C));
+}
+
// --- InputReaderIntegrationTest ---
// These tests create and interact with the InputReader only through its interface.
@@ -1873,10 +1954,6 @@
// --- TouchProcessTest ---
class TouchIntegrationTest : public InputReaderIntegrationTest {
protected:
- static const int32_t FIRST_SLOT = 0;
- static const int32_t SECOND_SLOT = 1;
- static const int32_t FIRST_TRACKING_ID = 0;
- static const int32_t SECOND_TRACKING_ID = 1;
const std::string UNIQUE_ID = "local:0";
virtual void SetUp() override {
@@ -1884,7 +1961,7 @@
// At least add an internal display.
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
- ViewportType::VIEWPORT_INTERNAL);
+ ViewportType::INTERNAL);
mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -1947,9 +2024,9 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
// ACTION_POINTER_UP (Second slot)
- mDevice->sendUp();
+ mDevice->sendPointerUp();
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
args.action);
// ACTION_UP
@@ -1964,11 +2041,13 @@
const Point centerPoint = mDevice->getCenterPoint();
// ACTION_DOWN
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
mDevice->sendDown(centerPoint);
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- // ACTION_POINTER_DOWN (Second slot)
+ // ACTION_POINTER_DOWN (second slot)
const Point secondPoint = centerPoint + Point(100, 100);
mDevice->sendSlot(SECOND_SLOT);
mDevice->sendTrackingId(SECOND_TRACKING_ID);
@@ -1977,26 +2056,31 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
args.action);
- // ACTION_MOVE (Second slot)
+ // ACTION_MOVE (second slot)
mDevice->sendMove(secondPoint + Point(1, 1));
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- // Send MT_TOOL_PALM, which indicates that the touch IC has determined this to be a grip event.
- // Expect to receive ACTION_CANCEL, to abort the entire gesture.
+ // Send MT_TOOL_PALM (second slot), which indicates that the touch IC has determined this to be
+ // a palm event.
+ // Expect to receive the ACTION_POINTER_UP with cancel flag.
mDevice->sendToolType(MT_TOOL_PALM);
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ args.action);
+ ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, args.flags);
- // ACTION_POINTER_UP (Second slot)
- mDevice->sendUp();
+ // Send up to second slot, expect first slot send moving.
+ mDevice->sendPointerUp();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- // ACTION_UP
+ // Send ACTION_UP (first slot)
mDevice->sendSlot(FIRST_SLOT);
mDevice->sendUp();
- // Expect no event received after abort the entire gesture.
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
// --- InputDeviceTest ---
@@ -2007,7 +2091,7 @@
static const int32_t DEVICE_ID;
static const int32_t DEVICE_GENERATION;
static const int32_t DEVICE_CONTROLLER_NUMBER;
- static const uint32_t DEVICE_CLASSES;
+ static const Flags<InputDeviceClass> DEVICE_CLASSES;
static const int32_t EVENTHUB_ID;
std::shared_ptr<FakeEventHub> mFakeEventHub;
@@ -2023,7 +2107,7 @@
mFakeListener = new TestInputListener();
mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
- mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, 0);
+ mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, Flags<InputDeviceClass>(0));
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
identifier.location = DEVICE_LOCATION;
@@ -2044,14 +2128,14 @@
const int32_t InputDeviceTest::DEVICE_ID = END_RESERVED_ID + 1000;
const int32_t InputDeviceTest::DEVICE_GENERATION = 2;
const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0;
-const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD
- | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK;
+const Flags<InputDeviceClass> InputDeviceTest::DEVICE_CLASSES =
+ InputDeviceClass::KEYBOARD | InputDeviceClass::TOUCH | InputDeviceClass::JOYSTICK;
const int32_t InputDeviceTest::EVENTHUB_ID = 1;
TEST_F(InputDeviceTest, ImmutableProperties) {
ASSERT_EQ(DEVICE_ID, mDevice->getId());
ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str());
- ASSERT_EQ(0U, mDevice->getClasses());
+ ASSERT_EQ(Flags<InputDeviceClass>(0), mDevice->getClasses());
}
TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) {
@@ -2220,8 +2304,7 @@
// Prepare displays.
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi,
- ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi, ViewportType::INTERNAL);
mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
ASSERT_TRUE(mDevice->isEnabled());
@@ -2247,7 +2330,7 @@
static const int32_t DEVICE_ID;
static const int32_t DEVICE_GENERATION;
static const int32_t DEVICE_CONTROLLER_NUMBER;
- static const uint32_t DEVICE_CLASSES;
+ static const Flags<InputDeviceClass> DEVICE_CLASSES;
static const int32_t EVENTHUB_ID;
std::shared_ptr<FakeEventHub> mFakeEventHub;
@@ -2256,7 +2339,7 @@
FakeInputReaderContext* mFakeContext;
InputDevice* mDevice;
- virtual void SetUp(uint32_t classes) {
+ virtual void SetUp(Flags<InputDeviceClass> classes) {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -2294,6 +2377,7 @@
T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
configureDevice(0);
mDevice->reset(ARBITRARY_TIME);
+ mapper.reset(ARBITRARY_TIME);
return mapper;
}
@@ -2361,7 +2445,8 @@
const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000;
const int32_t InputMapperTest::DEVICE_GENERATION = 2;
const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
-const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests
+const Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
+ Flags<InputDeviceClass>(0); // not needed for current tests
const int32_t InputMapperTest::EVENTHUB_ID = 1;
// --- SwitchInputMapperTest ---
@@ -2421,8 +2506,8 @@
* orientation.
*/
void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
+ NO_PORT, ViewportType::INTERNAL);
}
void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
@@ -2727,7 +2812,7 @@
// ^--- already checked by the previous test
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2737,7 +2822,7 @@
constexpr int32_t newDisplayId = 2;
clearViewports();
setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2866,7 +2951,8 @@
std::unique_ptr<InputDevice> device2 =
std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
identifier);
- mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/);
+ mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME,
+ Flags<InputDeviceClass>(0) /*classes*/);
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);
@@ -2898,9 +2984,9 @@
// Prepare second display.
constexpr int32_t newDisplayId = 2;
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, hdmi1, ViewportType::VIEWPORT_INTERNAL);
+ UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- SECONDARY_UNIQUE_ID, hdmi2, ViewportType::VIEWPORT_EXTERNAL);
+ SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
// Default device will reconfigure above, need additional reconfiguration for another device.
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2934,7 +3020,7 @@
class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
protected:
virtual void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL);
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL);
}
};
@@ -3035,7 +3121,7 @@
void prepareDisplay(int32_t orientation) {
const std::string uniqueId = "local:0";
- const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+ const ViewportType viewportType = ViewportType::INTERNAL;
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
orientation, uniqueId, NO_PORT, viewportType);
}
@@ -3780,8 +3866,7 @@
constexpr int32_t SECOND_DISPLAY_ID = 1;
const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
- SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
- ViewportType::VIEWPORT_EXTERNAL);
+ SECOND_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::EXTERNAL);
mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -3908,8 +3993,8 @@
};
void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
- UNIQUE_ID, port, ViewportType::VIEWPORT_INTERNAL);
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
+ port, ViewportType::INTERNAL);
}
void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
@@ -3918,9 +4003,9 @@
}
void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
- setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
- VIRTUAL_DISPLAY_HEIGHT, orientation,
- VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+ setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT,
+ orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT,
+ ViewportType::VIRTUAL);
}
void TouchInputMapperTest::prepareVirtualKeys() {
@@ -6773,7 +6858,7 @@
const uint8_t hdmi1 = 0;
const uint8_t hdmi2 = 1;
const std::string secondaryUniqueId = "uniqueId2";
- constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ constexpr ViewportType type = ViewportType::EXTERNAL;
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareAxes(POSITION);
@@ -6814,7 +6899,7 @@
mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
- prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+ prepareSecondaryDisplay(ViewportType::EXTERNAL);
prepareDisplay(DISPLAY_ORIENTATION_0);
prepareAxes(POSITION);
@@ -6848,7 +6933,8 @@
std::unique_ptr<InputDevice> device2 =
std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
identifier);
- mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/);
+ mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME,
+ Flags<InputDeviceClass>(0) /*classes*/);
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,
@@ -6881,11 +6967,11 @@
// Create displays.
prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
- prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL, hdmi2);
+ prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
// Default device will reconfigure above, need additional reconfiguration for another device.
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
// Two fingers down at default display.
int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -6992,7 +7078,7 @@
TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) {
constexpr uint8_t hdmi2 = 1;
const std::string secondaryUniqueId = "uniqueId2";
- constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ constexpr ViewportType type = ViewportType::EXTERNAL;
mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2);
@@ -7059,10 +7145,10 @@
}
/**
- * Test touch should be canceled when received the MT_TOOL_PALM event, and the following MOVE and
- * UP events should be ignored.
+ * Test single touch should be canceled when received the MT_TOOL_PALM event, and the following
+ * MOVE and UP events should be ignored.
*/
-TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) {
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(DISPLAY_ORIENTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
@@ -7072,7 +7158,7 @@
// default tool type is finger
constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
- processId(mapper, 1);
+ processId(mapper, FIRST_TRACKING_ID);
processPosition(mapper, x1, y1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7086,19 +7172,19 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
// Ignore the following MOVE and UP events if had detect a palm event.
- processId(mapper, 1);
+ processId(mapper, FIRST_TRACKING_ID);
processPosition(mapper, x2, y2);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
// finger up.
- processId(mapper, -1);
+ processId(mapper, INVALID_TRACKING_ID);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
// new finger down
+ processId(mapper, FIRST_TRACKING_ID);
processToolType(mapper, MT_TOOL_FINGER);
- processId(mapper, 1);
processPosition(mapper, x3, y3);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7107,11 +7193,10 @@
}
/**
- * Test multi-touch should be canceled when received the MT_TOOL_PALM event from some finger,
- * and could be allowed again after all non-MT_TOOL_PALM is release and the new point is
- * MT_TOOL_FINGER.
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event from some finger,
+ * and the rest active fingers could still be allowed to receive the events
*/
-TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType2) {
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(DISPLAY_ORIENTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
@@ -7120,8 +7205,8 @@
NotifyMotionArgs motionArgs;
// default tool type is finger
- constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
- processId(mapper, 1);
+ constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220;
+ processId(mapper, FIRST_TRACKING_ID);
processPosition(mapper, x1, y1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7129,51 +7214,232 @@
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
// Second finger down.
- processSlot(mapper, 1);
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
processPosition(mapper, x2, y2);
- processId(mapper, 2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
+
+ // If the tool type of the first finger changes to MT_TOOL_PALM,
+ // we expect to receive ACTION_POINTER_UP with cancel flag.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, FIRST_TRACKING_ID);
+ processToolType(mapper, MT_TOOL_PALM);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+ // The following MOVE events of second finger should be processed.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2 + 1, y2 + 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // First finger up. It used to be in palm mode, and we already generated ACTION_POINTER_UP for
+ // it. Second finger receive move.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // Second finger keeps moving.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2 + 2, y2 + 2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // Second finger up.
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+}
+
+/**
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event, if only 1 finger
+ * is active, it should send CANCEL after receiving the MT_TOOL_PALM event.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelWhenAllTouchIsPalm) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
+ // First finger down.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // Second finger down.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- // If the tool type of the first pointer changes to MT_TOOL_PALM,
- // the entire gesture should be aborted, so we expect to receive ACTION_CANCEL.
- processSlot(mapper, 0);
- processId(mapper, 1);
+ // If the tool type of the first finger changes to MT_TOOL_PALM,
+ // we expect to receive ACTION_POINTER_UP with cancel flag.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, FIRST_TRACKING_ID);
+ processToolType(mapper, MT_TOOL_PALM);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+ // Second finger keeps moving.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2 + 1, y2 + 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+ // second finger becomes palm, receive cancel due to only 1 finger is active.
+ processId(mapper, SECOND_TRACKING_ID);
processToolType(mapper, MT_TOOL_PALM);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
- // Ignore the following MOVE and UP events if had detect a palm event.
- processSlot(mapper, 1);
- processId(mapper, 2);
+ // third finger down.
+ processSlot(mapper, THIRD_SLOT);
+ processId(mapper, THIRD_TRACKING_ID);
+ processToolType(mapper, MT_TOOL_FINGER);
processPosition(mapper, x3, y3);
processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
- // second finger up.
- processId(mapper, -1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
- // first finger move, but still in palm
- processSlot(mapper, 0);
- processId(mapper, 1);
- processPosition(mapper, x1 - 1, y1 - 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
- // second finger down, expect as new finger down.
- processSlot(mapper, 1);
- processId(mapper, 2);
- processPosition(mapper, x2, y2);
- processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // third finger move
+ processId(mapper, THIRD_TRACKING_ID);
+ processPosition(mapper, x3 + 1, y3 + 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+ // first finger up, third finger receive move.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // second finger up, third finger receive move.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // third finger up.
+ processSlot(mapper, THIRD_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+}
+
+/**
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event from some finger,
+ * and the active finger could still be allowed to receive the events
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPointer) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // default tool type is finger
+ constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220;
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // Second finger down.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // If the tool type of the second finger changes to MT_TOOL_PALM,
+ // we expect to receive ACTION_POINTER_UP with cancel flag.
+ processId(mapper, SECOND_TRACKING_ID);
+ processToolType(mapper, MT_TOOL_PALM);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+ // The following MOVE event should be processed.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1 + 1, y1 + 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // second finger up.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+ // first finger keep moving
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1 + 2, y1 + 2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+ // first finger up.
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
}
// --- MultiTouchInputMapperTest_ExternalDevice ---
@@ -7181,7 +7447,7 @@
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
protected:
virtual void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL);
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL);
}
};
@@ -7206,7 +7472,7 @@
ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
// Expect the event to be sent to the external viewport if it is present.
- prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+ prepareSecondaryDisplay(ViewportType::EXTERNAL);
processPosition(mapper, 100, 100);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7220,7 +7486,7 @@
protected:
void halfDisplayToCenterHorizontal(int32_t orientation) {
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
// Half display to (width/4, 0, width * 3/4, height) to make display has offset.
internalViewport->orientation = orientation;
@@ -7338,4 +7604,209 @@
constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
}
+
+TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) {
+ // we need a pointer controller for mouse mode of touchpad (start pointer at 0,0)
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ fakePointerController->setPosition(0, 0);
+ fakePointerController->setButtonState(0);
+
+ // prepare device and capture
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
+ mFakePolicy->setPointerCapture(true);
+ mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // captured touchpad should be a touchpad source
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+
+ // 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).
+ // FINGER 0 DOWN
+ processSlot(mapper, 0);
+ processId(mapper, 1);
+ processPosition(mapper, 100 + RAW_X_MIN, 100 + RAW_Y_MIN);
+ processKey(mapper, BTN_TOUCH, 1);
+ processSync(mapper);
+
+ // expect coord[0] to contain initial location of touch 0
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(1U, args.pointerCount);
+ ASSERT_EQ(0, args.pointerProperties[0].id);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, args.source);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // FINGER 1 DOWN
+ processSlot(mapper, 1);
+ processId(mapper, 2);
+ processPosition(mapper, 560 + RAW_X_MIN, 154 + RAW_Y_MIN);
+ processSync(mapper);
+
+ // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ args.action);
+ ASSERT_EQ(2U, args.pointerCount);
+ ASSERT_EQ(0, args.pointerProperties[0].id);
+ ASSERT_EQ(1, args.pointerProperties[1].id);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[1], 560, 154, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // FINGER 1 MOVE
+ processPosition(mapper, 540 + RAW_X_MIN, 690 + RAW_Y_MIN);
+ processSync(mapper);
+
+ // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
+ // from move
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[1], 540, 690, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // FINGER 0 MOVE
+ processSlot(mapper, 0);
+ processPosition(mapper, 50 + RAW_X_MIN, 800 + RAW_Y_MIN);
+ processSync(mapper);
+
+ // expect coord[0] to contain new touch 0 location, coord[1] to contain previous location
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 50, 800, 1, 0, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[1], 540, 690, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // BUTTON DOWN
+ processKey(mapper, BTN_LEFT, 1);
+ processSync(mapper);
+
+ // touchinputmapper design sends a move before button press
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+
+ // BUTTON UP
+ processKey(mapper, BTN_LEFT, 0);
+ processSync(mapper);
+
+ // touchinputmapper design sends a move after button release
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+ // FINGER 0 UP
+ processId(mapper, -1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | 0x0000, args.action);
+
+ // FINGER 1 MOVE
+ processSlot(mapper, 1);
+ processPosition(mapper, 320 + RAW_X_MIN, 900 + RAW_Y_MIN);
+ processSync(mapper);
+
+ // expect coord[0] to contain new location of touch 1, and properties[0].id to contain 1
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_EQ(1U, args.pointerCount);
+ ASSERT_EQ(1, args.pointerProperties[0].id);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 320, 900, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // FINGER 1 UP
+ processId(mapper, -1);
+ processKey(mapper, BTN_TOUCH, 0);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+
+ // non captured touchpad should be a mouse source
+ mFakePolicy->setPointerCapture(false);
+ configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) {
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ fakePointerController->setPosition(0, 0);
+ fakePointerController->setButtonState(0);
+
+ // prepare device and capture
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
+ mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+ // run uncaptured pointer tests - pushes out generic events
+ // FINGER 0 DOWN
+ processId(mapper, 3);
+ processPosition(mapper, 100, 100);
+ processKey(mapper, BTN_TOUCH, 1);
+ processSync(mapper);
+
+ // start at (100,100), cursor should be at (0,0) * scale
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+
+ // FINGER 0 MOVE
+ processPosition(mapper, 200, 200);
+ processSync(mapper);
+
+ // compute scaling to help with touch position checking
+ float rawDiagonal = hypotf(RAW_X_MAX - RAW_X_MIN, RAW_Y_MAX - RAW_Y_MIN);
+ float displayDiagonal = hypotf(DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ float scale =
+ mFakePolicy->getPointerGestureMovementSpeedRatio() * displayDiagonal / rawDiagonal;
+
+ // translate from (100,100) -> (200,200), cursor should have changed to (100,100) * scale)
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 100 * scale, 100 * scale, 0,
+ 0, 0, 0, 0, 0, 0, 0));
+}
+
+TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) {
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
+ mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ mFakePolicy->setPointerCapture(false);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // uncaptured touchpad should be a pointer device
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
+
+ // captured touchpad should be a touchpad device
+ mFakePolicy->setPointerCapture(true);
+ configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 0659511..7fec2c8 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -179,6 +179,11 @@
injectEvent(EV_SYN, SYN_REPORT, 0);
}
+void UinputTouchScreen::sendPointerUp() {
+ sendTrackingId(0xffffffff);
+ injectEvent(EV_SYN, SYN_REPORT, 0);
+}
+
void UinputTouchScreen::sendUp() {
sendTrackingId(0xffffffff);
injectEvent(EV_KEY, BTN_TOUCH, 0);
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index 22d1f63..01a557c 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -139,6 +139,7 @@
void sendTrackingId(int32_t trackingId);
void sendDown(const Point& point);
void sendMove(const Point& point);
+ void sendPointerUp();
void sendUp();
void sendToolType(int32_t toolType);
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index b0d3e3b..ec3dfc8 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -2,9 +2,14 @@
name: "libpowermanager",
srcs: [
- "IPowerManager.cpp",
- "Temperature.cpp",
+ "BatterySaverPolicyConfig.cpp",
"CoolingDevice.cpp",
+ "PowerHalController.cpp",
+ "PowerHalLoader.cpp",
+ "PowerHalWrapper.cpp",
+ "PowerSaveState.cpp",
+ "Temperature.cpp",
+ "WorkSource.cpp",
":libpowermanager_aidl",
],
@@ -17,9 +22,13 @@
},
shared_libs: [
- "libutils",
"libbinder",
- "liblog"
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-cpp",
],
cflags: [
@@ -34,22 +43,3 @@
"include",
],
}
-
-cc_test {
- name: "thermalmanager-test",
- srcs: ["IThermalManagerTest.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
- shared_libs: [
- "libbase",
- "libhidlbase",
- "liblog",
- "libpowermanager",
- "libbinder",
- "libutils",
- ],
-}
diff --git a/services/powermanager/BatterySaverPolicyConfig.cpp b/services/powermanager/BatterySaverPolicyConfig.cpp
new file mode 100644
index 0000000..ee55b6b
--- /dev/null
+++ b/services/powermanager/BatterySaverPolicyConfig.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 "BatterySaverPolicyConfig"
+
+#include <android/BatterySaverPolicyConfig.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t BatterySaverPolicyConfig::readDeviceSpecificSettings(const android::Parcel *parcel) {
+ int32_t num = 0;
+ status_t ret = parcel->readInt32(&num);
+ if (ret != OK) {
+ return ret;
+ }
+ for (int i = 0; i < num; i++) {
+ String16 key, val;
+ ret = parcel->readString16(&key) ?:
+ parcel->readString16(&val);
+ if (ret != OK) {
+ return ret;
+ }
+ mDeviceSpecificSettings.emplace_back(key, val);
+ }
+ return ret;
+}
+
+status_t BatterySaverPolicyConfig::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->readFloat(&mAdjustBrightnessFactor)
+ ?: parcel->readBool(&mAdvertiseIsEnabled)
+ ?: parcel->readBool(&mDeferFullBackup)
+ ?: parcel->readBool(&mDeferKeyValueBackup)
+ ?: readDeviceSpecificSettings(parcel)
+ ?: parcel->readBool(&mDisableAnimation)
+ ?: parcel->readBool(&mDisableAod)
+ ?: parcel->readBool(&mDisableLaunchBoost)
+ ?: parcel->readBool(&mDisableOptionalSensors)
+ ?: parcel->readBool(&mDisableSoundTrigger)
+ ?: parcel->readBool(&mDisableVibration)
+ ?: parcel->readBool(&mEnableAdjustBrightness)
+ ?: parcel->readBool(&mEnableDataSaver)
+ ?: parcel->readBool(&mEnableFirewall)
+ ?: parcel->readBool(&mEnableNightMode)
+ ?: parcel->readBool(&mEnableQuickDoze)
+ ?: parcel->readBool(&mForceAllAppsStandby)
+ ?: parcel->readBool(&mForceBackgroundCheck)
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode));
+}
+
+status_t BatterySaverPolicyConfig::writeDeviceSpecificSettings(android::Parcel *parcel) const {
+ status_t ret = parcel->writeInt32(mDeviceSpecificSettings.size());
+ if (ret != OK) {
+ return ret;
+ }
+ for (auto& settings : mDeviceSpecificSettings) {
+ ret = parcel->writeString16(settings.first) ?:
+ parcel->writeString16(settings.second);
+ if (ret != OK) {
+ return ret;
+ }
+ }
+ return ret;
+}
+
+status_t BatterySaverPolicyConfig::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeFloat(mAdjustBrightnessFactor)
+ ?: parcel->writeBool(mAdvertiseIsEnabled)
+ ?: parcel->writeBool(mDeferFullBackup)
+ ?: parcel->writeBool(mDeferKeyValueBackup)
+ ?: writeDeviceSpecificSettings(parcel)
+ ?: parcel->writeBool(mDisableAnimation)
+ ?: parcel->writeBool(mDisableAod)
+ ?: parcel->writeBool(mDisableLaunchBoost)
+ ?: parcel->writeBool(mDisableOptionalSensors)
+ ?: parcel->writeBool(mDisableSoundTrigger)
+ ?: parcel->writeBool(mDisableVibration)
+ ?: parcel->writeBool(mEnableAdjustBrightness)
+ ?: parcel->writeBool(mEnableDataSaver)
+ ?: parcel->writeBool(mEnableFirewall)
+ ?: parcel->writeBool(mEnableNightMode)
+ ?: parcel->writeBool(mEnableQuickDoze)
+ ?: parcel->writeBool(mForceAllAppsStandby)
+ ?: parcel->writeBool(mForceBackgroundCheck)
+ ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode));
+}
+
+} // namespace android::os
diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp
deleted file mode 100644
index ea3a831..0000000
--- a/services/powermanager/IPowerManager.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IPowerManager"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <powermanager/IPowerManager.h>
-
-namespace android {
-
-class BpPowerManager : public BpInterface<IPowerManager>
-{
-public:
- explicit BpPowerManager(const sp<IBinder>& impl)
- : BpInterface<IPowerManager>(impl)
- {
- }
-
- virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- data.writeString16(tag);
- data.writeString16(packageName);
- data.writeInt32(0); // no WorkSource
- data.writeString16(NULL, 0); // no history tag
- return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, int uid, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- data.writeString16(tag);
- data.writeString16(packageName);
- data.writeInt32(uid); // uid to blame for the work
- return remote()->transact(ACQUIRE_WAKE_LOCK_UID, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- return remote()->transact(RELEASE_WAKE_LOCK, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids,
- bool isOneWay) {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeStrongBinder(lock);
- data.writeInt32Array(len, uids);
- return remote()->transact(UPDATE_WAKE_LOCK_UIDS, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t powerHint(int hintId, int param)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(hintId);
- data.writeInt32(param);
- // This FLAG_ONEWAY is in the .aidl, so there is no way to disable it
- return remote()->transact(POWER_HINT, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt64(event_time_ms);
- data.writeInt32(reason);
- data.writeInt32(flags);
- return remote()->transact(GO_TO_SLEEP, data, &reply, 0);
- }
-
- virtual status_t reboot(bool confirm, const String16& reason, bool wait)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(confirm);
- data.writeString16(reason);
- data.writeInt32(wait);
- return remote()->transact(REBOOT, data, &reply, 0);
- }
-
- virtual status_t shutdown(bool confirm, const String16& reason, bool wait)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(confirm);
- data.writeString16(reason);
- data.writeInt32(wait);
- return remote()->transact(SHUTDOWN, data, &reply, 0);
- }
-
- virtual status_t crash(const String16& message)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeString16(message);
- return remote()->transact(CRASH, data, &reply, 0);
- }
-};
-
-IMPLEMENT_META_INTERFACE(PowerManager, "android.os.IPowerManager");
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
new file mode 100644
index 0000000..178f545
--- /dev/null
+++ b/services/powermanager/PowerHalController.cpp
@@ -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.
+ */
+
+#define LOG_TAG "PowerHalController"
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalController.h>
+#include <powermanager/PowerHalLoader.h>
+#include <utils/Log.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+std::unique_ptr<HalWrapper> HalConnector::connect() {
+ sp<IPower> halAidl = PowerHalLoader::loadAidl();
+ if (halAidl) {
+ return std::make_unique<AidlHalWrapper>(halAidl);
+ }
+ sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0();
+ sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1();
+ if (halHidlV1_1) {
+ return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1);
+ }
+ if (halHidlV1_0) {
+ return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0);
+ }
+ return nullptr;
+}
+
+void HalConnector::reset() {
+ PowerHalLoader::unloadAll();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalController::init() {
+ initHal();
+}
+
+// Check validity of current handle to the power HAL service, and create a new
+// one if necessary.
+std::shared_ptr<HalWrapper> PowerHalController::initHal() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect();
+ if (mConnectedHal == nullptr) {
+ // Unable to connect to Power HAL service. Fallback to default.
+ return mDefaultHal;
+ }
+ }
+ return mConnectedHal;
+}
+
+// Check if a call to Power HAL function failed; if so, log the failure and
+// invalidate the current Power HAL handle.
+HalResult PowerHalController::processHalResult(HalResult result, const char* fnName) {
+ if (result == HalResult::FAILED) {
+ ALOGE("%s() failed: power HAL service not available.", fnName);
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ // Drop Power HAL handle. This will force future api calls to reconnect.
+ mConnectedHal = nullptr;
+ mHalConnector->reset();
+ }
+ return result;
+}
+
+HalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->setBoost(boost, durationMs);
+ return processHalResult(result, "setBoost");
+}
+
+HalResult PowerHalController::setMode(Mode mode, bool enabled) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->setMode(mode, enabled);
+ return processHalResult(result, "setMode");
+}
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
new file mode 100644
index 0000000..1f1b43a
--- /dev/null
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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 "PowerHalLoader"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <binder/IServiceManager.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <powermanager/PowerHalLoader.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T, typename F>
+sp<T> loadHal(bool& exists, sp<T>& hal, F& loadFn, const char* halName) {
+ if (!exists) {
+ return nullptr;
+ }
+ if (hal) {
+ return hal;
+ }
+ hal = loadFn();
+ if (hal) {
+ ALOGV("Successfully connected to Power HAL %s service.", halName);
+ } else {
+ ALOGV("Power HAL %s service not available.", halName);
+ exists = false;
+ }
+ return hal;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+std::mutex PowerHalLoader::gHalMutex;
+sp<IPower> PowerHalLoader::gHalAidl = nullptr;
+sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr;
+sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
+
+void PowerHalLoader::unloadAll() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ gHalAidl = nullptr;
+ gHalHidlV1_0 = nullptr;
+ gHalHidlV1_1 = nullptr;
+}
+
+sp<IPower> PowerHalLoader::loadAidl() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return waitForVintfService<IPower>(); };
+ return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL");
+}
+
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ return loadHidlV1_0Locked();
+}
+
+sp<V1_1::IPower> PowerHalLoader::loadHidlV1_1() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_1::IPower::castFrom(loadHidlV1_0Locked()); };
+ return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1");
+}
+
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() {
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_0::IPower::getService(); };
+ return loadHal<V1_0::IPower>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0");
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
new file mode 100644
index 0000000..4a711ca
--- /dev/null
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 "HalWrapper"
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+inline HalResult toHalResult(const binder::Status& result) {
+ if (result.isOk()) {
+ return HalResult::SUCCESSFUL;
+ }
+ ALOGE("Power HAL request failed: %s", result.toString8().c_str());
+ return HalResult::FAILED;
+}
+
+template <typename T>
+inline HalResult toHalResult(const hardware::Return<T>& result) {
+ if (result.isOk()) {
+ return HalResult::SUCCESSFUL;
+ }
+ ALOGE("Power HAL request failed: %s", result.description().c_str());
+ return HalResult::FAILED;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+ ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
+ toString(boost).c_str(), durationMs);
+ return HalResult::UNSUPPORTED;
+}
+
+HalResult EmptyHalWrapper::setMode(Mode mode, bool enabled) {
+ ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
+ enabled ? "true" : "false");
+ return HalResult::UNSUPPORTED;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
+ if (boost == Boost::INTERACTION) {
+ return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs);
+ } else {
+ ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+}
+
+HalResult HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+ uint32_t data = enabled ? 1 : 0;
+ switch (mode) {
+ case Mode::LAUNCH:
+ return sendPowerHint(V1_0::PowerHint::LAUNCH, data);
+ case Mode::LOW_POWER:
+ return sendPowerHint(V1_0::PowerHint::LOW_POWER, data);
+ case Mode::SUSTAINED_PERFORMANCE:
+ return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data);
+ case Mode::VR:
+ return sendPowerHint(V1_0::PowerHint::VR_MODE, data);
+ case Mode::INTERACTIVE:
+ return setInteractive(enabled);
+ case Mode::DOUBLE_TAP_TO_WAKE:
+ return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled);
+ default:
+ ALOGV("Skipped setMode %s because Power HAL AIDL not available",
+ toString(mode).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+}
+
+HalResult HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+ return toHalResult(mHandleV1_0->powerHint(hintId, data));
+}
+
+HalResult HidlHalWrapperV1_0::setInteractive(bool enabled) {
+ return toHalResult(mHandleV1_0->setInteractive(enabled));
+}
+
+HalResult HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) {
+ return toHalResult(mHandleV1_0->setFeature(feature, enabled));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+ return toHalResult(mHandleV1_1->powerHintAsync(hintId, data));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+ std::unique_lock<std::mutex> lock(mBoostMutex);
+ // Quick return if boost is not supported by HAL
+ if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
+ mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
+ ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+
+ if (mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
+ bool isSupported = false;
+ auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
+ if (!isSupportedRet.isOk()) {
+ ALOGE("Skipped setBoost %s because check support failed with: %s",
+ toString(boost).c_str(), isSupportedRet.toString8().c_str());
+ return HalResult::FAILED;
+ }
+
+ mBoostSupportedArray[static_cast<int32_t>(boost)] =
+ isSupported ? HalSupport::ON : HalSupport::OFF;
+ if (!isSupported) {
+ ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
+ toString(boost).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+ }
+ lock.unlock();
+
+ return toHalResult(mHandle->setBoost(boost, durationMs));
+}
+
+HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) {
+ std::unique_lock<std::mutex> lock(mModeMutex);
+ // Quick return if mode is not supported by HAL
+ if (mode > Mode::DISPLAY_INACTIVE ||
+ mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
+ ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+
+ if (mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
+ bool isSupported = false;
+ auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
+ if (!isSupportedRet.isOk()) {
+ ALOGE("Skipped setMode %s because check support failed with: %s",
+ toString(mode).c_str(), isSupportedRet.toString8().c_str());
+ return HalResult::FAILED;
+ }
+
+ mModeSupportedArray[static_cast<int32_t>(mode)] =
+ isSupported ? HalSupport::ON : HalSupport::OFF;
+ if (!isSupported) {
+ ALOGV("Skipped setMode %s because Power HAL doesn't support it",
+ toString(mode).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+ }
+ lock.unlock();
+
+ return toHalResult(mHandle->setMode(mode, enabled));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerSaveState.cpp b/services/powermanager/PowerSaveState.cpp
new file mode 100644
index 0000000..6d1830a
--- /dev/null
+++ b/services/powermanager/PowerSaveState.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "PowerSaveState"
+
+#include <android/PowerSaveState.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t PowerSaveState::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->readBool(&mBatterySaverEnabled)
+ ?: parcel->readBool(&mGlobalBatterySaverEnabled)
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode))
+ ?: parcel->readFloat(&mBrightnessFactor);
+}
+
+status_t PowerSaveState::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeBool(mBatterySaverEnabled)
+ ?: parcel->writeBool(mGlobalBatterySaverEnabled)
+ ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode))
+ ?: parcel->writeFloat(mBrightnessFactor);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/TEST_MAPPING b/services/powermanager/TEST_MAPPING
new file mode 100644
index 0000000..caaec55
--- /dev/null
+++ b/services/powermanager/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libpowermanager_test"
+ }
+ ]
+}
diff --git a/services/powermanager/WorkSource.cpp b/services/powermanager/WorkSource.cpp
new file mode 100644
index 0000000..1006a06
--- /dev/null
+++ b/services/powermanager/WorkSource.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "WorkSource"
+
+#include <android/WorkSource.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t WorkSource::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ int32_t num;
+ status_t ret = parcel->readInt32(&num)
+ ?: parcel->readInt32Vector(&mUids)
+ ?: parcel->readString16Vector(&mNames);
+
+ return ret;
+}
+
+status_t WorkSource::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeInt32(mUids.size())
+ ?: parcel->writeInt32Vector(mUids)
+ ?: parcel->writeString16Vector(mNames);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
new file mode 100644
index 0000000..4c5d508
--- /dev/null
+++ b/services/powermanager/benchmarks/Android.bp
@@ -0,0 +1,42 @@
+// 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: "libpowermanager_benchmarks",
+ srcs: [
+ "main.cpp",
+ "PowerHalAidlBenchmarks.cpp",
+ "PowerHalControllerBenchmarks.cpp",
+ "PowerHalHidlBenchmarks.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libpowermanager",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-cpp",
+ ],
+ static_libs: [
+ "libtestUtil",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
diff --git a/services/powermanager/benchmarks/AndroidTest.xml b/services/powermanager/benchmarks/AndroidTest.xml
new file mode 100644
index 0000000..40f4872
--- /dev/null
+++ b/services/powermanager/benchmarks/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?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/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
new file mode 100644
index 0000000..1004828
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "PowerHalAidlBenchmarks"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <benchmark/benchmark.h>
+#include <binder/IServiceManager.h>
+#include <testUtil.h>
+#include <chrono>
+
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::Mode;
+using std::chrono::microseconds;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+// Values from Boost.aidl and Mode.aidl.
+static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION);
+static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT);
+static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
+static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+
+// Delay between oneway method calls to avoid overflowing the binder buffers.
+static constexpr microseconds ONEWAY_API_DELAY = 100us;
+
+template <class R, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower::*fn)(Args0...),
+ Args1&&... args1) {
+ sp<IPower> hal = waitForVintfService<IPower>();
+
+ if (hal == nullptr) {
+ ALOGI("Power HAL not available, skipping test...");
+ return;
+ }
+
+ binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+ ALOGI("Power HAL does not support this operation, skipping test...");
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ state.PauseTiming();
+ if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+ if (delay > 0us) {
+ testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+ }
+ state.ResumeTiming();
+ }
+}
+
+static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) {
+ bool isSupported;
+ Boost boost = static_cast<Boost>(state.range(0));
+ runBenchmark(state, 0us, &IPower::isBoostSupported, boost, &isSupported);
+}
+
+static void BM_PowerHalAidlBenchmarks_isModeSupported(benchmark::State& state) {
+ bool isSupported;
+ Mode mode = static_cast<Mode>(state.range(0));
+ runBenchmark(state, 0us, &IPower::isModeSupported, mode, &isSupported);
+}
+
+static void BM_PowerHalAidlBenchmarks_setBoost(benchmark::State& state) {
+ Boost boost = static_cast<Boost>(state.range(0));
+ runBenchmark(state, ONEWAY_API_DELAY, &IPower::setBoost, boost, 1);
+}
+
+static void BM_PowerHalAidlBenchmarks_setMode(benchmark::State& state) {
+ Mode mode = static_cast<Mode>(state.range(0));
+ runBenchmark(state, ONEWAY_API_DELAY, &IPower::setMode, mode, false);
+}
+
+BENCHMARK(BM_PowerHalAidlBenchmarks_isBoostSupported)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_isModeSupported)->DenseRange(FIRST_MODE, LAST_MODE, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_setBoost)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_setMode)->DenseRange(FIRST_MODE, LAST_MODE, 1);
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
new file mode 100644
index 0000000..598080b
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+#include <benchmark/benchmark.h>
+#include <powermanager/PowerHalController.h>
+#include <testUtil.h>
+#include <chrono>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::power::HalResult;
+using android::power::PowerHalController;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+// Values from Boost.aidl and Mode.aidl.
+static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION);
+static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT);
+static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
+static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+
+// Delay between oneway method calls to avoid overflowing the binder buffers.
+static constexpr std::chrono::microseconds ONEWAY_API_DELAY = 100us;
+
+template <class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, HalResult (PowerHalController::*fn)(Args0...),
+ Args1&&... args1) {
+ while (state.KeepRunning()) {
+ PowerHalController controller;
+ HalResult ret = (controller.*fn)(std::forward<Args1>(args1)...);
+ state.PauseTiming();
+ if (ret == HalResult::FAILED) state.SkipWithError("Power HAL request failed");
+ state.ResumeTiming();
+ }
+}
+
+template <class... Args0, class... Args1>
+static void runCachedBenchmark(benchmark::State& state,
+ HalResult (PowerHalController::*fn)(Args0...), Args1&&... args1) {
+ PowerHalController controller;
+ // First call out of test, to cache HAL service and isSupported result.
+ (controller.*fn)(std::forward<Args1>(args1)...);
+
+ while (state.KeepRunning()) {
+ HalResult ret = (controller.*fn)(std::forward<Args1>(args1)...);
+ state.PauseTiming();
+ if (ret == HalResult::FAILED) {
+ state.SkipWithError("Power HAL request failed");
+ }
+ testDelaySpin(
+ std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY).count());
+ state.ResumeTiming();
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_init(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ PowerHalController controller;
+ controller.init();
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_initCached(benchmark::State& state) {
+ PowerHalController controller;
+ // First connection out of test.
+ controller.init();
+
+ while (state.KeepRunning()) {
+ controller.init();
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_setBoost(benchmark::State& state) {
+ Boost boost = static_cast<Boost>(state.range(0));
+ runBenchmark(state, &PowerHalController::setBoost, boost, 0);
+}
+
+static void BM_PowerHalControllerBenchmarks_setBoostCached(benchmark::State& state) {
+ Boost boost = static_cast<Boost>(state.range(0));
+ runCachedBenchmark(state, &PowerHalController::setBoost, boost, 0);
+}
+
+static void BM_PowerHalControllerBenchmarks_setMode(benchmark::State& state) {
+ Mode mode = static_cast<Mode>(state.range(0));
+ runBenchmark(state, &PowerHalController::setMode, mode, false);
+}
+
+static void BM_PowerHalControllerBenchmarks_setModeCached(benchmark::State& state) {
+ Mode mode = static_cast<Mode>(state.range(0));
+ runCachedBenchmark(state, &PowerHalController::setMode, mode, false);
+}
+
+BENCHMARK(BM_PowerHalControllerBenchmarks_init);
+BENCHMARK(BM_PowerHalControllerBenchmarks_initCached);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setBoost)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setBoostCached)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setMode)->DenseRange(FIRST_MODE, LAST_MODE, 1);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setModeCached)->DenseRange(FIRST_MODE, LAST_MODE, 1);
diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
new file mode 100644
index 0000000..97e026b
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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 "PowerHalHidlBenchmarks"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <benchmark/benchmark.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <testUtil.h>
+#include <chrono>
+
+using android::hardware::Return;
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::PowerHint;
+using std::chrono::microseconds;
+using IPower1_0 = android::hardware::power::V1_0::IPower;
+using IPower1_1 = android::hardware::power::V1_1::IPower;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+// Values from types.hal from versions 1.0 to 1.3.
+static constexpr int64_t FIRST_POWER_HINT = static_cast<int64_t>(PowerHint::VSYNC);
+static constexpr int64_t LAST_POWER_HINT = static_cast<int64_t>(PowerHint::LAUNCH);
+
+// Delay between oneway method calls to avoid overflowing the binder buffers.
+static constexpr microseconds ONEWAY_API_DELAY = 100us;
+
+template <class R, class I, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, microseconds delay, Return<R> (I::*fn)(Args0...),
+ Args1&&... args1) {
+ sp<I> hal = I::getService();
+
+ if (hal == nullptr) {
+ ALOGI("Power HAL HIDL not available, skipping test...");
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ Return<R> ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+ state.PauseTiming();
+ if (!ret.isOk()) state.SkipWithError(ret.description().c_str());
+ if (delay > 0us) {
+ testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+ }
+ state.ResumeTiming();
+ }
+}
+
+static void BM_PowerHalHidlBenchmarks_setFeature(benchmark::State& state) {
+ runBenchmark(state, 0us, &IPower1_0::setFeature, Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE,
+ false);
+}
+
+static void BM_PowerHalHidlBenchmarks_setInteractive(benchmark::State& state) {
+ runBenchmark(state, 0us, &IPower1_0::setInteractive, false);
+}
+
+static void BM_PowerHalHidlBenchmarks_powerHint(benchmark::State& state) {
+ PowerHint powerHint = static_cast<PowerHint>(state.range(0));
+ runBenchmark(state, 0us, &IPower1_0::powerHint, powerHint, 0);
+}
+
+static void BM_PowerHalHidlBenchmarks_powerHintAsync(benchmark::State& state) {
+ PowerHint powerHint = static_cast<PowerHint>(state.range(0));
+ runBenchmark(state, ONEWAY_API_DELAY, &IPower1_1::powerHintAsync, powerHint, 0);
+}
+
+BENCHMARK(BM_PowerHalHidlBenchmarks_setFeature);
+BENCHMARK(BM_PowerHalHidlBenchmarks_setInteractive);
+BENCHMARK(BM_PowerHalHidlBenchmarks_powerHint)->DenseRange(FIRST_POWER_HINT, LAST_POWER_HINT, 1);
+BENCHMARK(BM_PowerHalHidlBenchmarks_powerHintAsync)
+ ->DenseRange(FIRST_POWER_HINT, LAST_POWER_HINT, 1);
diff --git a/services/powermanager/benchmarks/main.cpp b/services/powermanager/benchmarks/main.cpp
new file mode 100644
index 0000000..15c57bf
--- /dev/null
+++ b/services/powermanager/benchmarks/main.cpp
@@ -0,0 +1,19 @@
+/*
+ * 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 <benchmark/benchmark.h>
+
+BENCHMARK_MAIN();
diff --git a/services/powermanager/include/android/BatterySaverPolicyConfig.h b/services/powermanager/include/android/BatterySaverPolicyConfig.h
new file mode 100644
index 0000000..728c8a0
--- /dev/null
+++ b/services/powermanager/include/android/BatterySaverPolicyConfig.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H
+#define ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H
+
+#include <math.h>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+enum class LocationMode : int32_t;
+/**
+ * BatterySaverPolicyConfig is a structure of configs to set Battery Saver policy flags.
+ * This file needs to be kept in sync with
+ * frameworks/base/core/java/android/os/BatterySaverPolicyConfig.java
+ */
+struct BatterySaverPolicyConfig : public android::Parcelable {
+
+ BatterySaverPolicyConfig(float adjustBrightnessFactor = 1.0f,
+ bool advertiseIsEnabled = false,
+ bool deferFullBackup = false,
+ bool deferKeyValueBackup = false,
+ std::vector<std::pair<String16, String16>> deviceSpecificSettings = {},
+ bool disableAnimation = false,
+ bool disableAod = false,
+ bool disableLaunchBoost = false,
+ bool disableOptionalSensors = false,
+ bool disableSoundTrigger = false,
+ bool disableVibration = false,
+ bool enableAdjustBrightness = false,
+ bool enableDataSaver = false,
+ bool enableFirewall = false,
+ bool enableNightMode = false,
+ bool enableQuickDoze = false,
+ bool forceAllAppsStandby = false,
+ bool forceBackgroundCheck = false,
+ LocationMode locationMode = static_cast<LocationMode>(0))
+ : mAdjustBrightnessFactor(adjustBrightnessFactor),
+ mAdvertiseIsEnabled(advertiseIsEnabled),
+ mDeferFullBackup(deferFullBackup),
+ mDeferKeyValueBackup(deferKeyValueBackup),
+ mDeviceSpecificSettings(deviceSpecificSettings),
+ mDisableAnimation(disableAnimation),
+ mDisableAod(disableAod),
+ mDisableLaunchBoost(disableLaunchBoost),
+ mDisableOptionalSensors(disableOptionalSensors),
+ mDisableSoundTrigger(disableSoundTrigger),
+ mDisableVibration(disableVibration),
+ mEnableAdjustBrightness(enableAdjustBrightness),
+ mEnableDataSaver(enableDataSaver),
+ mEnableFirewall(enableFirewall),
+ mEnableNightMode(enableNightMode),
+ mEnableQuickDoze(enableQuickDoze),
+ mForceAllAppsStandby(forceAllAppsStandby),
+ mForceBackgroundCheck(forceBackgroundCheck),
+ mLocationMode(locationMode) {
+ }
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ bool operator == (const BatterySaverPolicyConfig &bsp) const {
+ return fabs(mAdjustBrightnessFactor - bsp.mAdjustBrightnessFactor) == 0.0f &&
+ mAdvertiseIsEnabled == bsp.mAdvertiseIsEnabled &&
+ mDeferFullBackup == bsp.mDeferFullBackup &&
+ mDeferKeyValueBackup == bsp.mDeferKeyValueBackup &&
+ mDeviceSpecificSettings == bsp.mDeviceSpecificSettings &&
+ mDisableAnimation == bsp.mDisableAnimation &&
+ mDisableAod == bsp.mDisableAod &&
+ mDisableLaunchBoost == bsp.mDisableLaunchBoost &&
+ mDisableOptionalSensors == bsp.mDisableOptionalSensors &&
+ mDisableSoundTrigger == bsp.mDisableSoundTrigger &&
+ mDisableVibration == bsp.mDisableVibration &&
+ mEnableAdjustBrightness == bsp.mEnableAdjustBrightness &&
+ mEnableDataSaver == bsp.mEnableDataSaver &&
+ mEnableFirewall == bsp.mEnableFirewall &&
+ mEnableNightMode == bsp.mEnableNightMode &&
+ mEnableQuickDoze == bsp.mEnableQuickDoze &&
+ mForceAllAppsStandby == bsp.mForceAllAppsStandby &&
+ mForceBackgroundCheck == bsp.mForceBackgroundCheck &&
+ mLocationMode == bsp.mLocationMode;
+ }
+
+private:
+ status_t readDeviceSpecificSettings(const android::Parcel *parcel);
+ status_t writeDeviceSpecificSettings(android::Parcel *parcel) const;
+ /** Adjust screen brightness factor */
+ float mAdjustBrightnessFactor;
+ /** Is advertise enabled */
+ bool mAdvertiseIsEnabled;
+ /** Defer full backup */
+ bool mDeferFullBackup;
+ /** Defer key value backup */
+ bool mDeferKeyValueBackup;
+ /** Device specific settings */
+ std::vector<std::pair<String16, String16>> mDeviceSpecificSettings;
+ /** Disable animation */
+ bool mDisableAnimation;
+ /** Disable Aod */
+ bool mDisableAod;
+ /** Disable launch boost */
+ bool mDisableLaunchBoost;
+ /** Disable optional sensors */
+ bool mDisableOptionalSensors;
+ /** Disable sound trigger */
+ bool mDisableSoundTrigger;
+ /** Disable vibration */
+ bool mDisableVibration;
+ /** Enable adjust brightness */
+ bool mEnableAdjustBrightness;
+ /** Enable data saver */
+ bool mEnableDataSaver;
+ /** Enable firewall */
+ bool mEnableFirewall;
+ /** Enable night mode */
+ bool mEnableNightMode;
+ /** Enable quick doze */
+ bool mEnableQuickDoze;
+ /** Force all Apps standby */
+ bool mForceAllAppsStandby;
+ /** Force Background check */
+ bool mForceBackgroundCheck;
+ /** Location mode */
+ LocationMode mLocationMode;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H */
diff --git a/services/powermanager/include/android/LocationMode.h b/services/powermanager/include/android/LocationMode.h
new file mode 100644
index 0000000..42933d4
--- /dev/null
+++ b/services/powermanager/include/android/LocationMode.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_LOCATION_MODE_H
+#define ANDROID_OS_LOCATION_MODE_H
+
+namespace android::os {
+
+enum class LocationMode : int32_t {
+ NO_CHANGE = IPowerManager::LOCATION_MODE_NO_CHANGE,
+ GPS_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF,
+ ALL_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF,
+ FOREGROUND_ONLY = IPowerManager::LOCATION_MODE_FOREGROUND_ONLY,
+ THROTTLE_REQUESTS_WHEN_SCREEN_OFF =
+ IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+ MIN = IPowerManager::LOCATION_MODE_NO_CHANGE,
+ MAX = IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_LOCATION_MODE_H */
diff --git a/services/powermanager/include/android/PowerSaveState.h b/services/powermanager/include/android/PowerSaveState.h
new file mode 100644
index 0000000..b421f6a
--- /dev/null
+++ b/services/powermanager/include/android/PowerSaveState.h
@@ -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.
+ */
+
+#ifndef ANDROID_OS_POWER_SAVE_STATE_H
+#define ANDROID_OS_POWER_SAVE_STATE_H
+
+#include <math.h>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+enum class LocationMode : int32_t;
+/**
+ * PowerSaveState is a structure to encapsulate PowerSaveState status.
+ * This file needs to be kept in sync with frameworks/base/core/java/android/os/PowerSaveState.java
+ */
+struct PowerSaveState : public android::Parcelable {
+
+ PowerSaveState(bool batterySaverEnabled = false,
+ bool globalBatterySaverEnabled = false,
+ LocationMode locationMode = static_cast<LocationMode>(0),
+ float brightnessFactor = 0.5f)
+ : mBatterySaverEnabled(batterySaverEnabled),
+ mGlobalBatterySaverEnabled(globalBatterySaverEnabled),
+ mLocationMode(locationMode),
+ mBrightnessFactor(brightnessFactor) {
+ }
+
+ bool getBatterySaverEnabled() const { return mBatterySaverEnabled; }
+ bool getGlobalBatterySaverEnabled() const { return mGlobalBatterySaverEnabled; }
+ LocationMode getLocationMode() const { return mLocationMode; }
+ float getBrightnessFactor() const { return mBrightnessFactor; }
+ bool operator == (const PowerSaveState &ps) const {
+ return mBatterySaverEnabled == ps.mBatterySaverEnabled &&
+ mGlobalBatterySaverEnabled == ps.mGlobalBatterySaverEnabled &&
+ mLocationMode == ps.mLocationMode &&
+ fabs(mBrightnessFactor - ps.mBrightnessFactor) == 0.0f;
+ }
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+private:
+ /** Whether we should enable battery saver for this service. */
+ bool mBatterySaverEnabled;
+ /** Whether battery saver mode is enabled. */
+ bool mGlobalBatterySaverEnabled;
+ /** Location mode */
+ LocationMode mLocationMode;
+ /** Screen brightness factor. */
+ float mBrightnessFactor;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_POWER_SAVE_STATE_H */
diff --git a/services/powermanager/include/android/WorkSource.h b/services/powermanager/include/android/WorkSource.h
new file mode 100644
index 0000000..f12847d
--- /dev/null
+++ b/services/powermanager/include/android/WorkSource.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_WORKSOURCE_H
+#define ANDROID_OS_WORKSOURCE_H
+
+#include <optional>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+/**
+ * WorkSource is a structure to describes the source of some work that may be done by someone else.
+ * This file needs to be kept in sync with frameworks/base/core/java/android/os/WorkSource.java
+ */
+struct WorkSource : public android::Parcelable {
+ WorkSource(
+ std::vector<int32_t> uids = {},
+ std::optional<std::vector<std::optional<String16>>> names = std::nullopt)
+ : mUids(uids),
+ mNames(names) {
+ }
+ std::vector<int32_t> getUids() const { return mUids; }
+ std::optional<std::vector<std::optional<String16>>> getNames() const { return mNames; }
+ bool operator == (const WorkSource &ws) const {
+ return mUids == ws.mUids && mNames == ws.mNames;
+ }
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+private:
+ /** WorkSource UID array */
+ std::vector<int32_t> mUids = {};
+ /** WorkSource Tag array */
+ std::optional<std::vector<std::optional<String16>>> mNames = {};
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_WORKSOURCE_H */
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
new file mode 100644
index 0000000..49abc11
--- /dev/null
+++ b/services/powermanager/tests/Android.bp
@@ -0,0 +1,45 @@
+// 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: "libpowermanager_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "IThermalManagerTest.cpp",
+ "PowerHalControllerTest.cpp",
+ "PowerHalLoaderTest.cpp",
+ "PowerHalWrapperAidlTest.cpp",
+ "PowerHalWrapperHidlV1_0Test.cpp",
+ "PowerHalWrapperHidlV1_1Test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libpowermanager",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/services/powermanager/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp
similarity index 100%
rename from services/powermanager/IThermalManagerTest.cpp
rename to services/powermanager/tests/IThermalManagerTest.cpp
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
new file mode 100644
index 0000000..141b244
--- /dev/null
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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 "PowerHalControllerTest"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalController.h>
+#include <utils/Log.h>
+
+#include <thread>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+class TestPowerHalConnector : public HalConnector {
+public:
+ TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {}
+ virtual ~TestPowerHalConnector() = default;
+
+ virtual std::unique_ptr<HalWrapper> connect() override {
+ mCountMutex.lock();
+ ++mConnectedCount;
+ mCountMutex.unlock();
+ return std::make_unique<HidlHalWrapperV1_0>(mHal);
+ }
+
+ void reset() override {
+ mCountMutex.lock();
+ ++mResetCount;
+ mCountMutex.unlock();
+ }
+
+ int getConnectCount() { return mConnectedCount; }
+
+ int getResetCount() { return mResetCount; }
+
+private:
+ sp<IPower> mHal = nullptr;
+ std::mutex mCountMutex;
+ int mConnectedCount = 0;
+ int mResetCount = 0;
+};
+
+class AlwaysFailingTestPowerHalConnector : public TestPowerHalConnector {
+public:
+ AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {}
+
+ std::unique_ptr<HalWrapper> connect() override {
+ // Call parent to update counter, but ignore connected HalWrapper.
+ TestPowerHalConnector::connect();
+ return nullptr;
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalControllerTest : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIPowerV1_0>();
+ std::unique_ptr<TestPowerHalConnector> halConnector =
+ std::make_unique<TestPowerHalConnector>(mMockHal);
+ mHalConnector = halConnector.get();
+ mHalController = std::make_unique<PowerHalController>(std::move(halConnector));
+ }
+
+protected:
+ sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+ TestPowerHalConnector* mHalConnector = nullptr;
+ std::unique_ptr<PowerHalController> mHalController = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalControllerTest, TestInitConnectsToPowerHalOnlyOnce) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ mHalController->init();
+ mHalController->init();
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) {
+ std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector =
+ std::make_unique<AlwaysFailingTestPowerHalConnector>();
+ AlwaysFailingTestPowerHalConnector* failingHalConnector = halConnector.get();
+ PowerHalController halController(std::move(halConnector));
+
+ int powerHalConnectCount = failingHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ // Still works with EmptyPowerHalWrapper as fallback ignoring every api call
+ // and logging.
+ auto result = halController.setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = halController.setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+
+ // PowerHalConnector was called every time to attempt to reconnect with
+ // underlying service.
+ powerHalConnectCount = failingHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 2);
+ // PowerHalConnector was never reset.
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestAllApiCallsDelegatedToConnectedPowerHal) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ {
+ InSequence seg;
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(100)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
+ }
+
+ auto result = mHalController->setBoost(Boost::INTERACTION, 100);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalRecoversFromFailureByRecreatingPowerHal) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+ .WillByDefault([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(4));
+
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::FAILED, result);
+ result = mHalController->setMode(Mode::VR, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mHalController->setMode(Mode::LOW_POWER, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+
+ // PowerHalConnector was called only twice: on first api call and after failed
+ // call.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 2);
+ // PowerHalConnector was reset once after failed call.
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 1);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalDoesNotTryToRecoverFromFailureOnUnsupportedCalls) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(10));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // PowerHalConnector was called only by the first thread to use the api and
+ // never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadWithFailureReconnectIsThreadSafe) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+ .WillByDefault([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(40));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::FAILED, result);
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::LOW_POWER, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::VR, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // PowerHalConnector was called at least once by the first thread.
+ // Reset and reconnect calls were made at most 10 times, once after each
+ // failure.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11)));
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_THAT(powerHalResetCount, Le(10));
+}
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
new file mode 100644
index 0000000..058e1b5
--- /dev/null
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "PowerHalLoaderTest"
+
+#include <android-base/logging.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalLoader.h>
+
+#include <future>
+
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerAidl = android::hardware::power::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+sp<T> loadHal();
+
+template <>
+sp<IPowerAidl> loadHal<IPowerAidl>() {
+ return PowerHalLoader::loadAidl();
+}
+
+template <>
+sp<IPowerV1_0> loadHal<IPowerV1_0>() {
+ return PowerHalLoader::loadHidlV1_0();
+}
+
+template <>
+sp<IPowerV1_1> loadHal<IPowerV1_1>() {
+ return PowerHalLoader::loadHidlV1_1();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+class PowerHalLoaderTest : public Test {
+public:
+ sp<T> load() { return ::loadHal<T>(); }
+ void unload() { PowerHalLoader::unloadAll(); }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes;
+TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
+ sp<TypeParam> firstHal = this->load();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+ sp<TypeParam> secondHal = this->load();
+ ASSERT_EQ(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestUnload) {
+ sp<TypeParam> firstHal = this->load();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+ this->unload();
+ sp<TypeParam> secondHal = this->load();
+ ASSERT_NE(secondHal, nullptr);
+ ASSERT_NE(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) {
+ std::vector<std::future<sp<TypeParam>>> futures;
+ for (int i = 0; i < 10; i++) {
+ futures.push_back(
+ std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this));
+ }
+
+ futures[0].wait();
+ sp<TypeParam> firstHal = futures[0].get();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+
+ for (int i = 1; i < 10; i++) {
+ futures[i].wait();
+ sp<TypeParam> currentHal = futures[i].get();
+ ASSERT_EQ(firstHal, currentHal);
+ }
+}
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
new file mode 100644
index 0000000..a765659
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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 "PowerHalWrapperAidlTest"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+#include <thread>
+
+using android::binder::Status;
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::Mode;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPower : public IPower {
+public:
+ MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
+ MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
+ MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
+ MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperAidlTest : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPower>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperAidlTest::SetUp() {
+ mMockHal = new StrictMock<MockIPower>();
+ mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ }
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
+ ASSERT_EQ(HalResult::FAILED, result);
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 1000);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = mWrapper->setBoost(Boost::CAMERA_SHOT, 10);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostMultiThreadCheckSupportedOnlyOnce) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10));
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::FAILED, result);
+ result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeMultiThreadCheckSupportedOnlyOnce) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10));
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->setMode(Mode::LAUNCH, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
new file mode 100644
index 0000000..6693d0b
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 "PowerHalWrapperHidlV1_0Test"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_0Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_0Test::SetUp() {
+ mMockHal = new StrictMock<MockIPowerV1_0>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_0>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostSuccessful) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))).Times(Exactly(1));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) {
+ auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) {
+ auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
new file mode 100644
index 0000000..55bbd6d
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -0,0 +1,157 @@
+/*
+ * 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 "PowerHalWrapperHidlV1_1Test"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::PowerHint;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPowerV1_0 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+class MockIPowerV1_1 : public IPowerV1_1 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats,
+ (getSubsystemLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_1Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr;
+ sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_1Test::SetUp() {
+ mMockHalV1_0 = new StrictMock<MockIPowerV1_0>();
+ mMockHalV1_1 = new StrictMock<MockIPowerV1_1>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) {
+ auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(0)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(),
+ powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(0)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_0.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) {
+ auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 60f9cd9..aa0dc92 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -699,6 +699,9 @@
if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) {
return PERMISSION_DENIED;
}
+ if (args.size() == 0) {
+ return BAD_INDEX;
+ }
if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
return BAD_VALUE;
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index a790d0b..e341af1 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -145,6 +145,7 @@
"DisplayHardware/HWComposer.cpp",
"DisplayHardware/PowerAdvisor.cpp",
"DisplayHardware/VirtualDisplaySurface.cpp",
+ "DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
"EventLog/EventLog.cpp",
"FrameTracer/FrameTracer.cpp",
@@ -152,6 +153,7 @@
"Layer.cpp",
"LayerProtoHelper.cpp",
"LayerRejecter.cpp",
+ "LayerRenderArea.cpp",
"LayerVector.cpp",
"MonitoredProducer.cpp",
"NativeWindowSurface.cpp",
@@ -160,7 +162,6 @@
"RenderArea.cpp",
"Scheduler/DispSync.cpp",
"Scheduler/DispSyncSource.cpp",
- "Scheduler/EventControlThread.cpp",
"Scheduler/EventThread.cpp",
"Scheduler/OneShotTimer.cpp",
"Scheduler/LayerHistory.cpp",
@@ -168,15 +169,15 @@
"Scheduler/LayerInfo.cpp",
"Scheduler/LayerInfoV2.cpp",
"Scheduler/MessageQueue.cpp",
- "Scheduler/PhaseOffsets.cpp",
"Scheduler/RefreshRateConfigs.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
"Scheduler/Timer.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncPredictor.cpp",
- "Scheduler/VSyncModulator.cpp",
+ "Scheduler/VsyncModulator.cpp",
"Scheduler/VSyncReactor.cpp",
+ "Scheduler/VsyncConfiguration.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceFlingerDefaultFactory.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index f0b0200..cf60b71 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -539,20 +539,6 @@
return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
}
-bool BufferLayer::latchUnsignaledBuffers() {
- static bool propertyLoaded = false;
- static bool latch = false;
- static std::mutex mutex;
- std::lock_guard<std::mutex> lock(mutex);
- if (!propertyLoaded) {
- char value[PROPERTY_VALUE_MAX] = {};
- property_get("debug.sf.latch_unsignaled", value, "0");
- latch = atoi(value);
- propertyLoaded = true;
- }
- return latch;
-}
-
// h/w composer set-up
bool BufferLayer::allTransactionsSignaled(nsecs_t expectedPresentTime) {
const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 26bfb49..2483abb 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -187,9 +187,6 @@
bool onPreComposition(nsecs_t) override;
void preparePerFrameCompositionState() override;
- // Loads the corresponding system property once per process
- static bool latchUnsignaledBuffers();
-
// Check all of the local sync points to ensure that all transactions
// which need to have been applied prior to the frame which is about to
// be latched have signaled
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 6e4235e..89284f2 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -131,10 +131,6 @@
// -----------------------------------------------------------------------
bool BufferQueueLayer::fenceHasSignaled() const {
- if (latchUnsignaledBuffers()) {
- return true;
- }
-
if (!hasFrameUpdate()) {
return true;
}
@@ -208,8 +204,12 @@
}
bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
+ // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
+ const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+
bool sidebandStreamChanged = true;
- if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
+ if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false) ||
+ updateSidebandStream) {
// mSidebandStreamChanged was changed to false
mSidebandStream = mConsumer->getSidebandStream();
auto* layerCompositionState = editCompositionState();
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 790f2ec..884cc0c 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -422,10 +422,6 @@
// Interface implementation for BufferLayer
// -----------------------------------------------------------------------
bool BufferStateLayer::fenceHasSignaled() const {
- if (latchUnsignaledBuffers()) {
- return true;
- }
-
const bool fenceSignaled =
getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
if (!fenceSignaled) {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 6cc90cb..5c7f12d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -80,10 +80,6 @@
// The clip region, or visible region that is being rendered to
const Region& clip;
- // If true, the layer should use an identity transform for its position
- // transform. Used only by the captureScreen API call.
- const bool useIdentityTransform;
-
// If set to true, the layer should enable filtering when rendering.
const bool needsFiltering;
@@ -148,7 +144,6 @@
static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
const LayerFE::ClientCompositionTargetSettings& rhs) {
return lhs.clip.hasSameRects(rhs.clip) &&
- lhs.useIdentityTransform == rhs.useIdentityTransform &&
lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport &&
@@ -170,7 +165,6 @@
*os << "ClientCompositionTargetSettings{";
*os << "\n .clip = \n";
PrintTo(settings.clip, os);
- *os << "\n .useIdentityTransform = " << settings.useIdentityTransform;
*os << "\n .needsFiltering = " << settings.needsFiltering;
*os << "\n .isSecure = " << settings.isSecure;
*os << "\n .supportsProtectedContent = " << settings.supportsProtectedContent;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index baf5258..6cc7a53 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,11 +163,12 @@
virtual void setCompositionEnabled(bool) = 0;
// Sets the projection state to use
- virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& sourceClip,
- const Rect& destinationClip, bool needsFiltering) = 0;
+ virtual void setProjection(const ui::Transform&, uint32_t orientation,
+ const Rect& orientedDisplaySpaceRect,
+ const Rect& layerStackSpaceRect, const Rect& displaySpaceRect,
+ bool needsFiltering) = 0;
// Sets the bounds to use
- virtual void setBounds(const ui::Size&) = 0;
+ virtual void setDisplaySpaceSize(const ui::Size&) = 0;
// Sets the layer stack filtering settings for this output. See
// belongsInOutput for full details.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
new file mode 100644
index 0000000..9d15665
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -0,0 +1,56 @@
+/*
+ * 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 <ostream>
+
+#include <android-base/stringprintf.h>
+#include <ui/Rect.h>
+#include <ui/Rotation.h>
+#include <ui/Transform.h>
+
+namespace android {
+namespace compositionengine {
+
+// Geometrical space to which content is projected.
+// For example, this can be the layer space or the physical display space.
+struct ProjectionSpace {
+ ProjectionSpace() = default;
+ ProjectionSpace(ui::Size size, Rect content)
+ : bounds(std::move(size)), content(std::move(content)) {}
+
+ // Bounds of this space. Always starts at (0,0).
+ Rect bounds;
+
+ // Rect onto which content is projected.
+ Rect content;
+};
+
+} // namespace compositionengine
+
+inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
+ return android::base::StringPrintf("ProjectionSpace(bounds = %s, content = %s)",
+ to_string(space.bounds).c_str(),
+ to_string(space.content).c_str());
+}
+
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(const android::compositionengine::ProjectionSpace& space, ::std::ostream* os) {
+ *os << to_string(space);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 6f25e63..57b7a97 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -38,10 +38,10 @@
bool isValid() const override;
std::optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
- void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& sourceClip, const Rect& destinationClip,
- bool needsFiltering) override;
- void setBounds(const ui::Size&) override;
+ void setProjection(const ui::Transform&, uint32_t orientation,
+ const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
+ const Rect& displaySpaceRect, bool needsFiltering) override;
+ void setDisplaySpaceSize(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 66ed2b6..462d952 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -29,6 +29,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+#include <compositionengine/ProjectionSpace.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Transform.h>
@@ -50,8 +51,7 @@
// If true, the current frame on this output uses device composition
bool usesDeviceComposition{false};
- // If true, the client target should be flipped when performing client
- // composition
+ // If true, the client target should be flipped when performing client composition
bool flipClientTarget{false};
// If true, the current frame reused the buffer from a previous client composition
@@ -63,28 +63,26 @@
// The layer stack to display on this display
uint32_t layerStackId{~0u};
- // The physical space screen bounds
- Rect bounds;
+ // The common space for all layers in the layer stack. layerStackSpace.content is the Rect
+ // which gets projected on the display. The content in this space is always in a single
+ // orientation.
+ ProjectionSpace layerStackSpace;
- // The logical to physical transformation to use
+ // Oriented physical display space. It will have the same size as displaySpace oriented to
+ // match the orientation of layerStackSpace. The content in this space is always in a single
+ // orientation.
+ ProjectionSpace orientedDisplaySpace;
+
+ // The space of the physical display. It is as big as the currently active display mode. The
+ // content in this space can be rotated.
+ ProjectionSpace displaySpace;
+
+ // Transformation from layerStackSpace to displaySpace
ui::Transform transform;
- // The physical orientation of the display, expressed as ui::Transform
- // orientation flags.
+ // The physical orientation of the display, expressed as ui::Transform orientation flags.
uint32_t orientation{0};
- // The logical space user visible bounds
- Rect frame;
-
- // The logical space user viewport rectangle
- Rect viewport;
-
- // The physical space source clip rectangle
- Rect sourceClip;
-
- // The physical space destination clip rectangle
- Rect destinationClip;
-
// If true, RenderEngine filtering should be enabled
bool needsFiltering{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 4661c5d..375d334 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,10 +36,9 @@
MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
MOCK_METHOD1(setCompositionEnabled, void(bool));
- MOCK_METHOD7(setProjection,
- void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&,
- const Rect&, bool));
- MOCK_METHOD1(setBounds, void(const ui::Size&));
+ MOCK_METHOD6(setProjection,
+ void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&, bool));
+ MOCK_METHOD1(setDisplaySpaceSize, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 9598430..9d1bb02 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -77,6 +77,7 @@
void dumpVal(std::string& out, const char* name, const ui::Transform& transform) {
transform.dump(out, name);
+ out.append(" ");
}
void dumpVal(std::string& out, const char* name, const ui::Size& size) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 34dc536..9e0a43a 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -105,26 +105,32 @@
dirtyEntireOutput();
}
-void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& sourceClip,
- const Rect& destinationClip, bool needsFiltering) {
+void Output::setProjection(const ui::Transform& transform, uint32_t orientation,
+ const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
+ const Rect& displaySpaceRect, bool needsFiltering) {
auto& outputState = editState();
outputState.transform = transform;
outputState.orientation = orientation;
- outputState.sourceClip = sourceClip;
- outputState.destinationClip = destinationClip;
- outputState.frame = frame;
- outputState.viewport = viewport;
+ outputState.displaySpace.content = displaySpaceRect;
+ // outputState.displaySpace.bounds should be already set from setDisplaySpaceSize().
+ outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+
+ ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
+ if (orientation == ui::Transform::ROT_90 || orientation == ui::Transform::ROT_270) {
+ std::swap(orientedSize.width, orientedSize.height);
+ }
+ outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
+
+ outputState.layerStackSpace.content = layerStackSpaceRect;
+ outputState.layerStackSpace.bounds = layerStackSpaceRect;
outputState.needsFiltering = needsFiltering;
dirtyEntireOutput();
}
-// TODO(b/121291683): Rename setSize() once more is moved.
-void Output::setBounds(const ui::Size& size) {
+void Output::setDisplaySpaceSize(const ui::Size& size) {
mRenderSurface->setDisplaySize(size);
- // TODO(b/121291683): Rename outputState.size once more is moved.
- editState().bounds = Rect(mRenderSurface->getSize());
+ editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
dirtyEntireOutput();
}
@@ -232,7 +238,7 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- editState().bounds = Rect(mRenderSurface->getSize());
+ editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
dirtyEntireOutput();
}
@@ -251,7 +257,7 @@
Region Output::getDirtyRegion(bool repaintEverything) const {
const auto& outputState = getState();
- Region dirty(outputState.viewport);
+ Region dirty(outputState.layerStackSpace.content);
if (!repaintEverything) {
dirty.andSelf(outputState.dirtyRegion);
}
@@ -336,7 +342,7 @@
// Compute the resulting coverage for this output, and store it for later
const ui::Transform& tr = outputState.transform;
- Region undefinedRegion{outputState.bounds};
+ Region undefinedRegion{outputState.displaySpace.bounds};
undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
outputState.undefinedRegion = undefinedRegion;
@@ -539,7 +545,7 @@
// TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
const auto& outputState = getState();
Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
- drawRegion.andSelf(outputState.bounds);
+ drawRegion.andSelf(outputState.displaySpace.bounds);
if (drawRegion.isEmpty()) {
return;
}
@@ -556,8 +562,8 @@
outputLayerState.visibleRegion = visibleRegion;
outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
outputLayerState.coveredRegion = coveredRegion;
- outputLayerState.outputSpaceVisibleRegion =
- outputState.transform.transform(visibleNonShadowRegion.intersect(outputState.viewport));
+ outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
+ visibleNonShadowRegion.intersect(outputState.layerStackSpace.content));
outputLayerState.shadowRegion = shadowRegion;
}
@@ -862,8 +868,8 @@
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
- clientCompositionDisplay.clip = outputState.sourceClip;
+ clientCompositionDisplay.physicalDisplay = outputState.displaySpace.content;
+ clientCompositionDisplay.clip = outputState.layerStackSpace.content;
clientCompositionDisplay.orientation = outputState.orientation;
clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
? outputState.dataspace
@@ -921,9 +927,8 @@
const nsecs_t renderEngineStart = systemTime();
status_t status =
- renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers,
- buf->getNativeBuffer(), /*useFramebufferCache=*/true,
- std::move(fd), &readyFence);
+ renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buf,
+ /*useFramebufferCache=*/true, std::move(fd), &readyFence);
if (status != NO_ERROR && mClientCompositionRequestCache) {
// If rendering was not successful, remove the request from the cache.
@@ -948,8 +953,7 @@
ALOGV("Rendering client layers");
const auto& outputState = getState();
- const Region viewportRegion(outputState.viewport);
- const bool useIdentityTransform = false;
+ const Region viewportRegion(outputState.layerStackSpace.content);
bool firstLayer = true;
// Used when a layer clears part of the buffer.
Region stubRegion;
@@ -985,18 +989,17 @@
!layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
if (clientComposition || clearClientComposition) {
- compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
- clip,
- useIdentityTransform,
- layer->needsFiltering() || outputState.needsFiltering,
- outputState.isSecure,
- supportsProtectedContent,
- clientComposition ? clearRegion : stubRegion,
- outputState.viewport,
- outputDataspace,
- realContentIsVisible,
- !clientComposition, /* clearContent */
- };
+ compositionengine::LayerFE::ClientCompositionTargetSettings
+ targetSettings{.clip = clip,
+ .needsFiltering =
+ layer->needsFiltering() || outputState.needsFiltering,
+ .isSecure = outputState.isSecure,
+ .supportsProtectedContent = supportsProtectedContent,
+ .clearRegion = clientComposition ? clearRegion : stubRegion,
+ .viewport = outputState.layerStackSpace.content,
+ .dataspace = outputDataspace,
+ .realContentIsVisible = realContentIsVisible,
+ .clearContent = !clientComposition};
std::vector<LayerFE::LayerSettings> results =
layerFE.prepareClientCompositionList(targetSettings);
if (realContentIsVisible && !results.empty()) {
@@ -1093,7 +1096,7 @@
void Output::dirtyEntireOutput() {
auto& outputState = editState();
- outputState.dirtyRegion.set(outputState.bounds);
+ outputState.dirtyRegion.set(outputState.displaySpace.bounds);
}
void Output::chooseCompositionStrategy() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 4835aef..776fdde 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -37,12 +37,12 @@
dumpVal(out, "transform", transform);
out.append("\n ");
-
- dumpVal(out, "bounds", bounds);
- dumpVal(out, "frame", frame);
- dumpVal(out, "viewport", viewport);
- dumpVal(out, "sourceClip", sourceClip);
- dumpVal(out, "destinationClip", destinationClip);
+ dumpVal(out, "layerStackSpace", to_string(layerStackSpace));
+ out.append("\n ");
+ dumpVal(out, "orientedDisplaySpace", to_string(orientedDisplaySpace));
+ out.append("\n ");
+ dumpVal(out, "displaySpace", to_string(displaySpace));
+ out.append("\n ");
dumpVal(out, "needsFiltering", needsFiltering);
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 1faf775..376b4b3 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -77,7 +77,7 @@
FloatRect activeCropFloat =
reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
- const Rect& viewport = getOutput().getState().viewport;
+ const Rect& viewport = getOutput().getState().layerStackSpace.content;
const ui::Transform& layerTransform = layerState.geomLayerTransform;
const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
// Transform to screen space.
@@ -189,7 +189,7 @@
Rect activeCrop = layerState.geomCrop;
if (!activeCrop.isEmpty() && bufferSize.isValid()) {
activeCrop = layerTransform.transform(activeCrop);
- if (!activeCrop.intersect(outputState.viewport, &activeCrop)) {
+ if (!activeCrop.intersect(outputState.layerStackSpace.content, &activeCrop)) {
activeCrop.clear();
}
activeCrop = inverseLayerTransform.transform(activeCrop, true);
@@ -215,7 +215,7 @@
// transformation. We then round upon constructing 'frame'.
Rect frame{
layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))};
- if (!frame.intersect(outputState.viewport, &frame)) {
+ if (!frame.intersect(outputState.layerStackSpace.content, &frame)) {
frame.clear();
}
const ui::Transform displayTransform{outputState.transform};
@@ -568,7 +568,7 @@
const auto& outputState = getOutput().getState();
Rect frame = layerFEState->cursorFrame;
- frame.intersect(outputState.viewport, &frame);
+ frame.intersect(outputState.layerStackSpace.content, &frame);
Rect position = outputState.transform.transform(frame);
if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 09f37fb..4519a9d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -56,8 +56,9 @@
using testing::SetArgPointee;
using testing::StrictMock;
-constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
-constexpr DisplayId VIRTUAL_DISPLAY_ID = DisplayId{43};
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId{42};
+// TODO(b/160679868) Use VirtualDisplayId
+constexpr PhysicalDisplayId VIRTUAL_DISPLAY_ID = PhysicalDisplayId{43};
constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
constexpr int32_t DEFAULT_LAYER_STACK = 123;
@@ -907,7 +908,7 @@
mDisplay->editState().isEnabled = true;
mDisplay->editState().usesClientComposition = false;
- mDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
@@ -928,7 +929,7 @@
nonHwcDisplay->editState().isEnabled = true;
nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
@@ -949,7 +950,7 @@
nonHwcDisplay->editState().isEnabled = true;
nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
nonHwcDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
CompositionRefreshArgs refreshArgs;
@@ -970,7 +971,7 @@
nonHwcDisplay->editState().isEnabled = true;
nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 75a4fec..1dd5df4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -46,7 +46,7 @@
MOCK_METHOD3(allocateVirtualDisplay,
std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
- MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, DisplayId));
+ MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
MOCK_METHOD3(getDeviceCompositionChanges,
@@ -107,8 +107,8 @@
MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hal::HWDisplayId>(int32_t));
MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hal::HWDisplayId>());
MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hal::HWDisplayId>());
- MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<DisplayId>(hal::HWDisplayId));
- MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(DisplayId));
+ MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<PhysicalDisplayId>(hal::HWDisplayId));
+ MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(PhysicalDisplayId));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 020f93a..df3da85 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -138,7 +138,7 @@
mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
mLayerFEState.geomBufferTransform = TR_IDENT;
- mOutputState.viewport = Rect{0, 0, 1920, 1080};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
}
FloatRect calculateOutputSourceCrop() {
@@ -223,7 +223,7 @@
}
TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
- mOutputState.viewport = Rect{0, 0, 960, 540};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
const FloatRect expected{0.f, 0.f, 960.f, 540.f};
EXPECT_THAT(calculateOutputSourceCrop(), expected);
@@ -245,7 +245,7 @@
mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
- mOutputState.viewport = Rect{0, 0, 1920, 1080};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
mOutputState.transform = ui::Transform{TR_IDENT};
}
@@ -293,7 +293,7 @@
}
TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
- mOutputState.viewport = Rect{0, 0, 960, 540};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
const Rect expected{0, 0, 960, 540};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
@@ -988,7 +988,7 @@
mLayerFEState.cursorFrame = kDefaultCursorFrame;
- mOutputState.viewport = kDefaultDisplayViewport;
+ mOutputState.layerStackSpace.content = kDefaultDisplayViewport;
mOutputState.transform = ui::Transform{kDefaultTransform};
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 7a06400..3dd26c0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -136,7 +136,7 @@
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
- mOutput->editState().bounds = kDefaultDisplaySize;
+ mOutput->editState().displaySpace.bounds = kDefaultDisplaySize;
}
void injectOutputLayer(InjectedLayer& layer) {
@@ -240,24 +240,22 @@
const int32_t orientation = 123;
const Rect frame{1, 2, 3, 4};
const Rect viewport{5, 6, 7, 8};
- const Rect sourceClip{9, 10, 11, 12};
const Rect destinationClip{13, 14, 15, 16};
const bool needsFiltering = true;
- mOutput->setProjection(transform, orientation, frame, viewport, sourceClip, destinationClip,
+ mOutput->setProjection(transform, orientation, frame, viewport, destinationClip,
needsFiltering);
EXPECT_THAT(mOutput->getState().transform, transform);
EXPECT_EQ(orientation, mOutput->getState().orientation);
- EXPECT_EQ(frame, mOutput->getState().frame);
- EXPECT_EQ(viewport, mOutput->getState().viewport);
- EXPECT_EQ(sourceClip, mOutput->getState().sourceClip);
- EXPECT_EQ(destinationClip, mOutput->getState().destinationClip);
+ 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);
}
/*
- * Output::setBounds()
+ * Output::setDisplaySpaceSize()
*/
TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
@@ -266,9 +264,9 @@
EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
- mOutput->setBounds(displaySize);
+ mOutput->setDisplaySpaceSize(displaySize);
- EXPECT_EQ(Rect(displaySize), mOutput->getState().bounds);
+ EXPECT_EQ(Rect(displaySize), mOutput->getState().displaySpace.bounds);
EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
}
@@ -431,7 +429,7 @@
mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
- EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().bounds);
+ EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().displaySpace.bounds);
}
/*
@@ -440,7 +438,7 @@
TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
const Rect viewport{100, 200};
- mOutput->editState().viewport = viewport;
+ mOutput->editState().layerStackSpace.content = viewport;
mOutput->editState().dirtyRegion.set(50, 300);
{
@@ -452,7 +450,7 @@
TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
const Rect viewport{100, 200};
- mOutput->editState().viewport = viewport;
+ mOutput->editState().layerStackSpace.content = viewport;
mOutput->editState().dirtyRegion.set(50, 300);
{
@@ -860,7 +858,7 @@
OutputRebuildLayerStacksTest() {
mOutput.mState.isEnabled = true;
mOutput.mState.transform = kIdentityTransform;
- mOutput.mState.bounds = kOutputBounds;
+ mOutput.mState.displaySpace.bounds = kOutputBounds;
mRefreshArgs.updatingOutputGeometryThisFrame = true;
@@ -1067,8 +1065,8 @@
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
.WillRepeatedly(Return(&mLayer.outputLayer));
- mOutput.mState.bounds = Rect(0, 0, 200, 300);
- mOutput.mState.viewport = Rect(0, 0, 200, 300);
+ mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300);
+ mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300);
mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
mLayer.layerFEState.isVisible = true;
@@ -1148,7 +1146,7 @@
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
- mOutput.mState.bounds = Rect(0, 0, 0, 0);
+ mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0);
ensureOutputLayerIfVisible();
}
@@ -1345,7 +1343,7 @@
mLayer.layerFEState.contentDirty = true;
mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mOutput.mState.viewport = Rect(0, 0, 300, 200);
+ mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
@@ -1371,7 +1369,7 @@
mLayer.layerFEState.contentDirty = true;
mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mOutput.mState.viewport = Rect(0, 0, 300, 200);
+ mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
@@ -2785,10 +2783,9 @@
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE);
- mOutput.mState.frame = kDefaultOutputFrame;
- mOutput.mState.viewport = kDefaultOutputViewport;
- mOutput.mState.sourceClip = kDefaultOutputSourceClip;
- mOutput.mState.destinationClip = kDefaultOutputDestinationClip;
+ mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
+ mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
+ mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
mOutput.mState.orientation = kDefaultOutputOrientation;
mOutput.mState.dataspace = kDefaultOutputDataspace;
@@ -2834,7 +2831,6 @@
static const Rect kDefaultOutputFrame;
static const Rect kDefaultOutputViewport;
- static const Rect kDefaultOutputSourceClip;
static const Rect kDefaultOutputDestinationClip;
static const mat4 kDefaultColorTransformMat;
@@ -2856,7 +2852,6 @@
const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
-const Rect OutputComposeSurfacesTest::kDefaultOutputSourceClip{1009, 1010, 1011, 1012};
const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs;
@@ -3122,7 +3117,7 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
Region::INVALID_REGION, kDefaultOutputOrientation})
.execute()
@@ -3133,7 +3128,7 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
Region::INVALID_REGION, kDefaultOutputOrientation})
.execute()
@@ -3144,7 +3139,7 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace,
kDefaultColorTransformMat, Region::INVALID_REGION,
kDefaultOutputOrientation})
@@ -3156,7 +3151,7 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace,
kDefaultColorTransformMat, Region::INVALID_REGION,
kDefaultOutputOrientation})
@@ -3169,7 +3164,7 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(true)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
Region::INVALID_REGION, kDefaultOutputOrientation})
.execute()
@@ -3414,10 +3409,9 @@
struct GenerateClientCompositionRequestsTest_ThreeLayers
: public GenerateClientCompositionRequestsTest {
GenerateClientCompositionRequestsTest_ThreeLayers() {
- mOutput.mState.frame = kDisplayFrame;
- mOutput.mState.viewport = kDisplayViewport;
- mOutput.mState.sourceClip = kDisplaySourceClip;
- mOutput.mState.destinationClip = kDisplayDestinationClip;
+ mOutput.mState.orientedDisplaySpace.content = kDisplayFrame;
+ mOutput.mState.layerStackSpace.content = kDisplayViewport;
+ mOutput.mState.displaySpace.content = kDisplayDestinationClip;
mOutput.mState.transform = ui::Transform{kDisplayOrientation};
mOutput.mState.orientation = kDisplayOrientation;
mOutput.mState.needsFiltering = false;
@@ -3448,7 +3442,6 @@
static const Rect kDisplayFrame;
static const Rect kDisplayViewport;
- static const Rect kDisplaySourceClip;
static const Rect kDisplayDestinationClip;
std::array<Layer, 3> mLayers;
@@ -3456,7 +3449,6 @@
const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
-const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplaySourceClip(0, 0, 102, 202);
const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayDestinationClip(0, 0, 103,
203);
@@ -3587,7 +3579,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3599,7 +3590,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3643,7 +3633,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(Rect(10, 10, 20, 20)),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3655,7 +3644,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(Rect(0, 0, 30, 30)),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3667,7 +3655,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(Rect(0, 0, 40, 201)),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3699,7 +3686,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3711,7 +3697,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3723,7 +3708,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3755,7 +3739,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3768,7 +3751,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3780,7 +3762,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3811,7 +3792,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
@@ -3823,7 +3803,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
@@ -3835,7 +3814,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
@@ -3864,7 +3842,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
@@ -3876,7 +3853,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
@@ -3888,7 +3864,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
@@ -3945,15 +3920,13 @@
const Rect kPortraitFrame(0, 0, 1000, 2000);
const Rect kPortraitViewport(0, 0, 2000, 1000);
- const Rect kPortraitSourceClip(0, 0, 1000, 2000);
const Rect kPortraitDestinationClip(0, 0, 1000, 2000);
const uint32_t kPortraitOrientation = TR_ROT_90;
constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
- mOutput.mState.frame = kPortraitFrame;
- mOutput.mState.viewport = kPortraitViewport;
- mOutput.mState.sourceClip = kPortraitSourceClip;
- mOutput.mState.destinationClip = kPortraitDestinationClip;
+ mOutput.mState.orientedDisplaySpace.content = kPortraitFrame;
+ mOutput.mState.layerStackSpace.content = kPortraitViewport;
+ mOutput.mState.displaySpace.content = kPortraitDestinationClip;
mOutput.mState.transform = ui::Transform{kPortraitOrientation};
mOutput.mState.orientation = kPortraitOrientation;
mOutput.mState.needsFiltering = false;
@@ -3982,7 +3955,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
Region(Rect(0, 0, 1000, 1000)),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
true, /* supports protected content */
@@ -4000,7 +3972,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
Region(Rect(1000, 0, 2000, 1000)),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
true, /* supports protected content */
@@ -4034,7 +4005,6 @@
Region accumClearRegion(Rect(10, 11, 12, 13));
compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -4080,7 +4050,6 @@
Region accumClearRegion(Rect(10, 11, 12, 13));
compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index fd47e45..519cc57 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -33,7 +33,7 @@
constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
-constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = std::make_optional(DisplayId{123u});
+constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = {PhysicalDisplayId(123u)};
const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
using testing::_;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 730f297..016b6ca 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -29,6 +29,7 @@
#include <compositionengine/DisplayColorProfileCreationArgs.h>
#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/ProjectionSpace.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/RenderSurfaceCreationArgs.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -101,11 +102,11 @@
}
int DisplayDevice::getWidth() const {
- return mCompositionDisplay->getState().bounds.getWidth();
+ return mCompositionDisplay->getState().displaySpace.bounds.getWidth();
}
int DisplayDevice::getHeight() const {
- return mCompositionDisplay->getState().bounds.getHeight();
+ return mCompositionDisplay->getState().displaySpace.bounds.getHeight();
}
void DisplayDevice::setDisplayName(const std::string& displayName) {
@@ -116,6 +117,10 @@
}
}
+void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) {
+ mDeviceProductInfo = std::move(info);
+}
+
uint32_t DisplayDevice::getPageFlipCount() const {
return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
}
@@ -151,13 +156,14 @@
}
void DisplayDevice::setDisplaySize(int width, int height) {
- mCompositionDisplay->setBounds(ui::Size(width, height));
+ mCompositionDisplay->setDisplaySpaceSize(ui::Size(width, height));
}
-void DisplayDevice::setProjection(ui::Rotation orientation, Rect viewport, Rect frame) {
+void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
+ Rect orientedDisplaySpaceRect) {
mOrientation = orientation;
- const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
+ const Rect& displayBounds = getCompositionDisplay()->getState().displaySpace.bounds;
const int displayWidth = displayBounds.width();
const int displayHeight = displayBounds.height();
@@ -167,40 +173,38 @@
rotation.set(flags, displayWidth, displayHeight);
}
- if (!frame.isValid()) {
+ if (!orientedDisplaySpaceRect.isValid()) {
// the destination frame can be invalid if it has never been set,
// in that case we assume the whole display frame.
- frame = Rect(displayWidth, displayHeight);
+ orientedDisplaySpaceRect = Rect(displayWidth, displayHeight);
}
- if (viewport.isEmpty()) {
- // viewport can be invalid if it has never been set, in that case
+ if (layerStackSpaceRect.isEmpty()) {
+ // layerStackSpaceRect can be invalid if it has never been set, in that case
// we assume the whole display size.
- // it's also invalid to have an empty viewport, so we handle that
+ // It's also invalid to have an empty layerStackSpaceRect, so we handle that
// case in the same way.
- viewport = Rect(displayWidth, displayHeight);
+ layerStackSpaceRect = Rect(displayWidth, displayHeight);
if (rotation.getOrientation() & ui::Transform::ROT_90) {
- // viewport is always specified in the logical orientation
- // of the display (ie: post-rotation).
- std::swap(viewport.right, viewport.bottom);
+ std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
}
}
ui::Transform logicalTranslation, physicalTranslation, scale;
- const float sourceWidth = viewport.width();
- const float sourceHeight = viewport.height();
- const float destWidth = frame.width();
- const float destHeight = frame.height();
+ const float sourceWidth = layerStackSpaceRect.width();
+ const float sourceHeight = layerStackSpaceRect.height();
+ const float destWidth = orientedDisplaySpaceRect.width();
+ const float destHeight = orientedDisplaySpaceRect.height();
if (sourceWidth != destWidth || sourceHeight != destHeight) {
const float scaleX = destWidth / sourceWidth;
const float scaleY = destHeight / sourceHeight;
scale.set(scaleX, 0, 0, scaleY);
}
- const float sourceX = viewport.left;
- const float sourceY = viewport.top;
- const float destX = frame.left;
- const float destY = frame.top;
+ const float sourceX = layerStackSpaceRect.left;
+ const float sourceY = layerStackSpaceRect.top;
+ const float destX = orientedDisplaySpaceRect.left;
+ const float destY = orientedDisplaySpaceRect.top;
logicalTranslation.set(-sourceX, -sourceY);
physicalTranslation.set(destX, destY);
@@ -213,7 +217,7 @@
}
}
- // The viewport and frame are both in the logical orientation.
+ // The layerStackSpaceRect and orientedDisplaySpaceRect are both in the logical orientation.
// Apply the logical translation, scale to physical size, apply the
// physical translation and finally rotate to the physical orientation.
ui::Transform globalTransform = rotation * physicalTranslation * scale * logicalTranslation;
@@ -222,13 +226,12 @@
const bool needsFiltering =
(!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
- const Rect& sourceClip = viewport;
- Rect destinationClip = globalTransform.transform(viewport);
- if (destinationClip.isEmpty()) {
- destinationClip = displayBounds;
+ Rect displaySpaceRect = globalTransform.transform(layerStackSpaceRect);
+ if (displaySpaceRect.isEmpty()) {
+ displaySpaceRect = displayBounds;
}
- // Make sure the destination clip is contained in the display bounds
- destinationClip.intersect(displayBounds, &destinationClip);
+ // Make sure the displaySpaceRect is contained in the display bounds
+ displaySpaceRect.intersect(displayBounds, &displaySpaceRect);
uint32_t transformOrientation;
@@ -239,8 +242,9 @@
transformOrientation = ui::Transform::toRotationFlags(orientation);
}
- getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport,
- sourceClip, destinationClip, needsFiltering);
+ getCompositionDisplay()->setProjection(globalTransform, transformOrientation,
+ orientedDisplaySpaceRect, layerStackSpaceRect,
+ displaySpaceRect, needsFiltering);
}
ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
@@ -269,6 +273,12 @@
StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(),
static_cast<int32_t>(mPowerMode));
StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
+ StringAppendF(&result, "deviceProductInfo=");
+ if (mDeviceProductInfo) {
+ mDeviceProductInfo->dump(result);
+ } else {
+ result.append("{}");
+ }
getCompositionDisplay()->dump(result);
}
@@ -287,7 +297,7 @@
}
const Rect& DisplayDevice::getBounds() const {
- return mCompositionDisplay->getState().bounds;
+ return mCompositionDisplay->getState().displaySpace.bounds;
}
const Region& DisplayDevice::getUndefinedRegion() const {
@@ -306,16 +316,12 @@
return mCompositionDisplay->getState().transform;
}
-const Rect& DisplayDevice::getViewport() const {
- return mCompositionDisplay->getState().viewport;
+const Rect& DisplayDevice::getLayerStackSpaceRect() const {
+ return mCompositionDisplay->getState().layerStackSpace.content;
}
-const Rect& DisplayDevice::getFrame() const {
- return mCompositionDisplay->getState().frame;
-}
-
-const Rect& DisplayDevice::getSourceClip() const {
- return mCompositionDisplay->getState().sourceClip;
+const Rect& DisplayDevice::getOrientedDisplaySpaceRect() const {
+ return mCompositionDisplay->getState().orientedDisplaySpace.content;
}
bool DisplayDevice::hasWideColorGamut() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index cb467ea..35a8b62 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -40,7 +40,6 @@
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
-#include "RenderArea.h"
#include "Scheduler/HwcStrongTypes.h"
namespace android {
@@ -59,12 +58,14 @@
class DisplaySurface;
} // namespace compositionengine
-class DisplayDevice : public LightRefBase<DisplayDevice> {
+class DisplayDevice : public RefBase {
public:
constexpr static float sDefaultMinLumiance = 0.0;
constexpr static float sDefaultMaxLumiance = 500.0;
explicit DisplayDevice(DisplayDeviceCreationArgs& args);
+
+ // Must be destroyed on the main thread because it may call into HWComposer.
virtual ~DisplayDevice();
std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
@@ -98,9 +99,8 @@
}
const ui::Transform& getTransform() const;
- const Rect& getViewport() const;
- const Rect& getFrame() const;
- const Rect& getSourceClip() const;
+ const Rect& getLayerStackSpaceRect() const;
+ const Rect& getOrientedDisplaySpaceRect() const;
bool needsFiltering() const;
ui::LayerStack getLayerStack() const;
@@ -136,6 +136,11 @@
void setDisplayName(const std::string& displayName);
const std::string& getDisplayName() const { return mDisplayName; }
+ void setDeviceProductInfo(std::optional<DeviceProductInfo> info);
+ const std::optional<DeviceProductInfo>& getDeviceProductInfo() const {
+ return mDeviceProductInfo;
+ }
+
/* ------------------------------------------------------------------------
* Display power mode management.
*/
@@ -182,14 +187,16 @@
// TODO(b/74619554): Remove special cases for primary display.
const bool mIsPrimary;
+
+ std::optional<DeviceProductInfo> mDeviceProductInfo;
};
struct DisplayDeviceState {
struct Physical {
- DisplayId id;
+ PhysicalDisplayId id;
DisplayConnectionType type;
hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
-
+ std::optional<DeviceProductInfo> deviceProductInfo;
bool operator==(const Physical& other) const {
return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
}
@@ -201,8 +208,8 @@
std::optional<Physical> physical;
sp<IGraphicBufferProducer> surface;
ui::LayerStack layerStack = ui::NO_LAYER_STACK;
- Rect viewport;
- Rect frame;
+ Rect layerStackSpaceRect;
+ Rect orientedDisplaySpaceRect;
ui::Rotation orientation = ui::ROTATION_0;
uint32_t width = 0;
uint32_t height = 0;
@@ -237,118 +244,4 @@
bool isPrimary{false};
};
-class DisplayRenderArea : public RenderArea {
-public:
- DisplayRenderArea(const sp<const DisplayDevice>& display,
- RotationFlags rotation = ui::Transform::ROT_0)
- : DisplayRenderArea(display, display->getBounds(),
- static_cast<uint32_t>(display->getWidth()),
- static_cast<uint32_t>(display->getHeight()),
- display->getCompositionDataSpace(), rotation) {}
-
- DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation,
- bool allowSecureLayers = true)
- : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
- display->getViewport(), applyDeviceOrientation(rotation, display)),
- mDisplay(std::move(display)),
- mSourceCrop(sourceCrop),
- mAllowSecureLayers(allowSecureLayers) {}
-
- const ui::Transform& getTransform() const override { return mTransform; }
- Rect getBounds() const override { return mDisplay->getBounds(); }
- int getHeight() const override { return mDisplay->getHeight(); }
- int getWidth() const override { return mDisplay->getWidth(); }
- bool isSecure() const override { return mAllowSecureLayers && mDisplay->isSecure(); }
- sp<const DisplayDevice> getDisplayDevice() const override { return mDisplay; }
-
- bool needsFiltering() const override {
- // check if the projection from the logical render area
- // to the physical render area requires filtering
- const Rect& sourceCrop = getSourceCrop();
- int width = sourceCrop.width();
- int height = sourceCrop.height();
- if (getRotationFlags() & ui::Transform::ROT_90) {
- std::swap(width, height);
- }
- return width != getReqWidth() || height != getReqHeight();
- }
-
- Rect getSourceCrop() const override {
- // use the projected display viewport by default.
- if (mSourceCrop.isEmpty()) {
- return mDisplay->getSourceClip();
- }
-
- // If there is a source crop provided then it is assumed that the device
- // was in portrait orientation. This may not logically be true, so
- // correct for the orientation error by undoing the rotation
-
- ui::Rotation logicalOrientation = mDisplay->getOrientation();
- if (logicalOrientation == ui::Rotation::Rotation90) {
- logicalOrientation = ui::Rotation::Rotation270;
- } else if (logicalOrientation == ui::Rotation::Rotation270) {
- logicalOrientation = ui::Rotation::Rotation90;
- }
-
- const auto flags = ui::Transform::toRotationFlags(logicalOrientation);
- int width = mDisplay->getSourceClip().getWidth();
- int height = mDisplay->getSourceClip().getHeight();
- ui::Transform rotation;
- rotation.set(flags, width, height);
- return rotation.transform(mSourceCrop);
- }
-
-private:
- static RotationFlags applyDeviceOrientation(RotationFlags orientationFlag,
- const sp<const DisplayDevice>& device) {
- uint32_t inverseRotate90 = 0;
- uint32_t inverseReflect = 0;
-
- // Reverse the logical orientation.
- ui::Rotation logicalOrientation = device->getOrientation();
- if (logicalOrientation == ui::Rotation::Rotation90) {
- logicalOrientation = ui::Rotation::Rotation270;
- } else if (logicalOrientation == ui::Rotation::Rotation270) {
- logicalOrientation = ui::Rotation::Rotation90;
- }
-
- const ui::Rotation orientation = device->getPhysicalOrientation() + logicalOrientation;
-
- switch (orientation) {
- case ui::ROTATION_0:
- return orientationFlag;
-
- case ui::ROTATION_90:
- inverseRotate90 = ui::Transform::ROT_90;
- inverseReflect = ui::Transform::ROT_180;
- break;
-
- case ui::ROTATION_180:
- inverseReflect = ui::Transform::ROT_180;
- break;
-
- case ui::ROTATION_270:
- inverseRotate90 = ui::Transform::ROT_90;
- break;
- }
-
- const uint32_t rotate90 = orientationFlag & ui::Transform::ROT_90;
- uint32_t reflect = orientationFlag & ui::Transform::ROT_180;
-
- // Apply reflection for double rotation.
- if (rotate90 & inverseRotate90) {
- reflect = ~reflect & ui::Transform::ROT_180;
- }
-
- return static_cast<RotationFlags>((rotate90 ^ inverseRotate90) |
- (reflect ^ inverseReflect));
- }
-
- const sp<const DisplayDevice> mDisplay;
- const Rect mSourceCrop;
- const bool mAllowSecureLayers;
- const ui::Transform mTransform = ui::Transform();
-};
-
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 4dfc743..98209bb 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.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 "DisplayIdentification"
@@ -38,7 +34,6 @@
constexpr size_t kEdidBlockSize = 128;
constexpr size_t kEdidHeaderLength = 5;
-constexpr uint16_t kFallbackEdidManufacturerId = 0;
constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu;
std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) {
@@ -71,12 +66,8 @@
DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
DeviceProductInfo info;
- std::copy(edid.displayName.begin(), edid.displayName.end(), info.name.begin());
- info.name[edid.displayName.size()] = '\0';
-
- const auto productId = std::to_string(edid.productId);
- std::copy(productId.begin(), productId.end(), info.productId.begin());
- info.productId[productId.size()] = '\0';
+ info.name.assign(edid.displayName);
+ info.productId = std::to_string(edid.productId);
info.manufacturerPnpId = edid.pnpId;
constexpr uint8_t kModelYearFlag = 0xff;
@@ -99,8 +90,6 @@
if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) {
const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress;
info.relativeAddress = {address.a, address.b, address.c, address.d};
- } else {
- info.relativeAddress = DeviceProductInfo::NO_RELATIVE_ADDRESS;
}
return info;
}
@@ -132,8 +121,8 @@
constexpr uint8_t kVendorSpecificDataBlockTag = 0x3;
if (tag == kVendorSpecificDataBlockTag) {
- const uint32_t ieeeRegistrationId =
- dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16);
+ const uint32_t ieeeRegistrationId = static_cast<uint32_t>(
+ dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16));
constexpr uint32_t kHdmiIeeeRegistrationId = 0xc03;
if (ieeeRegistrationId == kHdmiIeeeRegistrationId) {
@@ -158,14 +147,6 @@
} // namespace
-uint16_t DisplayId::manufacturerId() const {
- return static_cast<uint16_t>(value >> 40);
-}
-
-DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) {
- return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(modelHash) << 8) | port};
-}
-
bool isEdid(const DisplayIdentificationData& data) {
const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
return data.size() >= sizeof(kMagic) &&
@@ -190,7 +171,7 @@
// Plug and play ID encoded as big-endian 16-bit value.
const uint16_t manufacturerId =
- (edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1];
+ static_cast<uint16_t>((edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1]);
const auto pnpId = getPnpId(manufacturerId);
if (!pnpId) {
@@ -203,7 +184,8 @@
ALOGE("Invalid EDID: product ID is truncated.");
return {};
}
- const uint16_t productId = edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8);
+ const uint16_t productId =
+ static_cast<uint16_t>(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8));
constexpr size_t kManufactureWeekOffset = 16;
if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) {
@@ -238,7 +220,6 @@
constexpr size_t kDescriptorCount = 4;
constexpr size_t kDescriptorLength = 18;
- static_assert(kDescriptorLength - kEdidHeaderLength < DeviceProductInfo::TEXT_BUFFER_SIZE);
for (size_t i = 0; i < kDescriptorCount; i++) {
if (view.size() < kDescriptorLength) {
@@ -330,8 +311,8 @@
return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
}
-std::optional<PnpId> getPnpId(DisplayId displayId) {
- return getPnpId(displayId.manufacturerId());
+std::optional<PnpId> getPnpId(PhysicalDisplayId displayId) {
+ return getPnpId(displayId.getManufacturerId());
}
std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
@@ -346,21 +327,15 @@
return {};
}
- const auto displayId = DisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
+ const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
return DisplayIdentificationInfo{.id = displayId,
.name = std::string(edid->displayName),
.deviceProductInfo = buildDeviceProductInfo(*edid)};
}
-DisplayId getFallbackDisplayId(uint8_t port) {
- return DisplayId::fromEdid(port, kFallbackEdidManufacturerId, 0);
-}
-
-DisplayId getVirtualDisplayId(uint32_t id) {
- return DisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
+PhysicalDisplayId getVirtualDisplayId(uint32_t id) {
+ return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
}
} // namespace android
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index 4819d1d..fbea4e5 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,38 +24,18 @@
#include <vector>
#include <ui/DeviceProductInfo.h>
-#include <ui/PhysicalDisplayId.h>
+#include <ui/DisplayId.h>
#define LEGACY_DISPLAY_TYPE_PRIMARY 0
#define LEGACY_DISPLAY_TYPE_EXTERNAL 1
namespace android {
-struct DisplayId {
- using Type = PhysicalDisplayId;
- Type value;
-
- uint16_t manufacturerId() const;
-
- static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash);
-};
-
-inline bool operator==(DisplayId lhs, DisplayId rhs) {
- return lhs.value == rhs.value;
-}
-
-inline bool operator!=(DisplayId lhs, DisplayId rhs) {
- return !(lhs == rhs);
-}
-
-inline std::string to_string(DisplayId displayId) {
- return std::to_string(displayId.value);
-}
using DisplayIdentificationData = std::vector<uint8_t>;
struct DisplayIdentificationInfo {
- DisplayId id;
+ PhysicalDisplayId id;
std::string name;
std::optional<DeviceProductInfo> deviceProductInfo;
};
@@ -94,23 +74,12 @@
bool isEdid(const DisplayIdentificationData&);
std::optional<Edid> parseEdid(const DisplayIdentificationData&);
std::optional<PnpId> getPnpId(uint16_t manufacturerId);
-std::optional<PnpId> getPnpId(DisplayId);
+std::optional<PnpId> getPnpId(PhysicalDisplayId);
std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
uint8_t port, const DisplayIdentificationData&);
-DisplayId getFallbackDisplayId(uint8_t port);
-DisplayId getVirtualDisplayId(uint32_t id);
+PhysicalDisplayId getVirtualDisplayId(uint32_t id);
} // namespace android
-namespace std {
-
-template <>
-struct hash<android::DisplayId> {
- size_t operator()(android::DisplayId displayId) const {
- return hash<android::DisplayId::Type>()(displayId.value);
- }
-};
-
-} // namespace std
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 6819ff4..12f26f6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_SF_HWC2_H
-#define ANDROID_SF_HWC2_H
+#pragma once
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
@@ -36,15 +35,16 @@
#include "Hal.h"
namespace android {
- struct DisplayedFrameStats;
- class Fence;
- class FloatRect;
- class GraphicBuffer;
- namespace Hwc2 {
- class Composer;
- }
- class TestableSurfaceFlinger;
+class Fence;
+class FloatRect;
+class GraphicBuffer;
+class TestableSurfaceFlinger;
+struct DisplayedFrameStats;
+
+namespace Hwc2 {
+class Composer;
+} // namespace Hwc2
namespace HWC2 {
@@ -61,19 +61,17 @@
// All calls receive a sequenceId, which will be the value that was supplied to
// HWC2::Device::registerCallback(). It's used to help differentiate callbacks
// from different hardware composer instances.
-class ComposerCallback {
- public:
- virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId display,
- hal::Connection connection) = 0;
- virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId display) = 0;
- virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp,
- std::optional<hal::VsyncPeriodNanos> vsyncPeriod) = 0;
- virtual void onVsyncPeriodTimingChangedReceived(
- int32_t sequenceId, hal::HWDisplayId display,
- const hal::VsyncPeriodChangeTimeline& updatedTimeline) = 0;
- virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) = 0;
+struct ComposerCallback {
+ virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId, hal::Connection) = 0;
+ virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId) = 0;
+ virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp,
+ std::optional<hal::VsyncPeriodNanos>) = 0;
+ virtual void onVsyncPeriodTimingChangedReceived(int32_t sequenceId, hal::HWDisplayId,
+ const hal::VsyncPeriodChangeTimeline&) = 0;
+ virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId) = 0;
- virtual ~ComposerCallback() = default;
+protected:
+ ~ComposerCallback() = default;
};
// Convenience C++ class to access per display functions directly.
@@ -454,5 +452,3 @@
} // namespace impl
} // namespace HWC2
} // namespace android
-
-#endif // ANDROID_SF_HWC2_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7a2f0f3..05ef599 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -284,7 +284,8 @@
return displayId;
}
-void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) {
+void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId,
+ PhysicalDisplayId displayId) {
if (!mInternalHwcDisplayId) {
mInternalHwcDisplayId = hwcDisplayId;
} else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
@@ -865,7 +866,8 @@
result.append(mComposer->dumpDebugInfo());
}
-std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const {
+std::optional<PhysicalDisplayId> HWComposer::toPhysicalDisplayId(
+ hal::HWDisplayId hwcDisplayId) const {
if (const auto it = mPhysicalDisplayIdMap.find(hwcDisplayId);
it != mPhysicalDisplayIdMap.end()) {
return it->second;
@@ -873,7 +875,8 @@
return {};
}
-std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(DisplayId displayId) const {
+std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(
+ PhysicalDisplayId displayId) const {
if (const auto it = mDisplayData.find(displayId);
it != mDisplayData.end() && !it->second.isVirtual) {
return it->second.hwcDisplay->getId();
@@ -937,7 +940,7 @@
port = isPrimary ? LEGACY_DISPLAY_TYPE_PRIMARY : LEGACY_DISPLAY_TYPE_EXTERNAL;
}
- return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
+ return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port),
.name = isPrimary ? "Internal display"
: "External display",
.deviceProductInfo = std::nullopt};
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index c355ebd..698e3af 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -64,6 +64,8 @@
const uint32_t id;
};
+// See the comment for SurfaceFlinger::getHwComposer for the thread safety rules for accessing
+// this class.
class HWComposer {
public:
struct DeviceRequestedChanges {
@@ -82,23 +84,22 @@
virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
- virtual bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
+ virtual bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
DisplayIdentificationData* outData) const = 0;
- virtual bool hasCapability(hal::Capability capability) const = 0;
- virtual bool hasDisplayCapability(DisplayId displayId,
- hal::DisplayCapability capability) const = 0;
+ virtual bool hasCapability(hal::Capability) const = 0;
+ virtual bool hasDisplayCapability(DisplayId, hal::DisplayCapability) const = 0;
// Attempts to allocate a virtual display and returns its ID if created on the HWC device.
virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
- ui::PixelFormat* format) = 0;
+ ui::PixelFormat*) = 0;
- virtual void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) = 0;
+ virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
// Attempts to create a new layer on this display
- virtual HWC2::Layer* createLayer(DisplayId displayId) = 0;
+ virtual HWC2::Layer* createLayer(DisplayId) = 0;
// Destroy a previously created layer
- virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0;
+ virtual void destroyLayer(DisplayId, HWC2::Layer*) = 0;
// Gets any required composition change requests from the HWC device.
//
@@ -111,104 +112,97 @@
DisplayId, bool frameUsesClientComposition,
std::optional<DeviceRequestedChanges>* outChanges) = 0;
- virtual status_t setClientTarget(DisplayId displayId, uint32_t slot,
- const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
- ui::Dataspace dataspace) = 0;
+ virtual status_t setClientTarget(DisplayId, uint32_t slot, const sp<Fence>& acquireFence,
+ const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
// Present layers to the display and read releaseFences.
- virtual status_t presentAndGetReleaseFences(DisplayId displayId) = 0;
+ virtual status_t presentAndGetReleaseFences(DisplayId) = 0;
// set power mode
- virtual status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) = 0;
+ virtual status_t setPowerMode(DisplayId, hal::PowerMode) = 0;
// Sets a color transform to be applied to the result of composition
- virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0;
+ virtual status_t setColorTransform(DisplayId, const mat4& transform) = 0;
// reset state when an external, non-virtual display is disconnected
- virtual void disconnectDisplay(DisplayId displayId) = 0;
+ virtual void disconnectDisplay(DisplayId) = 0;
// get the present fence received from the last call to present.
- virtual sp<Fence> getPresentFence(DisplayId displayId) const = 0;
+ virtual sp<Fence> getPresentFence(DisplayId) const = 0;
// Get last release fence for the given layer
- virtual sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const = 0;
+ virtual sp<Fence> getLayerReleaseFence(DisplayId, HWC2::Layer*) const = 0;
// Set the output buffer and acquire fence for a virtual display.
// Returns INVALID_OPERATION if displayId is not a virtual display.
- virtual status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+ virtual status_t setOutputBuffer(DisplayId, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& buffer) = 0;
// After SurfaceFlinger has retrieved the release fences for all the frames,
// it can call this to clear the shared pointers in the release fence map
- virtual void clearReleaseFences(DisplayId displayId) = 0;
+ virtual void clearReleaseFences(DisplayId) = 0;
// Fetches the HDR capabilities of the given display
- virtual status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) = 0;
+ virtual status_t getHdrCapabilities(DisplayId, HdrCapabilities* outCapabilities) = 0;
- virtual int32_t getSupportedPerFrameMetadata(DisplayId displayId) const = 0;
+ virtual int32_t getSupportedPerFrameMetadata(DisplayId) const = 0;
// Returns the available RenderIntent of the given display.
- virtual std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId,
- ui::ColorMode colorMode) const = 0;
+ virtual std::vector<ui::RenderIntent> getRenderIntents(DisplayId, ui::ColorMode) const = 0;
- virtual mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) = 0;
+ virtual mat4 getDataspaceSaturationMatrix(DisplayId, ui::Dataspace) = 0;
// Returns the attributes of the color sampling engine.
- virtual status_t getDisplayedContentSamplingAttributes(DisplayId displayId,
- ui::PixelFormat* outFormat,
+ virtual status_t getDisplayedContentSamplingAttributes(DisplayId, ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
uint8_t* outComponentMask) = 0;
- virtual status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+ virtual status_t setDisplayContentSamplingEnabled(DisplayId, bool enabled,
uint8_t componentMask,
uint64_t maxFrames) = 0;
- virtual status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
- uint64_t timestamp,
+ virtual status_t getDisplayedContentSample(DisplayId, uint64_t maxFrames, uint64_t timestamp,
DisplayedFrameStats* outStats) = 0;
// Sets the brightness of a display.
- virtual std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) = 0;
+ virtual std::future<status_t> setDisplayBrightness(DisplayId, float brightness) = 0;
// Events handling ---------------------------------------------------------
// Returns stable display ID (and display name on connection of new or previously disconnected
// display), or std::nullopt if hotplug event was ignored.
// This function is called from SurfaceFlinger.
- virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId hwcDisplayId,
- hal::Connection connection) = 0;
+ virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId,
+ hal::Connection) = 0;
- virtual bool onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) = 0;
- virtual void setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) = 0;
+ virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
+ virtual void setVsyncEnabled(DisplayId, hal::Vsync enabled) = 0;
- virtual nsecs_t getRefreshTimestamp(DisplayId displayId) const = 0;
- virtual bool isConnected(DisplayId displayId) const = 0;
+ virtual nsecs_t getRefreshTimestamp(DisplayId) const = 0;
+ virtual bool isConnected(DisplayId) const = 0;
// Non-const because it can update configMap inside of mDisplayData
virtual std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
- DisplayId displayId) const = 0;
+ DisplayId) const = 0;
- virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
- DisplayId displayId) const = 0;
- virtual int getActiveConfigIndex(DisplayId displayId) const = 0;
+ virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(DisplayId) const = 0;
+ virtual int getActiveConfigIndex(DisplayId) const = 0;
- virtual std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const = 0;
+ virtual std::vector<ui::ColorMode> getColorModes(DisplayId) const = 0;
- virtual status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
- ui::RenderIntent renderIntent) = 0;
+ virtual status_t setActiveColorMode(DisplayId, ui::ColorMode mode, ui::RenderIntent) = 0;
virtual bool isUsingVrComposer() const = 0;
// Composer 2.4
virtual DisplayConnectionType getDisplayConnectionType(DisplayId) const = 0;
- virtual bool isVsyncPeriodSwitchSupported(DisplayId displayId) const = 0;
- virtual nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const = 0;
+ virtual bool isVsyncPeriodSwitchSupported(DisplayId) const = 0;
+ virtual nsecs_t getDisplayVsyncPeriod(DisplayId) const = 0;
virtual status_t setActiveConfigWithConstraints(
- DisplayId displayId, size_t configId,
- const hal::VsyncPeriodChangeConstraints& constraints,
+ DisplayId, size_t configId, const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
- virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0;
+ virtual status_t setAutoLowLatencyMode(DisplayId, bool on) = 0;
virtual status_t getSupportedContentTypes(
- DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
- virtual status_t setContentType(DisplayId displayId, hal::ContentType contentType) = 0;
+ DisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
+ virtual status_t setContentType(DisplayId, hal::ContentType) = 0;
virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
const = 0;
@@ -221,8 +215,8 @@
virtual std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const = 0;
virtual std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const = 0;
- virtual std::optional<DisplayId> toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const = 0;
- virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(DisplayId displayId) const = 0;
+ virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
+ virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
};
namespace impl {
@@ -236,118 +230,112 @@
void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
- bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
+ bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
DisplayIdentificationData* outData) const override;
- bool hasCapability(hal::Capability capability) const override;
- bool hasDisplayCapability(DisplayId displayId,
- hal::DisplayCapability capability) const override;
+ bool hasCapability(hal::Capability) const override;
+ bool hasDisplayCapability(DisplayId, hal::DisplayCapability) const override;
// Attempts to allocate a virtual display and returns its ID if created on the HWC device.
std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
- ui::PixelFormat* format) override;
+ ui::PixelFormat*) override;
// Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
- void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) override;
+ void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
// Attempts to create a new layer on this display
- HWC2::Layer* createLayer(DisplayId displayId) override;
+ HWC2::Layer* createLayer(DisplayId) override;
// Destroy a previously created layer
- void destroyLayer(DisplayId displayId, HWC2::Layer* layer) override;
+ void destroyLayer(DisplayId, HWC2::Layer*) override;
status_t getDeviceCompositionChanges(
DisplayId, bool frameUsesClientComposition,
std::optional<DeviceRequestedChanges>* outChanges) override;
- status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence,
- const sp<GraphicBuffer>& target, ui::Dataspace dataspace) override;
+ status_t setClientTarget(DisplayId, uint32_t slot, const sp<Fence>& acquireFence,
+ const sp<GraphicBuffer>& target, ui::Dataspace) override;
// Present layers to the display and read releaseFences.
- status_t presentAndGetReleaseFences(DisplayId displayId) override;
+ status_t presentAndGetReleaseFences(DisplayId) override;
// set power mode
- status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) override;
+ status_t setPowerMode(DisplayId, hal::PowerMode mode) override;
// Sets a color transform to be applied to the result of composition
- status_t setColorTransform(DisplayId displayId, const mat4& transform) override;
+ status_t setColorTransform(DisplayId, const mat4& transform) override;
// reset state when an external, non-virtual display is disconnected
- void disconnectDisplay(DisplayId displayId) override;
+ void disconnectDisplay(DisplayId) override;
// get the present fence received from the last call to present.
- sp<Fence> getPresentFence(DisplayId displayId) const override;
+ sp<Fence> getPresentFence(DisplayId) const override;
// Get last release fence for the given layer
- sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const override;
+ sp<Fence> getLayerReleaseFence(DisplayId, HWC2::Layer*) const override;
// Set the output buffer and acquire fence for a virtual display.
// Returns INVALID_OPERATION if displayId is not a virtual display.
- status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+ status_t setOutputBuffer(DisplayId, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& buffer) override;
// After SurfaceFlinger has retrieved the release fences for all the frames,
// it can call this to clear the shared pointers in the release fence map
- void clearReleaseFences(DisplayId displayId) override;
+ void clearReleaseFences(DisplayId) override;
// Fetches the HDR capabilities of the given display
- status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) override;
+ status_t getHdrCapabilities(DisplayId, HdrCapabilities* outCapabilities) override;
- int32_t getSupportedPerFrameMetadata(DisplayId displayId) const override;
+ int32_t getSupportedPerFrameMetadata(DisplayId) const override;
// Returns the available RenderIntent of the given display.
- std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId,
- ui::ColorMode colorMode) const override;
+ std::vector<ui::RenderIntent> getRenderIntents(DisplayId, ui::ColorMode) const override;
- mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) override;
+ mat4 getDataspaceSaturationMatrix(DisplayId, ui::Dataspace) override;
// Returns the attributes of the color sampling engine.
- status_t getDisplayedContentSamplingAttributes(DisplayId displayId, ui::PixelFormat* outFormat,
+ status_t getDisplayedContentSamplingAttributes(DisplayId, ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
uint8_t* outComponentMask) override;
- status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
- uint8_t componentMask, uint64_t maxFrames) override;
- status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp,
+ status_t setDisplayContentSamplingEnabled(DisplayId, bool enabled, uint8_t componentMask,
+ uint64_t maxFrames) override;
+ status_t getDisplayedContentSample(DisplayId, uint64_t maxFrames, uint64_t timestamp,
DisplayedFrameStats* outStats) override;
- std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) override;
+ std::future<status_t> setDisplayBrightness(DisplayId, float brightness) override;
// Events handling ---------------------------------------------------------
// Returns stable display ID (and display name on connection of new or previously disconnected
// display), or std::nullopt if hotplug event was ignored.
- std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId hwcDisplayId,
- hal::Connection connection) override;
+ std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, hal::Connection) override;
- bool onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) override;
- void setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) override;
+ bool onVsync(hal::HWDisplayId, int64_t timestamp) override;
+ void setVsyncEnabled(DisplayId, hal::Vsync enabled) override;
- nsecs_t getRefreshTimestamp(DisplayId displayId) const override;
- bool isConnected(DisplayId displayId) const override;
+ nsecs_t getRefreshTimestamp(DisplayId) const override;
+ bool isConnected(DisplayId) const override;
// Non-const because it can update configMap inside of mDisplayData
- std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
- DisplayId displayId) const override;
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(DisplayId) const override;
- std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
- DisplayId displayId) const override;
- int getActiveConfigIndex(DisplayId displayId) const override;
+ std::shared_ptr<const HWC2::Display::Config> getActiveConfig(DisplayId) const override;
+ int getActiveConfigIndex(DisplayId) const override;
- std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const override;
+ std::vector<ui::ColorMode> getColorModes(DisplayId) const override;
- status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
- ui::RenderIntent renderIntent) override;
+ status_t setActiveColorMode(DisplayId, ui::ColorMode, ui::RenderIntent) override;
bool isUsingVrComposer() const override;
// Composer 2.4
DisplayConnectionType getDisplayConnectionType(DisplayId) const override;
- bool isVsyncPeriodSwitchSupported(DisplayId displayId) const override;
- nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const override;
- status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId,
- const hal::VsyncPeriodChangeConstraints& constraints,
+ bool isVsyncPeriodSwitchSupported(DisplayId) const override;
+ nsecs_t getDisplayVsyncPeriod(DisplayId) const override;
+ status_t setActiveConfigWithConstraints(DisplayId, size_t configId,
+ const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline* outTimeline) override;
- status_t setAutoLowLatencyMode(DisplayId displayId, bool) override;
- status_t getSupportedContentTypes(DisplayId displayId, std::vector<hal::ContentType>*) override;
- status_t setContentType(DisplayId displayId, hal::ContentType) override;
+ status_t setAutoLowLatencyMode(DisplayId, bool) override;
+ status_t getSupportedContentTypes(DisplayId, std::vector<hal::ContentType>*) override;
+ status_t setContentType(DisplayId, hal::ContentType) override;
const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
@@ -364,17 +352,16 @@
return mExternalHwcDisplayId;
}
- std::optional<DisplayId> toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const override;
- std::optional<hal::HWDisplayId> fromPhysicalDisplayId(DisplayId displayId) const override;
+ std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const override;
+ std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const override;
private:
// For unit tests
friend TestableSurfaceFlinger;
- std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId hwcDisplayId);
- std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId hwcDisplayId);
- bool shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId,
- bool hasDisplayIdentificationData) const;
+ std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
+ std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
+ bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
void loadCapabilities();
void loadLayerMetadataSupport();
@@ -409,7 +396,7 @@
std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata;
bool mRegisteredCallback = false;
- std::unordered_map<hal::HWDisplayId, DisplayId> mPhysicalDisplayIdMap;
+ std::unordered_map<hal::HWDisplayId, PhysicalDisplayId> mPhysicalDisplayIdMap;
std::optional<hal::HWDisplayId> mInternalHwcDisplayId;
std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
bool mHasMultiDisplaySupport = false;
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
new file mode 100644
index 0000000..9a6b328
--- /dev/null
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 "DisplayRenderArea.h"
+#include "DisplayDevice.h"
+
+namespace android {
+namespace {
+
+RenderArea::RotationFlags applyDeviceOrientation(bool useIdentityTransform,
+ const DisplayDevice& display) {
+ if (!useIdentityTransform) {
+ return RenderArea::RotationFlags::ROT_0;
+ }
+
+ const ui::Rotation orientation = display.getPhysicalOrientation() + display.getOrientation();
+ return ui::Transform::toRotationFlags(orientation);
+}
+
+} // namespace
+
+std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak,
+ const Rect& sourceCrop, ui::Size reqSize,
+ ui::Dataspace reqDataSpace,
+ bool useIdentityTransform,
+ bool allowSecureLayers) {
+ if (auto display = displayWeak.promote()) {
+ // Using new to access a private constructor.
+ return std::unique_ptr<DisplayRenderArea>(
+ new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
+ useIdentityTransform, allowSecureLayers));
+ }
+ return nullptr;
+}
+
+DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
+ ui::Size reqSize, ui::Dataspace reqDataSpace,
+ bool useIdentityTransform, bool allowSecureLayers)
+ : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, display->getLayerStackSpaceRect(),
+ allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)),
+ mDisplay(std::move(display)),
+ mSourceCrop(sourceCrop) {}
+
+const ui::Transform& DisplayRenderArea::getTransform() const {
+ return mTransform;
+}
+
+Rect DisplayRenderArea::getBounds() const {
+ return mDisplay->getBounds();
+}
+
+int DisplayRenderArea::getHeight() const {
+ return mDisplay->getHeight();
+}
+
+int DisplayRenderArea::getWidth() const {
+ return mDisplay->getWidth();
+}
+
+bool DisplayRenderArea::isSecure() const {
+ return mAllowSecureLayers && mDisplay->isSecure();
+}
+
+sp<const DisplayDevice> DisplayRenderArea::getDisplayDevice() const {
+ return mDisplay;
+}
+
+bool DisplayRenderArea::needsFiltering() const {
+ // check if the projection from the logical render area
+ // to the physical render area requires filtering
+ const Rect& sourceCrop = getSourceCrop();
+ int width = sourceCrop.width();
+ int height = sourceCrop.height();
+ if (getRotationFlags() & ui::Transform::ROT_90) {
+ std::swap(width, height);
+ }
+ return width != getReqWidth() || height != getReqHeight();
+}
+
+Rect DisplayRenderArea::getSourceCrop() const {
+ // use the projected display viewport by default.
+ if (mSourceCrop.isEmpty()) {
+ return mDisplay->getLayerStackSpaceRect();
+ }
+
+ // Correct for the orientation when the screen capture request contained
+ // useIdentityTransform. This will cause the rotation flag to be non 0 since
+ // it needs to rotate based on the screen orientation to allow the screenshot
+ // to be taken in the ROT_0 orientation
+ const auto flags = getRotationFlags();
+ int width = mDisplay->getLayerStackSpaceRect().getWidth();
+ int height = mDisplay->getLayerStackSpaceRect().getHeight();
+ ui::Transform rotation;
+ rotation.set(flags, width, height);
+ return rotation.transform(mSourceCrop);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
new file mode 100644
index 0000000..3478fc1
--- /dev/null
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include "RenderArea.h"
+
+namespace android {
+
+class DisplayDevice;
+
+class DisplayRenderArea : public RenderArea {
+public:
+ static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
+ ui::Size reqSize, ui::Dataspace,
+ bool useIdentityTransform,
+ bool allowSecureLayers = true);
+
+ const ui::Transform& getTransform() const override;
+ Rect getBounds() const override;
+ int getHeight() const override;
+ int getWidth() const override;
+ bool isSecure() const override;
+ sp<const DisplayDevice> getDisplayDevice() const override;
+ bool needsFiltering() const override;
+ Rect getSourceCrop() const override;
+
+private:
+ DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
+ ui::Dataspace, bool useIdentityTransform, bool allowSecureLayers = true);
+
+ const sp<const DisplayDevice> mDisplay;
+ const Rect mSourceCrop;
+ const ui::Transform mTransform;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
index b986f38..2dfb9a9 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -25,7 +25,7 @@
#include "FrameTracer.h"
#include <android-base/stringprintf.h>
-#include <perfetto/trace/clock_snapshot.pbzero.h>
+#include <perfetto/common/builtin_clock.pbzero.h>
#include <algorithm>
#include <mutex>
@@ -34,7 +34,6 @@
namespace android {
-using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock;
void FrameTracer::initialize() {
std::call_once(mInitializationFlag, [this]() {
perfetto::TracingInitArgs args;
@@ -136,7 +135,7 @@
uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
FrameEvent::BufferEventType type, nsecs_t duration) {
auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(Clock::MONOTONIC);
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
packet->set_timestamp(timestamp);
auto* event = packet->set_graphics_frame_event()->set_buffer_event();
event->set_buffer_id(static_cast<uint32_t>(bufferID));
diff --git a/services/surfaceflinger/FrameTracer/OWNERS b/services/surfaceflinger/FrameTracer/OWNERS
new file mode 100644
index 0000000..e4f5d45
--- /dev/null
+++ b/services/surfaceflinger/FrameTracer/OWNERS
@@ -0,0 +1 @@
+adsrini@google.com
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 03903f6..138d08c 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -39,6 +39,7 @@
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <math.h>
+#include <private/android_filesystem_config.h>
#include <renderengine/RenderEngine.h>
#include <stdint.h>
#include <stdlib.h>
@@ -67,12 +68,14 @@
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
+#include "input/InputWindow.h"
#define DEBUG_RESIZE 0
namespace android {
using base::StringAppendF;
+using namespace android::flag_operators;
std::atomic<int32_t> Layer::sSequence{1};
@@ -80,7 +83,8 @@
: mFlinger(args.flinger),
mName(args.name),
mClientRef(args.client),
- mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) {
+ mWindowType(static_cast<InputWindowInfo::Type>(
+ args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) {
uint32_t layerFlags = 0;
if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -136,6 +140,14 @@
mCallingPid = args.callingPid;
mCallingUid = args.callingUid;
+
+ if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) {
+ // If the system didn't send an ownerUid, use the callingUid for the ownerUid.
+ mOwnerUid = args.metadata.getInt32(METADATA_OWNER_UID, mCallingUid);
+ } else {
+ // A create layer request from a non system request cannot specify the owner uid
+ mOwnerUid = mCallingUid;
+ }
}
void Layer::onFirstRef() {
@@ -608,7 +620,7 @@
// ---------------------------------------------------------------------------
std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+ compositionengine::LayerFE::ClientCompositionTargetSettings& /* targetSettings */) {
if (!getCompositionState()) {
return {};
}
@@ -618,11 +630,7 @@
compositionengine::LayerFE::LayerSettings layerSettings;
layerSettings.geometry.boundaries = bounds;
- if (targetSettings.useIdentityTransform) {
- layerSettings.geometry.positionTransform = mat4();
- } else {
- layerSettings.geometry.positionTransform = getTransform().asMatrix4();
- }
+ layerSettings.geometry.positionTransform = getTransform().asMatrix4();
if (hasColorTransform()) {
layerSettings.colorTransform = getColorTransform();
@@ -639,9 +647,9 @@
}
std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareShadowClientComposition(
- const LayerFE::LayerSettings& casterLayerSettings, const Rect& displayViewport,
+ const LayerFE::LayerSettings& casterLayerSettings, const Rect& layerStackRect,
ui::Dataspace outputDataspace) {
- renderengine::ShadowSettings shadow = getShadowSettings(displayViewport);
+ renderengine::ShadowSettings shadow = getShadowSettings(layerStackRect);
if (shadow.length <= 0.f) {
return {};
}
@@ -1666,8 +1674,8 @@
}
void Layer::dumpCallingUidPid(std::string& result) const {
- StringAppendF(&result, "Layer %s (%s) pid:%d uid:%d\n", getName().c_str(), getType(),
- mCallingPid, mCallingUid);
+ StringAppendF(&result, "Layer %s (%s) callingPid:%d callingUid:%d ownerUid:%d\n",
+ getName().c_str(), getType(), mCallingPid, mCallingUid, mOwnerUid);
}
void Layer::onDisconnect() {
@@ -1820,7 +1828,7 @@
onRemovedFromCurrentState();
}
- if (callSetTransactionFlags || attachChildren()) {
+ if (attachChildren() || callSetTransactionFlags) {
setTransactionFlags(eTransactionNeeded);
}
return true;
@@ -1848,9 +1856,9 @@
if (client != nullptr && parentClient != client) {
if (child->mLayerDetached) {
child->mLayerDetached = false;
+ child->attachChildren();
changed = true;
}
- changed |= child->attachChildren();
}
}
@@ -2150,12 +2158,12 @@
: RoundedCornerState();
}
-renderengine::ShadowSettings Layer::getShadowSettings(const Rect& viewport) const {
+renderengine::ShadowSettings Layer::getShadowSettings(const Rect& layerStackRect) const {
renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
// Shift the spot light x-position to the middle of the display and then
// offset it by casting layer's screen pos.
- state.lightPos.x = (viewport.width() / 2.f) - mScreenBounds.left;
+ state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left;
state.lightPos.y -= mScreenBounds.top;
state.length = mEffectiveShadowRadius;
@@ -2195,7 +2203,7 @@
}
LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags,
- const DisplayDevice* display) const {
+ const DisplayDevice* display) {
LayerProto* layerProto = layersProto.add_layers();
writeToProtoDrawingState(layerProto, traceFlags, display);
writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
@@ -2216,7 +2224,7 @@
}
void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
- const DisplayDevice* display) const {
+ const DisplayDevice* display) {
ui::Transform transform = getTransform();
if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
@@ -2245,6 +2253,7 @@
layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
layerInfo->set_corner_radius(getRoundedCornerState().radius);
+ layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
[&]() { return layerInfo->mutable_position(); });
@@ -2272,7 +2281,7 @@
}
void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet,
- uint32_t traceFlags) const {
+ uint32_t traceFlags) {
const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
const State& state = useDrawing ? mDrawingState : mCurrentState;
@@ -2339,10 +2348,19 @@
}
layerInfo->set_is_relative_of(state.isRelativeOf);
+
+ layerInfo->set_owner_uid(mOwnerUid);
}
if (traceFlags & SurfaceTracing::TRACE_INPUT) {
- LayerProtoHelper::writeToProto(state.inputInfo, state.touchableRegionCrop,
+ InputWindowInfo info;
+ if (useDrawing) {
+ info = fillInputInfo();
+ } else {
+ info = state.inputInfo;
+ }
+
+ LayerProtoHelper::writeToProto(info, state.touchableRegionCrop,
[&]() { return layerInfo->mutable_input_window_info(); });
}
@@ -2363,9 +2381,8 @@
mDrawingState.inputInfo.name = getName();
mDrawingState.inputInfo.ownerUid = mCallingUid;
mDrawingState.inputInfo.ownerPid = mCallingPid;
- mDrawingState.inputInfo.inputFeatures =
- InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
- mDrawingState.inputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
+ mDrawingState.inputInfo.inputFeatures = InputWindowInfo::Feature::NO_INPUT_CHANNEL;
+ mDrawingState.inputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
mDrawingState.inputInfo.displayId = getLayerStack();
}
@@ -2377,17 +2394,8 @@
}
ui::Transform t = getTransform();
- const float xScale = t.sx();
- const float yScale = t.sy();
int32_t xSurfaceInset = info.surfaceInset;
int32_t ySurfaceInset = info.surfaceInset;
- if (xScale != 1.0f || yScale != 1.0f) {
- info.windowXScale *= (xScale != 0.0f) ? 1.0f / xScale : 0.0f;
- info.windowYScale *= (yScale != 0.0f) ? 1.0f / yScale : 0.0f;
- info.touchableRegion.scaleSelf(xScale, yScale);
- xSurfaceInset = std::round(xSurfaceInset * xScale);
- ySurfaceInset = std::round(ySurfaceInset * yScale);
- }
// Transform layer size to screen space and inset it by surface insets.
// If this is a portal window, set the touchableRegion to the layerBounds.
@@ -2397,6 +2405,15 @@
if (!layerBounds.isValid()) {
layerBounds = getCroppedBufferSize(getDrawingState());
}
+
+ const float xScale = t.getScaleX();
+ const float yScale = t.getScaleY();
+ if (xScale != 1.0f || yScale != 1.0f) {
+ info.touchableRegion.scaleSelf(xScale, yScale);
+ xSurfaceInset = std::round(xSurfaceInset * xScale);
+ ySurfaceInset = std::round(ySurfaceInset * yScale);
+ }
+
layerBounds = t.transform(layerBounds);
// clamp inset to layer bounds
@@ -2411,6 +2428,10 @@
info.frameRight = layerBounds.right;
info.frameBottom = layerBounds.bottom;
+ ui::Transform inputTransform(t);
+ inputTransform.set(layerBounds.left, layerBounds.top);
+ info.transform = inputTransform.inverse();
+
// Position the touchable region relative to frame screen location and restrict it to frame
// bounds.
info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
@@ -2567,7 +2588,7 @@
}
// Cloned layers shouldn't handle watch outside since their z order is not determined by
// WM or the client.
- mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH;
+ mDrawingState.inputInfo.flags &= ~InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH;
}
void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2c90c92..521659d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -286,7 +286,7 @@
void onFirstRef() override;
- int getWindowType() const { return mWindowType; }
+ InputWindowInfo::Type getWindowType() const { return mWindowType; }
void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
@@ -366,7 +366,7 @@
virtual bool setMetadata(const LayerMetadata& data);
bool reparentChildren(const sp<IBinder>& newParentHandle);
void reparentChildren(const sp<Layer>& newParent);
- virtual void setChildrenDrawingParent(const sp<Layer>& layer);
+ virtual void setChildrenDrawingParent(const sp<Layer>&);
virtual bool reparent(const sp<IBinder>& newParentHandle);
virtual bool detachChildren();
bool attachChildren();
@@ -436,9 +436,7 @@
// Deprecated, please use compositionengine::Output::belongsInOutput()
// instead.
// TODO(lpique): Move the remaining callers (screencap) to the new function.
- bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
- return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
- }
+ bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; }
FloatRect getBounds(const Region& activeTransparentRegion) const;
FloatRect getBounds() const;
@@ -528,19 +526,17 @@
bool isRemovedFromCurrentState() const;
- LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags,
- const DisplayDevice*) const;
+ LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*);
// Write states that are modified by the main thread. This includes drawing
// state as well as buffer data. This should be called in the main or tracing
// thread.
- void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
- const DisplayDevice*) const;
+ void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, const DisplayDevice*);
// Write drawing or current state. If writing current state, the caller should hold the
// external mStateLock. If writing drawing state, this function should be called on the
// main or tracing thread.
- void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+ 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; }
@@ -589,16 +585,16 @@
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>& layer);
+ 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& layerSettings, const Rect& displayViewport,
+ 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& layerSettings, bool blackout) const;
+ void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
public:
/*
@@ -760,7 +756,7 @@
// ignored.
virtual RoundedCornerState getRoundedCornerState() const;
- renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+ renderengine::ShadowSettings getShadowSettings(const Rect& layerStackRect) const;
/**
* Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
@@ -770,17 +766,15 @@
* the scene state, but it's also more efficient than traverseInZOrder and so useful for
* book-keeping.
*/
- void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
- void traverseInReverseZOrder(LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor);
- void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
+ void traverse(LayerVector::StateSet, const LayerVector::Visitor&);
+ void traverseInReverseZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
+ void traverseInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
/**
* Traverse only children in z order, ignoring relative layers that are not children of the
* parent.
*/
- void traverseChildrenInZOrder(LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor);
+ void traverseChildrenInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
size_t getChildrenCount() const;
@@ -792,7 +786,7 @@
// the current state, but should not be called anywhere else!
LayerVector& getCurrentChildren() { return mCurrentChildren; }
- void addChild(const sp<Layer>& layer);
+ void addChild(const sp<Layer>&);
// Returns index if removed, or negative value otherwise
// for symmetry with Vector::remove
ssize_t removeChild(const sp<Layer>& layer);
@@ -806,7 +800,7 @@
// Copy the current list of children to the drawing state. Called by
// SurfaceFlinger to complete a transaction.
void commitChildList();
- int32_t getZ(LayerVector::StateSet stateSet) const;
+ int32_t getZ(LayerVector::StateSet) const;
virtual void pushPendingState();
/**
@@ -832,7 +826,7 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- bool setFrameRate(FrameRate frameRate);
+ bool setFrameRate(FrameRate);
virtual FrameRate getFrameRateForLayerTree() const;
static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
@@ -874,8 +868,8 @@
* crop coordinates, transforming them into layer space.
*/
void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
- void setParent(const sp<Layer>& layer);
- LayerVector makeTraversalList(LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers);
+ void setParent(const sp<Layer>&);
+ LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers);
void addZOrderRelative(const wp<Layer>& relative);
void removeZOrderRelative(const wp<Layer>& relative);
@@ -969,10 +963,12 @@
*/
virtual bool needsInputInfo() const { return hasInputInfo(); }
+ uid_t getOwnerUid() { return mOwnerUid; }
+
protected:
compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
- bool usingRelativeZ(LayerVector::StateSet stateSet) const;
+ bool usingRelativeZ(LayerVector::StateSet) const;
bool mPremultipliedAlpha{true};
const std::string mName;
@@ -1040,7 +1036,7 @@
bool mChildrenChanged{false};
// Window types from WindowManager.LayoutParams
- const int mWindowType;
+ const InputWindowInfo::Type mWindowType;
private:
virtual void setTransformHint(ui::Transform::RotationFlags) {}
@@ -1052,15 +1048,14 @@
* Returns an unsorted vector of all layers that are part of this tree.
* That includes the current layer and all its descendants.
*/
- std::vector<Layer*> getLayersInTree(LayerVector::StateSet stateSet);
+ std::vector<Layer*> getLayersInTree(LayerVector::StateSet);
/**
* Traverses layers that are part of this tree in the correct z order.
* layersInTree must be sorted before calling this method.
*/
void traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree,
- LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor);
- LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
+ LayerVector::StateSet, const LayerVector::Visitor&);
+ LayerVector makeChildrenTraversalList(LayerVector::StateSet,
const std::vector<Layer*>& layersInTree);
void updateTreeHasFrameRateVote();
@@ -1091,6 +1086,10 @@
pid_t mCallingPid;
uid_t mCallingUid;
+ // The owner of the layer. If created from a non system process, it will be the calling uid.
+ // If created from a system process, the value can be passed in.
+ uid_t mOwnerUid;
+
// The current layer is a clone of mClonedFrom. This means that this layer will update it's
// properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
// this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 0fe1421..5d99908 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -131,8 +131,12 @@
}
InputWindowInfoProto* proto = getInputWindowInfoProto();
- proto->set_layout_params_flags(inputInfo.layoutParamsFlags);
- proto->set_layout_params_type(inputInfo.layoutParamsType);
+ proto->set_layout_params_flags(inputInfo.flags.get());
+ using U = std::underlying_type_t<InputWindowInfo::Type>;
+ // TODO(b/129481165): This static assert can be safely removed once conversion warnings
+ // are re-enabled.
+ static_assert(std::is_same_v<U, int32_t>);
+ proto->set_layout_params_type(static_cast<U>(inputInfo.type));
LayerProtoHelper::writeToProto({inputInfo.frameLeft, inputInfo.frameTop, inputInfo.frameRight,
inputInfo.frameBottom},
@@ -147,8 +151,7 @@
proto->set_has_wallpaper(inputInfo.hasWallpaper);
proto->set_global_scale_factor(inputInfo.globalScaleFactor);
- proto->set_window_x_scale(inputInfo.windowXScale);
- proto->set_window_y_scale(inputInfo.windowYScale);
+ LayerProtoHelper::writeToProto(inputInfo.transform, proto->mutable_transform());
proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop);
auto cropLayer = touchableRegionBounds.promote();
if (cropLayer != nullptr) {
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
new file mode 100644
index 0000000..e84508f
--- /dev/null
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include "ContainerLayer.h"
+#include "DisplayDevice.h"
+#include "Layer.h"
+#include "LayerRenderArea.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+namespace {
+
+struct ReparentForDrawing {
+ const sp<Layer>& oldParent;
+
+ ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
+ const Rect& drawingBounds)
+ : oldParent(oldParent) {
+ // Compute and cache the bounds for the new parent layer.
+ newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
+ 0.f /* shadowRadius */);
+ oldParent->setChildrenDrawingParent(newParent);
+ }
+ ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
+};
+
+} // namespace
+
+LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop,
+ ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly,
+ const Rect& layerStackRect, bool allowSecureLayers)
+ : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, layerStackRect, allowSecureLayers),
+ mLayer(std::move(layer)),
+ mCrop(crop),
+ mFlinger(flinger),
+ mChildrenOnly(childrenOnly) {}
+
+const ui::Transform& LayerRenderArea::getTransform() const {
+ return mTransform;
+}
+
+Rect LayerRenderArea::getBounds() const {
+ return mLayer->getBufferSize(mLayer->getDrawingState());
+}
+
+int LayerRenderArea::getHeight() const {
+ return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
+}
+
+int LayerRenderArea::getWidth() const {
+ return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
+}
+
+bool LayerRenderArea::isSecure() const {
+ return mAllowSecureLayers;
+}
+
+bool LayerRenderArea::needsFiltering() const {
+ return mNeedsFiltering;
+}
+
+sp<const DisplayDevice> LayerRenderArea::getDisplayDevice() const {
+ return nullptr;
+}
+
+Rect LayerRenderArea::getSourceCrop() const {
+ if (mCrop.isEmpty()) {
+ return getBounds();
+ } else {
+ return mCrop;
+ }
+}
+
+void LayerRenderArea::render(std::function<void()> drawLayers) {
+ using namespace std::string_literals;
+
+ const Rect sourceCrop = getSourceCrop();
+ // no need to check rotation because there is none
+ mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight();
+
+ if (!mChildrenOnly) {
+ mTransform = mLayer->getTransform().inverse();
+ drawLayers();
+ } else {
+ uint32_t w = static_cast<uint32_t>(getWidth());
+ uint32_t h = static_cast<uint32_t>(getHeight());
+ // In the "childrenOnly" case we reparent the children to a screenshot
+ // layer which has no properties set and which does not draw.
+ sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer(
+ {&mFlinger, nullptr, "Screenshot Parent"s, w, h, 0, LayerMetadata()});
+
+ ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
+ drawLayers();
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
new file mode 100644
index 0000000..6a90694
--- /dev/null
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -0,0 +1,61 @@
+/*
+ * 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 <string>
+
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+#include <utils/StrongPointer.h>
+
+#include "RenderArea.h"
+
+namespace android {
+
+class DisplayDevice;
+class Layer;
+class SurfaceFlinger;
+
+class LayerRenderArea : public RenderArea {
+public:
+ LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
+ ui::Dataspace reqDataSpace, bool childrenOnly, const Rect& layerStackRect,
+ bool allowSecureLayers);
+
+ const ui::Transform& getTransform() const override;
+ Rect getBounds() const override;
+ int getHeight() const override;
+ int getWidth() const override;
+ bool isSecure() const override;
+ bool needsFiltering() const override;
+ sp<const DisplayDevice> getDisplayDevice() const override;
+ Rect getSourceCrop() const override;
+
+ void render(std::function<void()> drawLayers) override;
+
+private:
+ const sp<Layer> mLayer;
+ const Rect mCrop;
+
+ ui::Transform mTransform;
+ bool mNeedsFiltering = false;
+
+ SurfaceFlinger& mFlinger;
+ const bool mChildrenOnly;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index f602412..d8477e7 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -106,41 +106,71 @@
drawSegment(Segment::Buttom, left, color, buffer, pixels);
}
-sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number,
- const half4& color) {
- if (number < 0 || number > 1000) return nullptr;
+std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(
+ int number, const half4& color, bool showSpinner) {
+ if (number < 0 || number > 1000) return {};
const auto hundreds = number / 100;
const auto tens = (number / 10) % 10;
const auto ones = number % 10;
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
- GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
- GRALLOC_USAGE_HW_TEXTURE,
- "RefreshRateOverlayBuffer");
- uint8_t* pixels;
- buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
- // Clear buffer content
- drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
- int left = 0;
- if (hundreds != 0) {
- drawDigit(hundreds, left, color, buffer, pixels);
+ std::vector<sp<GraphicBuffer>> buffers;
+ const auto loopCount = showSpinner ? 6 : 1;
+ for (int i = 0; i < loopCount; i++) {
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "RefreshRateOverlayBuffer");
+ uint8_t* pixels;
+ buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+ // Clear buffer content
+ drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
+ int left = 0;
+ if (hundreds != 0) {
+ drawDigit(hundreds, left, color, buffer, pixels);
+ }
left += DIGIT_WIDTH + DIGIT_SPACE;
- }
- if (tens != 0) {
- drawDigit(tens, left, color, buffer, pixels);
+ if (tens != 0) {
+ drawDigit(tens, left, color, buffer, pixels);
+ }
left += DIGIT_WIDTH + DIGIT_SPACE;
- }
- drawDigit(ones, left, color, buffer, pixels);
- buffer->unlock();
- return buffer;
+ drawDigit(ones, left, color, buffer, pixels);
+ left += DIGIT_WIDTH + DIGIT_SPACE;
+
+ if (showSpinner) {
+ switch (i) {
+ case 0:
+ drawSegment(Segment::Upper, left, color, buffer, pixels);
+ break;
+ case 1:
+ drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+ break;
+ case 2:
+ drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+ break;
+ case 3:
+ drawSegment(Segment::Buttom, left, color, buffer, pixels);
+ break;
+ case 4:
+ drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+ break;
+ case 5:
+ drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+ break;
+ }
+ }
+
+ buffer->unlock();
+ buffers.emplace_back(buffer);
+ }
+ return buffers;
}
-RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
- : mFlinger(flinger), mClient(new Client(&mFlinger)) {
+RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, bool showSpinner)
+ : mFlinger(flinger), mClient(new Client(&mFlinger)), mShowSpinner(showSpinner) {
createLayer();
primeCache();
}
@@ -176,7 +206,7 @@
if (allRefreshRates.size() == 1) {
auto fps = allRefreshRates.begin()->second->getFps();
half4 color = {LOW_FPS_COLOR, ALPHA};
- mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+ mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner));
return;
}
@@ -196,12 +226,12 @@
color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
color.a = ALPHA;
- mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+ mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner));
}
}
void RefreshRateOverlay::setViewport(ui::Size viewport) {
- Rect frame(viewport.width >> 3, viewport.height >> 5);
+ Rect frame((3 * viewport.width) >> 4, viewport.height >> 5);
frame.offsetBy(viewport.width >> 5, viewport.height >> 4);
mLayer->setFrame(frame);
@@ -209,7 +239,19 @@
}
void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
- auto buffer = mBufferCache[refreshRate.getFps()];
+ mCurrentFps = refreshRate.getFps();
+ auto buffer = mBufferCache[*mCurrentFps][mFrame];
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+
+ mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+}
+
+void RefreshRateOverlay::onInvalidate() {
+ if (!mCurrentFps.has_value()) return;
+
+ const auto& buffers = mBufferCache[*mCurrentFps];
+ mFrame = (mFrame + 1) % buffers.size();
+ auto buffer = buffers[mFrame];
mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 35c8020..1a8938f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -38,15 +38,17 @@
class RefreshRateOverlay {
public:
- explicit RefreshRateOverlay(SurfaceFlinger&);
+ RefreshRateOverlay(SurfaceFlinger&, bool showSpinner);
void setViewport(ui::Size);
void changeRefreshRate(const RefreshRate&);
+ void onInvalidate();
private:
class SevenSegmentDrawer {
public:
- static sp<GraphicBuffer> drawNumber(int number, const half4& color);
+ static std::vector<sp<GraphicBuffer>> drawNumber(int number, const half4& color,
+ bool showSpinner);
static uint32_t getHeight() { return BUFFER_HEIGHT; }
static uint32_t getWidth() { return BUFFER_WIDTH; }
@@ -65,7 +67,7 @@
static constexpr uint32_t DIGIT_SPACE = 16;
static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT;
static constexpr uint32_t BUFFER_WIDTH =
- 3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit
+ 4 * DIGIT_WIDTH + 3 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit|Space|Spinner
};
bool createLayer();
@@ -77,11 +79,14 @@
sp<IBinder> mIBinder;
sp<IGraphicBufferProducer> mGbp;
- std::unordered_map<int, sp<GraphicBuffer>> mBufferCache;
-
+ std::unordered_map<int, std::vector<sp<GraphicBuffer>>> mBufferCache;
+ std::optional<int> mCurrentFps;
+ int mFrame = 0;
static constexpr float ALPHA = 0.8f;
const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
+
+ const bool mShowSpinner;
};
} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 27353d8..0157a7f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -35,7 +35,9 @@
#include <string>
#include "DisplayDevice.h"
+#include "DisplayRenderArea.h"
#include "Layer.h"
+#include "Promise.h"
#include "Scheduler/DispSync.h"
#include "SurfaceFlinger.h"
@@ -336,8 +338,20 @@
return;
}
- const auto device = mFlinger.getDefaultDisplayDevice();
- const auto orientation = ui::Transform::toRotationFlags(device->getOrientation());
+ wp<const DisplayDevice> displayWeak;
+
+ ui::LayerStack layerStack;
+ ui::Transform::RotationFlags orientation;
+ ui::Size displaySize;
+
+ {
+ // TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread
+ const sp<const DisplayDevice> display = mFlinger.getDefaultDisplayDevice();
+ displayWeak = display;
+ layerStack = display->getLayerStack();
+ orientation = ui::Transform::toRotationFlags(display->getOrientation());
+ displaySize = display->getSize();
+ }
std::vector<RegionSamplingThread::Descriptor> descriptors;
Region sampleRegion;
@@ -346,20 +360,18 @@
descriptors.emplace_back(descriptor);
}
- const Rect sampledArea = sampleRegion.bounds();
-
auto dx = 0;
auto dy = 0;
switch (orientation) {
case ui::Transform::ROT_90:
- dx = device->getWidth();
+ dx = displaySize.getWidth();
break;
case ui::Transform::ROT_180:
- dx = device->getWidth();
- dy = device->getHeight();
+ dx = displaySize.getWidth();
+ dy = displaySize.getHeight();
break;
case ui::Transform::ROT_270:
- dy = device->getHeight();
+ dy = displaySize.getHeight();
break;
default:
break;
@@ -368,8 +380,14 @@
ui::Transform t(orientation);
auto screencapRegion = t.transform(sampleRegion);
screencapRegion = screencapRegion.translate(dx, dy);
- DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(),
- sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation);
+
+ const Rect sampledBounds = sampleRegion.bounds();
+
+ SurfaceFlinger::RenderAreaFuture renderAreaFuture = promise::defer([=] {
+ return DisplayRenderArea::create(displayWeak, screencapRegion.bounds(),
+ sampledBounds.getSize(), ui::Dataspace::V0_SRGB,
+ orientation);
+ });
std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
@@ -393,9 +411,9 @@
constexpr bool roundOutwards = true;
Rect transformed = transform.transform(bounds, roundOutwards);
- // If this layer doesn't intersect with the larger sampledArea, skip capturing it
+ // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
Rect ignore;
- if (!transformed.intersect(sampledArea, &ignore)) return;
+ if (!transformed.intersect(sampledBounds, &ignore)) return;
// If the layer doesn't intersect a sampling area, skip capturing it
bool intersectsAnyArea = false;
@@ -411,22 +429,22 @@
bounds.top, bounds.right, bounds.bottom);
visitor(layer);
};
- mFlinger.traverseLayersInDisplay(device, filterVisitor);
+ mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
};
sp<GraphicBuffer> buffer = nullptr;
- if (mCachedBuffer && mCachedBuffer->getWidth() == sampledArea.getWidth() &&
- mCachedBuffer->getHeight() == sampledArea.getHeight()) {
+ if (mCachedBuffer && mCachedBuffer->getWidth() == sampledBounds.getWidth() &&
+ mCachedBuffer->getHeight() == sampledBounds.getHeight()) {
buffer = mCachedBuffer;
} else {
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
- buffer = new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(),
+ buffer = new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
}
- bool ignored;
- mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false /* identityTransform */,
- true /* regionSampling */, ignored);
+ ScreenCaptureResults captureResults;
+ mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+ true /* regionSampling */, captureResults);
std::vector<Descriptor> activeDescriptors;
for (const auto& descriptor : descriptors) {
@@ -437,7 +455,7 @@
ALOGV("Sampling %zu descriptors", activeDescriptors.size());
std::vector<float> lumas =
- sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors, orientation);
+ sampleBuffer(buffer, sampledBounds.leftTop(), activeDescriptors, orientation);
if (lumas.size() != activeDescriptors.size()) {
ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(),
activeDescriptors.size());
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 6b0455a..c9f7f46 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -23,15 +23,15 @@
static float getCaptureFillValue(CaptureFill captureFill);
- RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
- ui::Dataspace reqDataSpace, const Rect& displayViewport,
+ RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
+ const Rect& layerStackRect, bool allowSecureLayers = false,
RotationFlags rotation = ui::Transform::ROT_0)
- : mReqWidth(reqWidth),
- mReqHeight(reqHeight),
+ : mAllowSecureLayers(allowSecureLayers),
+ mReqSize(reqSize),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
mRotationFlags(rotation),
- mDisplayViewport(displayViewport) {}
+ mLayerStackSpaceRect(layerStackRect) {}
virtual ~RenderArea() = default;
@@ -70,8 +70,8 @@
RotationFlags getRotationFlags() const { return mRotationFlags; }
// Returns the size of the physical render area.
- int getReqWidth() const { return static_cast<int>(mReqWidth); }
- int getReqHeight() const { return static_cast<int>(mReqHeight); }
+ int getReqWidth() const { return mReqSize.width; }
+ int getReqHeight() const { return mReqSize.height; }
// Returns the composition data space of the render area.
ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
@@ -83,15 +83,17 @@
virtual sp<const DisplayDevice> getDisplayDevice() const = 0;
// Returns the source display viewport.
- const Rect& getDisplayViewport() const { return mDisplayViewport; }
+ const Rect& getLayerStackSpaceRect() const { return mLayerStackSpaceRect; }
+
+protected:
+ const bool mAllowSecureLayers;
private:
- const uint32_t mReqWidth;
- const uint32_t mReqHeight;
+ const ui::Size mReqSize;
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const RotationFlags mRotationFlags;
- const Rect mDisplayViewport;
+ const Rect mLayerStackSpaceRect;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp
deleted file mode 100644
index 7f9db9c..0000000
--- a/services/surfaceflinger/Scheduler/EventControlThread.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 <pthread.h>
-#include <sched.h>
-#include <sys/resource.h>
-
-#include <cutils/sched_policy.h>
-#include <log/log.h>
-#include <system/thread_defs.h>
-
-#include "EventControlThread.h"
-
-namespace android {
-
-EventControlThread::~EventControlThread() = default;
-
-namespace impl {
-
-EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
- : mSetVSyncEnabled(std::move(function)) {
- pthread_setname_np(mThread.native_handle(), "EventControlThread");
-
- pid_t tid = pthread_gettid_np(mThread.native_handle());
- setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY);
- set_sched_policy(tid, SP_FOREGROUND);
-}
-
-EventControlThread::~EventControlThread() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mKeepRunning = false;
- mCondition.notify_all();
- }
- mThread.join();
-}
-
-void EventControlThread::setVsyncEnabled(bool enabled) {
- std::lock_guard<std::mutex> lock(mMutex);
- mVsyncEnabled = enabled;
- mCondition.notify_all();
-}
-
-// Unfortunately std::unique_lock gives warnings with -Wthread-safety
-void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
- auto keepRunning = true;
- auto currentVsyncEnabled = false;
-
- while (keepRunning) {
- mSetVSyncEnabled(currentVsyncEnabled);
-
- std::unique_lock<std::mutex> lock(mMutex);
- mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS {
- return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning;
- });
- currentVsyncEnabled = mVsyncEnabled;
- keepRunning = mKeepRunning;
- }
-}
-
-} // namespace impl
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/EventControlThread.h b/services/surfaceflinger/Scheduler/EventControlThread.h
deleted file mode 100644
index cafae53..0000000
--- a/services/surfaceflinger/Scheduler/EventControlThread.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <condition_variable>
-#include <cstddef>
-#include <functional>
-#include <mutex>
-#include <thread>
-
-#include <android-base/thread_annotations.h>
-
-namespace android {
-
-class EventControlThread {
-public:
- virtual ~EventControlThread();
-
- virtual void setVsyncEnabled(bool enabled) = 0;
-};
-
-namespace impl {
-
-class EventControlThread final : public android::EventControlThread {
-public:
- using SetVSyncEnabledFunction = std::function<void(bool)>;
-
- explicit EventControlThread(SetVSyncEnabledFunction function);
- ~EventControlThread();
-
- // EventControlThread implementation
- void setVsyncEnabled(bool enabled) override;
-
-private:
- void threadMain();
-
- std::mutex mMutex;
- std::condition_variable mCondition;
-
- const SetVSyncEnabledFunction mSetVSyncEnabled;
- bool mVsyncEnabled GUARDED_BY(mMutex) = false;
- bool mKeepRunning GUARDED_BY(mMutex) = true;
-
- // Must be last so that everything is initialized before the thread starts.
- std::thread mThread{&EventControlThread::threadMain, this};
-};
-
-} // namespace impl
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 845bf50..846190c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -74,18 +74,16 @@
std::string toString(const DisplayEventReceiver::Event& event) {
switch (event.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
- return StringPrintf("Hotplug{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", %s}",
- event.header.displayId,
+ return StringPrintf("Hotplug{displayId=%s, %s}",
+ to_string(event.header.displayId).c_str(),
event.hotplug.connected ? "connected" : "disconnected");
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
- return StringPrintf("VSync{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
- ", count=%u, expectedVSyncTimestamp=%" PRId64 "}",
- event.header.displayId, event.vsync.count,
+ return StringPrintf("VSync{displayId=%s, count=%u, expectedVSyncTimestamp=%" PRId64 "}",
+ to_string(event.header.displayId).c_str(), event.vsync.count,
event.vsync.expectedVSyncTimestamp);
case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
- return StringPrintf("ConfigChanged{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
- ", configId=%u}",
- event.header.displayId, event.config.configId);
+ return StringPrintf("ConfigChanged{displayId=%s, configId=%u}",
+ to_string(event.header.displayId).c_str(), event.config.configId);
default:
return "Event{}";
}
@@ -473,8 +471,8 @@
StringAppendF(&result, "%s: state=%s VSyncState=", mThreadName, toCString(mState));
if (mVSyncState) {
- StringAppendF(&result, "{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%u%s}\n",
- mVSyncState->displayId, mVSyncState->count,
+ StringAppendF(&result, "{displayId=%s, count=%u%s}\n",
+ to_string(mVSyncState->displayId).c_str(), mVSyncState->count,
mVSyncState->synthetic ? ", synthetic" : "");
} else {
StringAppendF(&result, "none\n");
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index ecf2597..36433c2 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -20,6 +20,7 @@
#include "LayerHistory.h"
+#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
@@ -183,4 +184,11 @@
mActiveLayersEnd = 0;
}
+
+std::string LayerHistory::dump() const {
+ std::lock_guard lock(mLock);
+ return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(),
+ mActiveLayersEnd);
+}
+
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 228b8a0..128699b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -22,6 +22,7 @@
#include <memory>
#include <mutex>
+#include <string>
#include <utility>
#include <vector>
@@ -71,6 +72,7 @@
virtual Summary summarize(nsecs_t now) = 0;
virtual void clear() = 0;
+ virtual std::string dump() const = 0;
};
namespace impl {
@@ -97,6 +99,7 @@
android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
void clear() override;
+ std::string dump() const override;
private:
friend class android::scheduler::LayerHistoryTest;
@@ -155,6 +158,7 @@
android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
void clear() override;
+ std::string dump() const override;
private:
friend android::scheduler::LayerHistoryTestV2;
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index aa04bd7..37e67e1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -20,6 +20,7 @@
#include "LayerHistory.h"
+#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
@@ -31,9 +32,8 @@
#include <utility>
#include "../Layer.h"
-#include "SchedulerUtils.h"
-
#include "LayerInfoV2.h"
+#include "SchedulerUtils.h"
namespace android::scheduler::impl {
@@ -214,4 +214,10 @@
}
}
+std::string LayerHistoryV2::dump() const {
+ std::lock_guard lock(mLock);
+ return base::StringPrintf("LayerHistoryV2{size=%zu, active=%zu}", mLayerInfos.size(),
+ mActiveLayersEnd);
+}
+
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
deleted file mode 100644
index fe2e406..0000000
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "PhaseOffsets.h"
-
-#include <cutils/properties.h>
-
-#include <optional>
-
-#include "SurfaceFlingerProperties.h"
-
-namespace {
-
-std::optional<nsecs_t> getProperty(const char* name) {
- char value[PROPERTY_VALUE_MAX];
- property_get(name, value, "-1");
- if (const int i = atoi(value); i != -1) return i;
- return std::nullopt;
-}
-
-bool fpsEqualsWithMargin(float fpsA, float fpsB) {
- static constexpr float MARGIN = 0.01f;
- return std::abs(fpsA - fpsB) <= MARGIN;
-}
-
-std::vector<float> getRefreshRatesFromConfigs(
- const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
- const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
- std::vector<float> refreshRates;
- refreshRates.reserve(allRefreshRates.size());
-
- for (const auto& [ignored, refreshRate] : allRefreshRates) {
- refreshRates.emplace_back(refreshRate->getFps());
- }
-
- return refreshRates;
-}
-
-} // namespace
-
-namespace android::scheduler {
-
-PhaseConfiguration::~PhaseConfiguration() = default;
-
-namespace impl {
-
-PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().getFps(),
- sysprop::vsync_event_phase_offset_ns(1000000),
- sysprop::vsync_sf_event_phase_offset_ns(1000000),
- getProperty("debug.sf.early_phase_offset_ns"),
- getProperty("debug.sf.early_gl_phase_offset_ns"),
- getProperty("debug.sf.early_app_phase_offset_ns"),
- getProperty("debug.sf.early_gl_app_phase_offset_ns"),
- // Below defines the threshold when an offset is considered to be negative,
- // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
- // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
- // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
- // vsync.
- getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
- .value_or(std::numeric_limits<nsecs_t>::max())) {}
-
-PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
- std::optional<nsecs_t> earlySfOffsetNs,
- std::optional<nsecs_t> earlyGlSfOffsetNs,
- std::optional<nsecs_t> earlyAppOffsetNs,
- std::optional<nsecs_t> earlyGlAppOffsetNs, nsecs_t thresholdForNextVsync)
- : mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
- mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
- mEarlySfOffsetNs(earlySfOffsetNs),
- mEarlyGlSfOffsetNs(earlyGlSfOffsetNs),
- mEarlyAppOffsetNs(earlyAppOffsetNs),
- mEarlyGlAppOffsetNs(earlyGlAppOffsetNs),
- mThresholdForNextVsync(thresholdForNextVsync),
- mOffsets(initializeOffsets(refreshRates)),
- mRefreshRateFps(currentFps) {}
-
-void PhaseOffsets::dump(std::string& result) const {
- const auto [early, earlyGl, late] = getCurrentOffsets();
- using base::StringAppendF;
- StringAppendF(&result,
- " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n"
- " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n"
- " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
- "next VSYNC threshold: %9" PRId64 " ns\n",
- late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
- mThresholdForNextVsync);
-}
-
-std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
- const std::vector<float>& refreshRates) const {
- std::unordered_map<float, Offsets> offsets;
-
- for (const auto& refreshRate : refreshRates) {
- offsets.emplace(refreshRate,
- getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
- }
- return offsets;
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
- if (fps > 65.0f) {
- return getHighFpsOffsets(vsyncPeriod);
- } else {
- return getDefaultOffsets(vsyncPeriod);
- }
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
- return {
- {
- mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
- ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
- : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
-
- mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
- },
- {
- mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
- ? mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
- : mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
-
- mEarlyGlAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
- },
- {
- mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
- ? mSfVSyncPhaseOffsetNs
- : mSfVSyncPhaseOffsetNs - vsyncDuration,
-
- mVSyncPhaseOffsetNs,
- },
- };
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
- const auto highFpsLateAppOffsetNs =
- getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
- const auto highFpsLateSfOffsetNs =
- getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
-
- const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
- const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
- const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns");
- const auto highFpsEarlyGlAppOffsetNs =
- getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
-
- return {
- {
- highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
- ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
- : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
- vsyncDuration,
-
- highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
- },
- {
- highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
- mThresholdForNextVsync
- ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
- : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
- vsyncDuration,
-
- highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
- },
- {
- highFpsLateSfOffsetNs < mThresholdForNextVsync
- ? highFpsLateSfOffsetNs
- : highFpsLateSfOffsetNs - vsyncDuration,
-
- highFpsLateAppOffsetNs,
- },
- };
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
- const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
- [&fps](const std::pair<float, Offsets>& candidateFps) {
- return fpsEqualsWithMargin(fps, candidateFps.first);
- });
-
- if (iter != mOffsets.end()) {
- return iter->second;
- }
-
- // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
- // In this case just construct the offset.
- ALOGW("Can't find offset for %.2f fps", fps);
- return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
-}
-
-static void validateSysprops() {
- const auto validatePropertyBool = [](const char* prop) {
- LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
- };
-
- validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
-
- LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
- "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
- "duration");
-
- LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
- "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
- "duration");
-
- const auto validateProperty = [](const char* prop) {
- LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
- "%s is set to %" PRId64 " but expecting duration", prop,
- getProperty(prop).value_or(-1));
- };
-
- validateProperty("debug.sf.early_phase_offset_ns");
- validateProperty("debug.sf.early_gl_phase_offset_ns");
- validateProperty("debug.sf.early_app_phase_offset_ns");
- validateProperty("debug.sf.early_gl_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
-}
-
-static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) {
- return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration;
-}
-
-static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) {
- return sfDuration == -1 ? 1'000'000
- : vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
-}
-
-PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const {
- return Offsets{
- {
- mSfEarlyDuration < vsyncDuration
- ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration)
- : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration),
- },
- {
- mSfEarlyGlDuration < vsyncDuration
- ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration)
- : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration),
- },
- {
- mSfDuration < vsyncDuration
- ? sfDurationToOffset(mSfDuration, vsyncDuration)
- : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration),
- },
- };
-}
-
-std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
- const std::vector<float>& refreshRates) const {
- std::unordered_map<float, Offsets> offsets;
-
- for (const auto fps : refreshRates) {
- offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
- }
- return offsets;
-}
-
-PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().getFps(),
- getProperty("debug.sf.late.sf.duration").value_or(-1),
- getProperty("debug.sf.late.app.duration").value_or(-1),
- getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
- getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
- getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
- getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
- validateSysprops();
-}
-
-PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
- nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration,
- nsecs_t appEarlyGlDuration)
- : mSfDuration(sfDuration),
- mAppDuration(appDuration),
- mSfEarlyDuration(sfEarlyDuration),
- mAppEarlyDuration(appEarlyDuration),
- mSfEarlyGlDuration(sfEarlyGlDuration),
- mAppEarlyGlDuration(appEarlyGlDuration),
- mOffsets(initializeOffsets(refreshRates)),
- mRefreshRateFps(currentFps) {}
-
-PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
- const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
- return fpsEqualsWithMargin(fps, candidateFps.first);
- });
-
- if (iter != mOffsets.end()) {
- return iter->second;
- }
-
- // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
- // In this case just construct the offset.
- ALOGW("Can't find offset for %.2f fps", fps);
- return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
-}
-
-void PhaseDurations::dump(std::string& result) const {
- const auto [early, earlyGl, late] = getCurrentOffsets();
- using base::StringAppendF;
- StringAppendF(&result,
- " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
- " ns\n"
- " app duration: %9" PRId64 " ns\t SF duration: %9" PRId64
- " ns\n"
- " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
- " ns\n"
- " early app duration: %9" PRId64 " ns\t early SF duration: %9" PRId64
- " ns\n"
- " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
- " ns\n"
- " GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64
- " ns\n",
- late.app,
-
- late.sf,
-
- mAppDuration, mSfDuration,
-
- early.app, early.sf,
-
- mAppEarlyDuration, mSfEarlyDuration,
-
- earlyGl.app,
-
- earlyGl.sf,
-
- mAppEarlyGlDuration, mSfEarlyGlDuration);
-}
-
-} // namespace impl
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
deleted file mode 100644
index 9ec6d56..0000000
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <unordered_map>
-
-#include "RefreshRateConfigs.h"
-#include "VSyncModulator.h"
-
-namespace android::scheduler {
-
-/*
- * This class encapsulates offsets for different refresh rates. Depending
- * on what refresh rate we are using, and wheter we are composing in GL,
- * different offsets will help us with latency. This class keeps track of
- * which mode the device is on, and returns approprate offsets when needed.
- */
-class PhaseConfiguration {
-public:
- using Offsets = VSyncModulator::OffsetsConfig;
-
- virtual ~PhaseConfiguration();
-
- virtual Offsets getCurrentOffsets() const = 0;
- virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
-
- virtual void setRefreshRateFps(float fps) = 0;
-
- virtual void dump(std::string& result) const = 0;
-};
-
-namespace impl {
-
-/*
- * This is the old implementation of phase offsets and considered as deprecated.
- * PhaseDurations is the new implementation.
- */
-class PhaseOffsets : public scheduler::PhaseConfiguration {
-public:
- PhaseOffsets(const scheduler::RefreshRateConfigs&);
-
- // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(float fps) const override;
-
- // Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
-
- // This function should be called when the device is switching between different
- // refresh rates, to properly update the offsets.
- void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
-
- // Returns current offsets in human friendly format.
- void dump(std::string& result) const override;
-
-protected:
- // Used for unit tests
- PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
- std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGlSfOffsetNs,
- std::optional<nsecs_t> earlyAppOffsetNs, std::optional<nsecs_t> earlyGlAppOffsetNs,
- nsecs_t thresholdForNextVsync);
- std::unordered_map<float, Offsets> initializeOffsets(
- const std::vector<float>& refreshRates) const;
- Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
- Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
- Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
-
- const nsecs_t mVSyncPhaseOffsetNs;
- const nsecs_t mSfVSyncPhaseOffsetNs;
- const std::optional<nsecs_t> mEarlySfOffsetNs;
- const std::optional<nsecs_t> mEarlyGlSfOffsetNs;
- const std::optional<nsecs_t> mEarlyAppOffsetNs;
- const std::optional<nsecs_t> mEarlyGlAppOffsetNs;
- const nsecs_t mThresholdForNextVsync;
- const std::unordered_map<float, Offsets> mOffsets;
-
- std::atomic<float> mRefreshRateFps;
-};
-
-/*
- * Class that encapsulates the phase offsets for SurfaceFlinger and App.
- * The offsets are calculated from durations for each one of the (late, early, earlyGL)
- * offset types.
- */
-class PhaseDurations : public scheduler::PhaseConfiguration {
-public:
- PhaseDurations(const scheduler::RefreshRateConfigs&);
-
- // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(float fps) const override;
-
- // Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
-
- // This function should be called when the device is switching between different
- // refresh rates, to properly update the offsets.
- void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
-
- // Returns current offsets in human friendly format.
- void dump(std::string& result) const override;
-
-protected:
- // Used for unit tests
- PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
- nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
- nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration);
-
-private:
- std::unordered_map<float, Offsets> initializeOffsets(const std::vector<float>&) const;
- PhaseDurations::Offsets constructOffsets(nsecs_t vsyncDuration) const;
-
- const nsecs_t mSfDuration;
- const nsecs_t mAppDuration;
-
- const nsecs_t mSfEarlyDuration;
- const nsecs_t mAppEarlyDuration;
-
- const nsecs_t mSfEarlyGlDuration;
- const nsecs_t mAppEarlyGlDuration;
-
- const std::unordered_map<float, Offsets> mOffsets;
-
- std::atomic<float> mRefreshRateFps;
-};
-
-} // namespace impl
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 27bf0ec..eed8486 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -280,6 +280,11 @@
RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
HwcConfigIndexType currentConfigId);
+ // Returns whether switching configs (refresh rate or resolution) is possible.
+ // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the configs only
+ // differ in resolution.
+ bool canSwitch() const { return mRefreshRates.size() > 1; }
+
// Class to enumerate options around toggling the kernel timer on and off. We have an option
// for no change to avoid extra calls to kernel.
enum class KernelIdleTimerAction {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5c0ba01..4f4c1d0 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -20,11 +20,11 @@
#include "Scheduler.h"
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
-#include <cutils/properties.h>
#include <input/InputWindow.h>
#include <system/window.h>
#include <ui/DisplayStatInfo.h>
@@ -41,7 +41,6 @@
#include "../Layer.h"
#include "DispSync.h"
#include "DispSyncSource.h"
-#include "EventControlThread.h"
#include "EventThread.h"
#include "InjectVSyncSource.h"
#include "OneShotTimer.h"
@@ -60,67 +59,62 @@
} \
} while (false)
+using namespace std::string_literals;
+
namespace android {
-std::unique_ptr<DispSync> createDispSync(bool supportKernelTimer) {
- // TODO (140302863) remove this and use the vsync_reactor system.
- if (property_get_bool("debug.sf.vsync_reactor", true)) {
- // TODO (144707443) tune Predictor tunables.
- static constexpr int defaultRate = 60;
- static constexpr auto initialPeriod =
- std::chrono::duration<nsecs_t, std::ratio<1, defaultRate>>(1);
- static constexpr size_t vsyncTimestampHistorySize = 20;
- static constexpr size_t minimumSamplesForPrediction = 6;
- static constexpr uint32_t discardOutlierPercent = 20;
- auto tracker = std::make_unique<
- scheduler::VSyncPredictor>(std::chrono::duration_cast<std::chrono::nanoseconds>(
- initialPeriod)
- .count(),
- vsyncTimestampHistorySize, minimumSamplesForPrediction,
- discardOutlierPercent);
+namespace {
- static constexpr auto vsyncMoveThreshold =
- std::chrono::duration_cast<std::chrono::nanoseconds>(3ms);
- static constexpr auto timerSlack =
- std::chrono::duration_cast<std::chrono::nanoseconds>(500us);
- auto dispatch = std::make_unique<
- scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), *tracker,
- timerSlack.count(), vsyncMoveThreshold.count());
-
- static constexpr size_t pendingFenceLimit = 20;
- return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
- std::move(dispatch), std::move(tracker),
- pendingFenceLimit, supportKernelTimer);
- } else {
- return std::make_unique<impl::DispSync>("SchedulerDispSync",
- sysprop::running_without_sync_framework(true));
- }
+std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() {
+ // TODO(b/144707443): Tune constants.
+ constexpr int kDefaultRate = 60;
+ constexpr auto initialPeriod = std::chrono::duration<nsecs_t, std::ratio<1, kDefaultRate>>(1);
+ constexpr nsecs_t idealPeriod =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(initialPeriod).count();
+ constexpr size_t vsyncTimestampHistorySize = 20;
+ constexpr size_t minimumSamplesForPrediction = 6;
+ constexpr uint32_t discardOutlierPercent = 20;
+ return std::make_unique<scheduler::VSyncPredictor>(idealPeriod, vsyncTimestampHistorySize,
+ minimumSamplesForPrediction,
+ discardOutlierPercent);
}
-Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
- const scheduler::RefreshRateConfigs& refreshRateConfig,
- ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
- bool useContentDetection)
- : mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
- mPrimaryDispSync(createDispSync(mSupportKernelTimer)),
- mEventControlThread(new impl::EventControlThread(std::move(function))),
- mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(refreshRateConfig),
- mUseContentDetection(useContentDetection),
- mUseContentDetectionV2(useContentDetectionV2) {
+std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(scheduler::VSyncTracker& tracker) {
+ // TODO(b/144707443): Tune constants.
+ constexpr std::chrono::nanoseconds vsyncMoveThreshold = 3ms;
+ constexpr std::chrono::nanoseconds timerSlack = 500us;
+ return std::make_unique<
+ scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), tracker,
+ timerSlack.count(), vsyncMoveThreshold.count());
+}
+
+const char* toContentDetectionString(bool useContentDetection, bool useContentDetectionV2) {
+ if (!useContentDetection) return "off";
+ return useContentDetectionV2 ? "V2" : "V1";
+}
+
+} // namespace
+
+Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
+ : Scheduler(configs, callback,
+ {.supportKernelTimer = sysprop::support_kernel_idle_timer(false),
+ .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false),
+ .useContentDetectionV2 =
+ base::GetBoolProperty("debug.sf.use_content_detection_v2"s, true),
+ // TODO(b/140302863): Remove this and use the vsync_reactor system.
+ .useVsyncPredictor = base::GetBoolProperty("debug.sf.vsync_reactor"s, true)}) {}
+
+Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
+ Options options)
+ : Scheduler(createVsyncSchedule(options), configs, callback,
+ createLayerHistory(configs, options.useContentDetectionV2), options) {
using namespace sysprop;
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(refreshRateConfig);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
-
- const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
+ const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);
if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
- const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
- : &Scheduler::idleTimerCallback;
+ const auto callback = mOptions.supportKernelTimer ? &Scheduler::kernelIdleTimerCallback
+ : &Scheduler::idleTimerCallback;
mIdleTimer.emplace(
std::chrono::milliseconds(millis),
[this, callback] { std::invoke(callback, this, TimerState::Reset); },
@@ -146,18 +140,16 @@
}
}
-Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
- std::unique_ptr<EventControlThread> eventControlThread,
- const scheduler::RefreshRateConfigs& configs,
- ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
- bool useContentDetection)
- : mSupportKernelTimer(false),
- mPrimaryDispSync(std::move(primaryDispSync)),
- mEventControlThread(std::move(eventControlThread)),
+Scheduler::Scheduler(VsyncSchedule schedule, const scheduler::RefreshRateConfigs& configs,
+ ISchedulerCallback& schedulerCallback,
+ std::unique_ptr<LayerHistory> layerHistory, Options options)
+ : mOptions(options),
+ mVsyncSchedule(std::move(schedule)),
+ mLayerHistory(std::move(layerHistory)),
mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(configs),
- mUseContentDetection(useContentDetection),
- mUseContentDetectionV2(useContentDetectionV2) {}
+ mRefreshRateConfigs(configs) {
+ mSchedulerCallback.setVsyncEnabled(false);
+}
Scheduler::~Scheduler() {
// Ensure the OneShotTimer threads are joined before we start destroying state.
@@ -166,13 +158,43 @@
mIdleTimer.reset();
}
+Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(Options options) {
+ if (!options.useVsyncPredictor) {
+ auto sync = std::make_unique<impl::DispSync>("SchedulerDispSync",
+ sysprop::running_without_sync_framework(true));
+ return {std::move(sync), nullptr, nullptr};
+ }
+
+ 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)};
+}
+
+std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
+ const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) {
+ if (!configs.canSwitch()) return nullptr;
+
+ if (useContentDetectionV2) {
+ return std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
+ }
+
+ return std::make_unique<scheduler::impl::LayerHistory>();
+}
+
DispSync& Scheduler::getPrimaryDispSync() {
- return *mPrimaryDispSync;
+ return *mVsyncSchedule.sync;
}
std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name,
nsecs_t phaseOffsetNs) {
- return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
+ return std::make_unique<DispSyncSource>(&getPrimaryDispSync(), phaseOffsetNs,
true /* traceVsync */, name);
}
@@ -278,8 +300,8 @@
}
void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
- stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0, systemTime());
- stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
+ stats->vsyncTime = getPrimaryDispSync().computeNextRefresh(0, systemTime());
+ stats->vsyncPeriod = getPrimaryDispSync().getPeriod();
}
Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
@@ -316,8 +338,8 @@
void Scheduler::enableHardwareVsync() {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
- mPrimaryDispSync->beginResync();
- mEventControlThread->setVsyncEnabled(true);
+ getPrimaryDispSync().beginResync();
+ mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
}
@@ -325,8 +347,8 @@
void Scheduler::disableHardwareVsync(bool makeUnavailable) {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
- mEventControlThread->setVsyncEnabled(false);
- mPrimaryDispSync->endResync();
+ mSchedulerCallback.setVsyncEnabled(false);
+ getPrimaryDispSync().endResync();
mPrimaryHWVsyncEnabled = false;
}
if (makeUnavailable) {
@@ -366,11 +388,11 @@
void Scheduler::setVsyncPeriod(nsecs_t period) {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
- mPrimaryDispSync->setPeriod(period);
+ getPrimaryDispSync().setPeriod(period);
if (!mPrimaryHWVsyncEnabled) {
- mPrimaryDispSync->beginResync();
- mEventControlThread->setVsyncEnabled(true);
+ getPrimaryDispSync().beginResync();
+ mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
}
@@ -383,7 +405,7 @@
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
needsHwVsync =
- mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
+ getPrimaryDispSync().addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
}
}
@@ -395,7 +417,7 @@
}
void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
- if (mPrimaryDispSync->addPresentFence(fenceTime)) {
+ if (getPrimaryDispSync().addPresentFence(fenceTime)) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
@@ -403,11 +425,11 @@
}
void Scheduler::setIgnorePresentFences(bool ignore) {
- mPrimaryDispSync->setIgnorePresentFences(ignore);
+ getPrimaryDispSync().setIgnorePresentFences(ignore);
}
nsecs_t Scheduler::getDispSyncExpectedPresentTime(nsecs_t now) {
- return mPrimaryDispSync->expectedPresentTime(now);
+ return getPrimaryDispSync().expectedPresentTime(now);
}
void Scheduler::registerLayer(Layer* layer) {
@@ -416,25 +438,25 @@
const auto minFps = mRefreshRateConfigs.getMinRefreshRate().getFps();
const auto maxFps = mRefreshRateConfigs.getMaxRefreshRate().getFps();
- if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
+ if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
mLayerHistory->registerLayer(layer, minFps, maxFps,
scheduler::LayerHistory::LayerVoteType::NoVote);
- } else if (!mUseContentDetection) {
+ } else if (!mOptions.useContentDetection) {
// If the content detection feature is off, all layers are registered at Max. We still keep
// the layer history, since we use it for other features (like Frame Rate API), so layers
// still need to be registered.
mLayerHistory->registerLayer(layer, minFps, maxFps,
scheduler::LayerHistory::LayerVoteType::Max);
- } else if (!mUseContentDetectionV2) {
+ } else if (!mOptions.useContentDetectionV2) {
// In V1 of content detection, all layers are registered as Heuristic (unless it's
// wallpaper).
const auto highFps =
- layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER ? minFps : maxFps;
+ layer->getWindowType() == InputWindowInfo::Type::WALLPAPER ? minFps : maxFps;
mLayerHistory->registerLayer(layer, minFps, highFps,
scheduler::LayerHistory::LayerVoteType::Heuristic);
} else {
- if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
+ if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
// Running Wallpaper at Min is considered as part of content detection.
mLayerHistory->registerLayer(layer, minFps, maxFps,
scheduler::LayerHistory::LayerVoteType::Min);
@@ -499,19 +521,10 @@
}
void Scheduler::notifyTouchEvent() {
- if (!mTouchTimer) return;
-
- // Touch event will boost the refresh rate to performance.
- // Clear Layer History to get fresh FPS detection.
- // NOTE: Instead of checking all the layers, we should be checking the layer
- // that is currently on top. b/142507166 will give us this capability.
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mLayerHistory) {
- // Layer History will be cleared based on RefreshRateConfigs::getBestRefreshRate
-
+ if (mTouchTimer) {
mTouchTimer->reset();
- if (mSupportKernelTimer && mIdleTimer) {
+ if (mOptions.supportKernelTimer && mIdleTimer) {
mIdleTimer->reset();
}
}
@@ -564,8 +577,14 @@
void Scheduler::touchTimerCallback(TimerState state) {
const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
+ // Touch event will boost the refresh rate to performance.
+ // Clear layer history to get fresh FPS detection.
+ // NOTE: Instead of checking all the layers, we should be checking the layer
+ // that is currently on top. b/142507166 will give us this capability.
if (handleTimerStateChanged(&mFeatures.touch, touch)) {
- mLayerHistory->clear();
+ if (mLayerHistory) {
+ mLayerHistory->clear();
+ }
}
ATRACE_INT("TouchState", static_cast<int>(touch));
}
@@ -577,14 +596,14 @@
void Scheduler::dump(std::string& result) const {
using base::StringAppendF;
- const char* const states[] = {"off", "on"};
- StringAppendF(&result, "+ Idle timer: %s\n",
- mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
+ StringAppendF(&result, "+ Idle timer: %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off");
StringAppendF(&result, "+ Touch timer: %s\n",
- mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
- StringAppendF(&result, "+ Use content detection: %s\n\n",
- sysprop::use_content_detection_for_refresh_rate(false) ? "on" : "off");
+ mTouchTimer ? mTouchTimer->dump().c_str() : "off");
+ StringAppendF(&result, "+ Content detection: %s %s\n\n",
+ toContentDetectionString(mOptions.useContentDetection,
+ mOptions.useContentDetectionV2),
+ mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
}
template <class T>
@@ -631,7 +650,7 @@
const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired;
- if (!mUseContentDetectionV2) {
+ if (!mOptions.useContentDetectionV2) {
// As long as touch is active we want to be in performance mode.
if (touchActive) {
return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 730ea8f..4e7a9a1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -29,7 +29,6 @@
#include <ui/GraphicTypes.h>
#pragma clang diagnostic pop
-#include "EventControlThread.h"
#include "EventThread.h"
#include "LayerHistory.h"
#include "OneShotTimer.h"
@@ -44,41 +43,30 @@
class DispSync;
class FenceTime;
class InjectVSyncSource;
-struct DisplayStateInfo;
-class ISchedulerCallback {
-public:
- virtual ~ISchedulerCallback() = default;
+namespace scheduler {
+class VSyncDispatch;
+class VSyncTracker;
+} // namespace scheduler
+
+struct ISchedulerCallback {
+ virtual void setVsyncEnabled(bool) = 0;
virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
scheduler::RefreshRateConfigEvent) = 0;
virtual void repaintEverythingForHWC() = 0;
virtual void kernelTimerChanged(bool expired) = 0;
+
+protected:
+ ~ISchedulerCallback() = default;
};
-class IPhaseOffsetControl {
-public:
- virtual ~IPhaseOffsetControl() = default;
- virtual void setPhaseOffset(scheduler::ConnectionHandle, nsecs_t phaseOffset) = 0;
-};
-
-class Scheduler : public IPhaseOffsetControl {
+class Scheduler {
public:
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
- // Indicates whether to start the transaction early, or at vsync time.
- enum class TransactionStart {
- Early, // DEPRECATED. Start the transaction early. Times out on its own
- EarlyStart, // Start the transaction early and keep this config until EarlyEnd
- EarlyEnd, // End the early config started at EarlyStart
- Normal // Start the transaction at the normal time
- };
-
- Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
- const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
- bool useContentDetectionV2, bool useContentDetection);
-
- virtual ~Scheduler();
+ Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&);
+ ~Scheduler();
DispSync& getPrimaryDispSync();
@@ -101,7 +89,7 @@
void onScreenReleased(ConnectionHandle);
// Modifies phase offset in the event thread.
- void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset) override;
+ void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset);
void getDisplayStatInfo(DisplayStatInfo* stats);
@@ -172,10 +160,33 @@
enum class TimerState { Reset, Expired };
enum class TouchState { Inactive, Active };
+ struct Options {
+ // Whether to use idle timer callbacks that support the kernel timer.
+ bool supportKernelTimer;
+ // Whether to use content detection at all.
+ bool useContentDetection;
+ // Whether to use improved content detection.
+ bool useContentDetectionV2;
+ // Whether to use improved DispSync implementation.
+ bool useVsyncPredictor;
+ };
+
+ struct VsyncSchedule {
+ std::unique_ptr<DispSync> sync;
+ std::unique_ptr<scheduler::VSyncTracker> tracker;
+ std::unique_ptr<scheduler::VSyncDispatch> dispatch;
+ };
+
+ // Unlike the testing constructor, this creates the VsyncSchedule, LayerHistory, and timers.
+ Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&, Options);
+
// Used by tests to inject mocks.
- Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
- const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
- bool useContentDetectionV2, bool useContentDetection);
+ Scheduler(VsyncSchedule, const scheduler::RefreshRateConfigs&, ISchedulerCallback&,
+ std::unique_ptr<LayerHistory>, Options);
+
+ static VsyncSchedule createVsyncSchedule(Options);
+ static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&,
+ bool useContentDetectionV2);
std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
@@ -224,14 +235,11 @@
std::atomic<nsecs_t> mLastResyncTime = 0;
- // Whether to use idle timer callbacks that support the kernel timer.
- const bool mSupportKernelTimer;
-
- std::unique_ptr<DispSync> mPrimaryDispSync;
- std::unique_ptr<EventControlThread> mEventControlThread;
+ const Options mOptions;
+ VsyncSchedule mVsyncSchedule;
// Used to choose refresh rate if content detection is enabled.
- std::unique_ptr<LayerHistory> mLayerHistory;
+ const std::unique_ptr<LayerHistory> mLayerHistory;
// Timer that records time between requests for next vsync.
std::optional<scheduler::OneShotTimer> mIdleTimer;
@@ -274,11 +282,6 @@
std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
GUARDED_BY(mVsyncTimelineLock);
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
-
- // This variable indicates whether to use the content detection feature at all.
- const bool mUseContentDetection;
- // This variable indicates whether to use V2 version of the content detection.
- const bool mUseContentDetectionV2;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
deleted file mode 100644
index 2567c04..0000000
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// 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 "VSyncModulator.h"
-
-#include <cutils/properties.h>
-#include <utils/Trace.h>
-
-#include <chrono>
-#include <cinttypes>
-#include <mutex>
-
-namespace android::scheduler {
-
-VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl,
- Scheduler::ConnectionHandle appConnectionHandle,
- Scheduler::ConnectionHandle sfConnectionHandle,
- const OffsetsConfig& config)
- : mPhaseOffsetControl(phaseOffsetControl),
- mAppConnectionHandle(appConnectionHandle),
- mSfConnectionHandle(sfConnectionHandle),
- mOffsetsConfig(config) {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.vsync_trace_detailed_info", value, "0");
- mTraceDetailedInfo = atoi(value);
-}
-
-void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
- std::lock_guard<std::mutex> lock(mMutex);
- mOffsetsConfig = config;
- updateOffsetsLocked();
-}
-
-void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
- switch (transactionStart) {
- case Scheduler::TransactionStart::EarlyStart:
- ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart");
- mExplicitEarlyWakeup = true;
- break;
- case Scheduler::TransactionStart::EarlyEnd:
- ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart");
- mExplicitEarlyWakeup = false;
- break;
- case Scheduler::TransactionStart::Normal:
- case Scheduler::TransactionStart::Early:
- // Non explicit don't change the explicit early wakeup state
- break;
- }
-
- if (mTraceDetailedInfo) {
- ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
- }
-
- if (!mExplicitEarlyWakeup &&
- (transactionStart == Scheduler::TransactionStart::Early ||
- transactionStart == Scheduler::TransactionStart::EarlyEnd)) {
- mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
- mEarlyTxnStartTime = std::chrono::steady_clock::now();
- }
-
- // An early transaction stays an early transaction.
- if (transactionStart == mTransactionStart ||
- mTransactionStart == Scheduler::TransactionStart::EarlyEnd) {
- return;
- }
- mTransactionStart = transactionStart;
- updateOffsets();
-}
-
-void VSyncModulator::onTransactionHandled() {
- mTxnAppliedTime = std::chrono::steady_clock::now();
- if (mTransactionStart == Scheduler::TransactionStart::Normal) return;
- mTransactionStart = Scheduler::TransactionStart::Normal;
- updateOffsets();
-}
-
-void VSyncModulator::onRefreshRateChangeInitiated() {
- if (mRefreshRateChangePending) {
- return;
- }
- mRefreshRateChangePending = true;
- updateOffsets();
-}
-
-void VSyncModulator::onRefreshRateChangeCompleted() {
- if (!mRefreshRateChangePending) {
- return;
- }
- mRefreshRateChangePending = false;
- updateOffsets();
-}
-
-void VSyncModulator::onRefreshed(bool usedRenderEngine) {
- bool updateOffsetsNeeded = false;
-
- // Apply a margin to account for potential data races
- // This might make us stay in early offsets for one
- // additional frame but it's better to be conservative here.
- if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) {
- if (mRemainingEarlyFrameCount > 0) {
- mRemainingEarlyFrameCount--;
- updateOffsetsNeeded = true;
- }
- }
- if (usedRenderEngine) {
- mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION;
- updateOffsetsNeeded = true;
- } else if (mRemainingRenderEngineUsageCount > 0) {
- mRemainingRenderEngineUsageCount--;
- updateOffsetsNeeded = true;
- }
- if (updateOffsetsNeeded) {
- updateOffsets();
- }
-}
-
-VSyncModulator::Offsets VSyncModulator::getOffsets() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mOffsets;
-}
-
-const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
- // Early offsets are used if we're in the middle of a refresh rate
- // change, or if we recently begin a transaction.
- if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd ||
- mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
- return mOffsetsConfig.early;
- } else if (mRemainingRenderEngineUsageCount > 0) {
- return mOffsetsConfig.earlyGl;
- } else {
- return mOffsetsConfig.late;
- }
-}
-
-void VSyncModulator::updateOffsets() {
- std::lock_guard<std::mutex> lock(mMutex);
- updateOffsetsLocked();
-}
-
-void VSyncModulator::updateOffsetsLocked() {
- const Offsets& offsets = getNextOffsets();
-
- mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf);
- mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app);
-
- mOffsets = offsets;
-
- if (!mTraceDetailedInfo) {
- return;
- }
-
- const bool isEarly = &offsets == &mOffsetsConfig.early;
- const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
- const bool isLate = &offsets == &mOffsetsConfig.late;
-
- ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
- ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl);
- ATRACE_INT("Vsync-LateOffsetsOn", isLate);
-}
-
-} // 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/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
deleted file mode 100644
index ab678c9..0000000
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <chrono>
-#include <mutex>
-
-#include "Scheduler.h"
-
-namespace android::scheduler {
-
-/*
- * Modulates the vsync-offsets depending on current SurfaceFlinger state.
- */
-class VSyncModulator {
-private:
- // Number of frames we'll keep the early phase offsets once they are activated for a
- // transaction. This acts as a low-pass filter in case the client isn't quick enough in
- // sending new transactions.
- static constexpr int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
-
- // Number of frames we'll keep the early gl phase offsets once they are activated.
- // This acts as a low-pass filter to avoid scenarios where we rapidly
- // switch in and out of gl composition.
- static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
-
- // Margin used to account for potential data races
- static const constexpr std::chrono::nanoseconds MARGIN_FOR_TX_APPLY = 1ms;
-
-public:
- // Wrapper for a collection of surfaceflinger/app offsets for a particular
- // configuration.
- struct Offsets {
- nsecs_t sf;
- nsecs_t app;
-
- bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
-
- bool operator!=(const Offsets& other) const { return !(*this == other); }
- };
-
- struct OffsetsConfig {
- Offsets early; // For transactions with the eEarlyWakeup flag.
- Offsets earlyGl; // As above but while compositing with GL.
- Offsets late; // Default.
-
- bool operator==(const OffsetsConfig& other) const {
- return early == other.early && earlyGl == other.earlyGl && late == other.late;
- }
-
- bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
- };
-
- VSyncModulator(IPhaseOffsetControl&, ConnectionHandle appConnectionHandle,
- ConnectionHandle sfConnectionHandle, const OffsetsConfig&);
-
- void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
-
- // Signals that a transaction has started, and changes offsets accordingly.
- void setTransactionStart(Scheduler::TransactionStart transactionStart);
-
- // Signals that a transaction has been completed, so that we can finish
- // special handling for a transaction.
- void onTransactionHandled();
-
- // Called when we send a refresh rate change to hardware composer, so that
- // we can move into early offsets.
- void onRefreshRateChangeInitiated();
-
- // Called when we detect from vsync signals that the refresh rate changed.
- // This way we can move out of early offsets if no longer necessary.
- void onRefreshRateChangeCompleted();
-
- // Called when the display is presenting a new frame. usedRenderEngine
- // should be set to true if RenderEngine was involved with composing the new
- // frame.
- void onRefreshed(bool usedRenderEngine);
-
- // Returns the offsets that we are currently using
- Offsets getOffsets() const EXCLUDES(mMutex);
-
-private:
- friend class VSyncModulatorTest;
- // Returns the next offsets that we should be using
- const Offsets& getNextOffsets() const REQUIRES(mMutex);
- // Updates offsets and persists them into the scheduler framework.
- void updateOffsets() EXCLUDES(mMutex);
- void updateOffsetsLocked() REQUIRES(mMutex);
-
- IPhaseOffsetControl& mPhaseOffsetControl;
- const ConnectionHandle mAppConnectionHandle;
- const ConnectionHandle mSfConnectionHandle;
-
- mutable std::mutex mMutex;
- OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex);
-
- Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
-
- std::atomic<Scheduler::TransactionStart> mTransactionStart =
- Scheduler::TransactionStart::Normal;
- std::atomic<bool> mRefreshRateChangePending = false;
- std::atomic<bool> mExplicitEarlyWakeup = false;
- std::atomic<int> mRemainingEarlyFrameCount = 0;
- std::atomic<int> mRemainingRenderEngineUsageCount = 0;
- std::atomic<std::chrono::steady_clock::time_point> mEarlyTxnStartTime = {};
- std::atomic<std::chrono::steady_clock::time_point> mTxnAppliedTime = {};
-
- bool mTraceDetailedInfo = false;
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index efa8bab..a2b279b 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -55,15 +55,15 @@
}
};
-VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
- std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
+VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncDispatch& dispatch,
+ VSyncTracker& tracker, size_t pendingFenceLimit,
bool supportKernelIdleTimer)
: mClock(std::move(clock)),
- mTracker(std::move(tracker)),
- mDispatch(std::move(dispatch)),
+ mTracker(tracker),
+ mDispatch(dispatch),
mPendingLimit(pendingFenceLimit),
mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false)
- ? std::make_unique<PredictedVsyncTracer>(*mDispatch)
+ ? std::make_unique<PredictedVsyncTracer>(mDispatch)
: nullptr),
mSupportKernelIdleTimer(supportKernelIdleTimer) {}
@@ -182,7 +182,7 @@
} else if (time == Fence::SIGNAL_TIME_INVALID) {
it = mUnfiredFences.erase(it);
} else {
- timestampAccepted &= mTracker->addVsyncTimestamp(time);
+ timestampAccepted &= mTracker.addVsyncTimestamp(time);
it = mUnfiredFences.erase(it);
}
@@ -194,7 +194,7 @@
}
mUnfiredFences.push_back(fence);
} else {
- timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+ timestampAccepted &= mTracker.addVsyncTimestamp(signalTime);
}
if (!timestampAccepted) {
@@ -224,12 +224,12 @@
}
nsecs_t VSyncReactor::computeNextRefresh(int periodOffset, nsecs_t now) const {
- auto const currentPeriod = periodOffset ? mTracker->currentPeriod() : 0;
- return mTracker->nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
+ auto const currentPeriod = periodOffset ? mTracker.currentPeriod() : 0;
+ return mTracker.nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
}
nsecs_t VSyncReactor::expectedPresentTime(nsecs_t now) {
- return mTracker->nextAnticipatedVSyncTimeFrom(now);
+ return mTracker.nextAnticipatedVSyncTimeFrom(now);
}
void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
@@ -262,11 +262,11 @@
}
nsecs_t VSyncReactor::getPeriod() {
- return mTracker->currentPeriod();
+ return mTracker.currentPeriod();
}
void VSyncReactor::beginResync() {
- mTracker->resetModel();
+ mTracker.resetModel();
}
void VSyncReactor::endResync() {}
@@ -307,7 +307,7 @@
if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
ATRACE_NAME("VSR: period confirmed");
if (mPeriodTransitioningTo) {
- mTracker->setPeriod(*mPeriodTransitioningTo);
+ mTracker.setPeriod(*mPeriodTransitioningTo);
for (auto& entry : mCallbacks) {
entry.second->setPeriod(*mPeriodTransitioningTo);
}
@@ -315,12 +315,12 @@
}
if (mLastHwVsync) {
- mTracker->addVsyncTimestamp(*mLastHwVsync);
+ mTracker.addVsyncTimestamp(*mLastHwVsync);
}
- mTracker->addVsyncTimestamp(timestamp);
+ mTracker.addVsyncTimestamp(timestamp);
endPeriodTransition();
- mMoreSamplesNeeded = mTracker->needsMoreSamples();
+ mMoreSamplesNeeded = mTracker.needsMoreSamples();
} else if (mPeriodConfirmationInProgress) {
ATRACE_NAME("VSR: still confirming period");
mLastHwVsync = timestamp;
@@ -329,8 +329,8 @@
} else {
ATRACE_NAME("VSR: adding sample");
*periodFlushed = false;
- mTracker->addVsyncTimestamp(timestamp);
- mMoreSamplesNeeded = mTracker->needsMoreSamples();
+ mTracker.addVsyncTimestamp(timestamp);
+ mMoreSamplesNeeded = mTracker.needsMoreSamples();
}
if (!mMoreSamplesNeeded) {
@@ -353,9 +353,9 @@
return NO_MEMORY;
}
- auto const period = mTracker->currentPeriod();
- auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
- phase, mClock->now());
+ 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;
}
@@ -409,9 +409,9 @@
}
StringAppendF(&result, "VSyncTracker:\n");
- mTracker->dump(result);
+ mTracker.dump(result);
StringAppendF(&result, "VSyncDispatch:\n");
- mDispatch->dump(result);
+ mDispatch.dump(result);
}
void VSyncReactor::reset() {}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 265d89c..22ceb39 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -35,9 +35,8 @@
// TODO (b/145217110): consider renaming.
class VSyncReactor : public android::DispSync {
public:
- VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
- std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
- bool supportKernelIdleTimer);
+ VSyncReactor(std::unique_ptr<Clock> clock, VSyncDispatch& dispatch, VSyncTracker& tracker,
+ size_t pendingFenceLimit, bool supportKernelIdleTimer);
~VSyncReactor();
bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
@@ -72,8 +71,8 @@
REQUIRES(mMutex);
std::unique_ptr<Clock> const mClock;
- std::unique_ptr<VSyncTracker> const mTracker;
- std::unique_ptr<VSyncDispatch> const mDispatch;
+ VSyncTracker& mTracker;
+ VSyncDispatch& mDispatch;
size_t const mPendingLimit;
mutable std::mutex mMutex;
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
new file mode 100644
index 0000000..aac2569
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VsyncConfiguration.h"
+
+#include <cutils/properties.h>
+
+#include <optional>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace {
+
+std::optional<nsecs_t> getProperty(const char* name) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get(name, value, "-1");
+ if (const int i = atoi(value); i != -1) return i;
+ return std::nullopt;
+}
+
+bool fpsEqualsWithMargin(float fpsA, float fpsB) {
+ static constexpr float MARGIN = 0.01f;
+ return std::abs(fpsA - fpsB) <= MARGIN;
+}
+
+std::vector<float> getRefreshRatesFromConfigs(
+ const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+ std::vector<float> refreshRates;
+ refreshRates.reserve(allRefreshRates.size());
+
+ for (const auto& [ignored, refreshRate] : allRefreshRates) {
+ refreshRates.emplace_back(refreshRate->getFps());
+ }
+
+ return refreshRates;
+}
+
+} // namespace
+
+namespace android::scheduler::impl {
+
+VsyncConfiguration::VsyncConfiguration(float currentFps) : mRefreshRateFps(currentFps) {}
+
+PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(float fps) const {
+ const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
+ [&fps](const std::pair<float, VsyncConfigSet>& candidateFps) {
+ return fpsEqualsWithMargin(fps, candidateFps.first);
+ });
+
+ if (iter != mOffsets.end()) {
+ return iter->second;
+ }
+
+ // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+ // In this case just construct the offset.
+ ALOGW("Can't find offset for %.2f fps", fps);
+ return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
+}
+
+void VsyncConfiguration::initializeOffsets(const std::vector<float>& refreshRates) {
+ for (const auto fps : refreshRates) {
+ mOffsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
+ }
+}
+
+void VsyncConfiguration::dump(std::string& result) const {
+ const auto [early, earlyGpu, late] = getCurrentConfigs();
+ using base::StringAppendF;
+ StringAppendF(&result,
+ " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
+ " ns\n"
+ " app duration: %9lld ns\t SF duration: %9lld ns\n"
+ " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
+ " ns\n"
+ " early app duration: %9lld ns\t early SF duration: %9lld ns\n"
+ " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
+ " ns\n"
+ " GL early app duration: %9lld ns\tGL early SF duration: %9lld ns\n",
+ late.appOffset, late.sfOffset,
+
+ late.appWorkDuration.count(), late.sfWorkDuration.count(),
+
+ early.appOffset, early.sfOffset,
+
+ early.appWorkDuration.count(), early.sfWorkDuration.count(),
+
+ earlyGpu.appOffset, earlyGpu.sfOffset,
+
+ earlyGpu.appWorkDuration.count(), earlyGpu.sfWorkDuration.count());
+}
+
+PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().getFps(),
+ sysprop::vsync_event_phase_offset_ns(1000000),
+ sysprop::vsync_sf_event_phase_offset_ns(1000000),
+ getProperty("debug.sf.early_phase_offset_ns"),
+ getProperty("debug.sf.early_gl_phase_offset_ns"),
+ getProperty("debug.sf.early_app_phase_offset_ns"),
+ getProperty("debug.sf.early_gl_app_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000),
+ getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000),
+ getProperty("debug.sf.high_fps_early_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_gl_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_app_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"),
+ // Below defines the threshold when an offset is considered to be negative,
+ // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+ // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+ // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+ // vsync.
+ getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+ .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(
+ const std::vector<float>& refreshRates, float currentFps, nsecs_t vsyncPhaseOffsetNs,
+ nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs,
+ std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
+ nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync)
+ : VsyncConfiguration(currentFps),
+ mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
+ mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
+ mEarlySfOffsetNs(earlySfOffsetNs),
+ mEarlyGpuSfOffsetNs(earlyGpuSfOffsetNs),
+ mEarlyAppOffsetNs(earlyAppOffsetNs),
+ mEarlyGpuAppOffsetNs(earlyGpuAppOffsetNs),
+ mHighFpsVSyncPhaseOffsetNs(highFpsVsyncPhaseOffsetNs),
+ mHighFpsSfVSyncPhaseOffsetNs(highFpsSfVSyncPhaseOffsetNs),
+ mHighFpsEarlySfOffsetNs(highFpsEarlySfOffsetNs),
+ mHighFpsEarlyGpuSfOffsetNs(highFpsEarlyGpuSfOffsetNs),
+ mHighFpsEarlyAppOffsetNs(highFpsEarlyAppOffsetNs),
+ mHighFpsEarlyGpuAppOffsetNs(highFpsEarlyGpuAppOffsetNs),
+ mThresholdForNextVsync(thresholdForNextVsync) {
+ initializeOffsets(refreshRates);
+}
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
+ if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
+ return getHighFpsOffsets(vsyncDuration);
+ } else {
+ return getDefaultOffsets(vsyncDuration);
+ }
+}
+
+namespace {
+std::chrono::nanoseconds sfOffsetToDuration(nsecs_t sfOffset, nsecs_t vsyncDuration) {
+ return std::chrono::nanoseconds(vsyncDuration - sfOffset);
+}
+
+std::chrono::nanoseconds appOffsetToDuration(nsecs_t appOffset, nsecs_t sfOffset,
+ nsecs_t vsyncDuration) {
+ auto duration = vsyncDuration + (sfOffset - appOffset);
+ if (duration < vsyncDuration) {
+ duration += vsyncDuration;
+ }
+
+ return std::chrono::nanoseconds(duration);
+}
+} // namespace
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+ const auto earlySfOffset =
+ mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+ : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyAppOffset = mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
+ const auto earlyGpuSfOffset =
+ mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+ : mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyGpuAppOffset = mEarlyGpuAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
+ const auto lateSfOffset = mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+ ? mSfVSyncPhaseOffsetNs
+ : mSfVSyncPhaseOffsetNs - vsyncDuration;
+ const auto lateAppOffset = mVSyncPhaseOffsetNs;
+
+ return {
+ .early = {.sfOffset = earlySfOffset,
+ .appOffset = earlyAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(earlyAppOffset, earlySfOffset, vsyncDuration)},
+ .earlyGpu = {.sfOffset = earlyGpuSfOffset,
+ .appOffset = earlyGpuAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset, earlyGpuSfOffset,
+ vsyncDuration)},
+ .late = {.sfOffset = lateSfOffset,
+ .appOffset = lateAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration)},
+ };
+}
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+ const auto earlySfOffset =
+ mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+ ? mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
+ : mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyAppOffset = mHighFpsEarlyAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
+ const auto earlyGpuSfOffset = mHighFpsEarlyGpuSfOffsetNs.value_or(
+ mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
+ : mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyGpuAppOffset = mHighFpsEarlyGpuAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
+ const auto lateSfOffset = mHighFpsSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+ ? mHighFpsSfVSyncPhaseOffsetNs
+ : mHighFpsSfVSyncPhaseOffsetNs - vsyncDuration;
+ const auto lateAppOffset = mHighFpsVSyncPhaseOffsetNs;
+
+ return {
+ .early =
+ {
+ .sfOffset = earlySfOffset,
+ .appOffset = earlyAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyAppOffset, earlySfOffset,
+ vsyncDuration),
+ },
+ .earlyGpu =
+ {
+ .sfOffset = earlyGpuSfOffset,
+ .appOffset = earlyGpuAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset,
+ earlyGpuSfOffset, vsyncDuration),
+ },
+ .late =
+ {
+ .sfOffset = lateSfOffset,
+ .appOffset = lateAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration),
+ },
+ };
+}
+
+static void validateSysprops() {
+ const auto validatePropertyBool = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
+ };
+
+ validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ const auto validateProperty = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
+ "%s is set to %" PRId64 " but expecting duration", prop,
+ getProperty(prop).value_or(-1));
+ };
+
+ validateProperty("debug.sf.early_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_phase_offset_ns");
+ validateProperty("debug.sf.early_app_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+}
+
+namespace {
+nsecs_t sfDurationToOffset(std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
+ return vsyncDuration - sfDuration.count() % vsyncDuration;
+}
+
+nsecs_t appDurationToOffset(std::chrono::nanoseconds appDuration,
+ std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
+ return vsyncDuration - (appDuration + sfDuration).count() % vsyncDuration;
+}
+} // namespace
+
+WorkDuration::VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
+ const auto sfDurationFixup = [vsyncDuration](nsecs_t duration) {
+ return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) - 1ms
+ : std::chrono::nanoseconds(duration);
+ };
+
+ const auto appDurationFixup = [vsyncDuration](nsecs_t duration) {
+ return duration == -1 ? std::chrono::nanoseconds(vsyncDuration)
+ : std::chrono::nanoseconds(duration);
+ };
+
+ const auto sfEarlyDuration = sfDurationFixup(mSfEarlyDuration);
+ const auto appEarlyDuration = appDurationFixup(mAppEarlyDuration);
+ const auto sfEarlyGpuDuration = sfDurationFixup(mSfEarlyGpuDuration);
+ const auto appEarlyGpuDuration = appDurationFixup(mAppEarlyGpuDuration);
+ const auto sfDuration = sfDurationFixup(mSfDuration);
+ const auto appDuration = appDurationFixup(mAppDuration);
+
+ return {
+ .early =
+ {
+
+ .sfOffset = sfEarlyDuration.count() < vsyncDuration
+ ? sfDurationToOffset(sfEarlyDuration, vsyncDuration)
+ : sfDurationToOffset(sfEarlyDuration, vsyncDuration) -
+ vsyncDuration,
+
+ .appOffset = appDurationToOffset(appEarlyDuration, sfEarlyDuration,
+ vsyncDuration),
+
+ .sfWorkDuration = sfEarlyDuration,
+ .appWorkDuration = appEarlyDuration,
+ },
+ .earlyGpu =
+ {
+
+ .sfOffset = sfEarlyGpuDuration.count() < vsyncDuration
+
+ ? sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration)
+ : sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration) -
+ vsyncDuration,
+
+ .appOffset = appDurationToOffset(appEarlyGpuDuration,
+ sfEarlyGpuDuration, vsyncDuration),
+ .sfWorkDuration = sfEarlyGpuDuration,
+ .appWorkDuration = appEarlyGpuDuration,
+ },
+ .late =
+ {
+
+ .sfOffset = sfDuration.count() < vsyncDuration
+
+ ? sfDurationToOffset(sfDuration, vsyncDuration)
+ : sfDurationToOffset(sfDuration, vsyncDuration) - vsyncDuration,
+
+ .appOffset =
+ appDurationToOffset(appDuration, sfDuration, vsyncDuration),
+
+ .sfWorkDuration = sfDuration,
+ .appWorkDuration = appDuration,
+ },
+ };
+}
+
+WorkDuration::WorkDuration(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : WorkDuration(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().getFps(),
+ getProperty("debug.sf.late.sf.duration").value_or(-1),
+ getProperty("debug.sf.late.app.duration").value_or(-1),
+ getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
+ getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
+ validateSysprops();
+}
+
+WorkDuration::WorkDuration(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+ nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration,
+ nsecs_t appEarlyGpuDuration)
+ : VsyncConfiguration(currentFps),
+ mSfDuration(sfDuration),
+ mAppDuration(appDuration),
+ mSfEarlyDuration(sfEarlyDuration),
+ mAppEarlyDuration(appEarlyDuration),
+ mSfEarlyGpuDuration(sfEarlyGpuDuration),
+ mAppEarlyGpuDuration(appEarlyGpuDuration) {
+ initializeOffsets(refreshRates);
+}
+
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
new file mode 100644
index 0000000..c27a25d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unordered_map>
+
+#include "RefreshRateConfigs.h"
+#include "VsyncModulator.h"
+
+namespace android::scheduler {
+
+/*
+ * This class encapsulates vsync configurations for different refresh rates. Depending
+ * on what refresh rate we are using, and wheter we are composing in GL,
+ * different offsets will help us with latency. This class keeps track of
+ * which mode the device is on, and returns approprate offsets when needed.
+ */
+class VsyncConfiguration {
+public:
+ using VsyncConfigSet = VsyncModulator::VsyncConfigSet;
+
+ virtual ~VsyncConfiguration() = default;
+ virtual VsyncConfigSet getCurrentConfigs() const = 0;
+ virtual VsyncConfigSet getConfigsForRefreshRate(float fps) const = 0;
+
+ virtual void setRefreshRateFps(float fps) = 0;
+
+ virtual void dump(std::string& result) const = 0;
+};
+
+namespace impl {
+
+/*
+ * This is a common implementation for both phase offsets and durations.
+ * PhaseOffsets and WorkDuration derive from this class and implement the
+ * constructOffsets method
+ */
+class VsyncConfiguration : public scheduler::VsyncConfiguration {
+public:
+ explicit VsyncConfiguration(float currentFps);
+
+ // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
+ VsyncConfigSet getConfigsForRefreshRate(float fps) const override;
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ VsyncConfigSet getCurrentConfigs() const override {
+ return getConfigsForRefreshRate(mRefreshRateFps);
+ }
+
+ // This function should be called when the device is switching between different
+ // refresh rates, to properly update the offsets.
+ void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& result) const override;
+
+protected:
+ void initializeOffsets(const std::vector<float>& refreshRates);
+ virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
+
+ std::unordered_map<float, VsyncConfigSet> mOffsets;
+ std::atomic<float> mRefreshRateFps;
+};
+
+/*
+ * This is the old implementation of phase offsets and considered as deprecated.
+ * WorkDuration is the new implementation.
+ */
+class PhaseOffsets : public VsyncConfiguration {
+public:
+ explicit PhaseOffsets(const scheduler::RefreshRateConfigs&);
+
+protected:
+ // Used for unit tests
+ PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
+ nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync);
+
+private:
+ VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+
+ VsyncConfigSet getDefaultOffsets(nsecs_t vsyncPeriod) const;
+ VsyncConfigSet getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+
+ const nsecs_t mVSyncPhaseOffsetNs;
+ const nsecs_t mSfVSyncPhaseOffsetNs;
+ const std::optional<nsecs_t> mEarlySfOffsetNs;
+ const std::optional<nsecs_t> mEarlyGpuSfOffsetNs;
+ const std::optional<nsecs_t> mEarlyAppOffsetNs;
+ const std::optional<nsecs_t> mEarlyGpuAppOffsetNs;
+
+ const nsecs_t mHighFpsVSyncPhaseOffsetNs;
+ const nsecs_t mHighFpsSfVSyncPhaseOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlySfOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyGpuSfOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyAppOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyGpuAppOffsetNs;
+
+ const nsecs_t mThresholdForNextVsync;
+};
+
+/*
+ * Class that encapsulates the phase offsets for SurfaceFlinger and App.
+ * The offsets are calculated from durations for each one of the (late, early, earlyGpu)
+ * offset types.
+ */
+class WorkDuration : public VsyncConfiguration {
+public:
+ explicit WorkDuration(const scheduler::RefreshRateConfigs&);
+
+protected:
+ // Used for unit tests
+ WorkDuration(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
+ nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
+
+private:
+ VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+
+ const nsecs_t mSfDuration;
+ const nsecs_t mAppDuration;
+
+ const nsecs_t mSfEarlyDuration;
+ const nsecs_t mAppEarlyDuration;
+
+ const nsecs_t mSfEarlyGpuDuration;
+ const nsecs_t mAppEarlyGpuDuration;
+};
+
+} // namespace impl
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
new file mode 100644
index 0000000..1f821be
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#undef LOG_TAG
+#define LOG_TAG "VsyncModulator"
+
+#include "VsyncModulator.h"
+
+#include <android-base/properties.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <chrono>
+#include <cinttypes>
+#include <mutex>
+
+using namespace std::chrono_literals;
+
+namespace android::scheduler {
+
+const std::chrono::nanoseconds VsyncModulator::MIN_EARLY_TRANSACTION_TIME = 1ms;
+
+VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now)
+ : mVsyncConfigSet(config),
+ mNow(now),
+ mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
+
+VsyncModulator::VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mVsyncConfigSet = config;
+ return updateVsyncConfigLocked();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(
+ TransactionSchedule schedule) {
+ switch (schedule) {
+ case Schedule::EarlyStart:
+ ALOGW_IF(mExplicitEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
+ mExplicitEarlyWakeup = true;
+ break;
+ case Schedule::EarlyEnd:
+ ALOGW_IF(!mExplicitEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__);
+ mExplicitEarlyWakeup = false;
+ break;
+ case Schedule::Early:
+ case Schedule::Late:
+ // No change to mExplicitEarlyWakeup for non-explicit states.
+ break;
+ }
+
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
+ }
+
+ if (!mExplicitEarlyWakeup && (schedule == Schedule::Early || schedule == Schedule::EarlyEnd)) {
+ mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
+ mEarlyTransactionStartTime = mNow();
+ }
+
+ // An early transaction stays an early transaction.
+ if (schedule == mTransactionSchedule || mTransactionSchedule == Schedule::EarlyEnd) {
+ return std::nullopt;
+ }
+ mTransactionSchedule = schedule;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onTransactionCommit() {
+ mLastTransactionCommitTime = mNow();
+ if (mTransactionSchedule == Schedule::Late) return std::nullopt;
+ mTransactionSchedule = Schedule::Late;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeInitiated() {
+ if (mRefreshRateChangePending) return std::nullopt;
+ mRefreshRateChangePending = true;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeCompleted() {
+ if (!mRefreshRateChangePending) return std::nullopt;
+ mRefreshRateChangePending = false;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onDisplayRefresh(bool usedGpuComposition) {
+ bool updateOffsetsNeeded = false;
+
+ if (mEarlyTransactionStartTime.load() + MIN_EARLY_TRANSACTION_TIME <=
+ mLastTransactionCommitTime.load()) {
+ if (mEarlyTransactionFrames > 0) {
+ mEarlyTransactionFrames--;
+ updateOffsetsNeeded = true;
+ }
+ }
+ if (usedGpuComposition) {
+ mEarlyGpuFrames = MIN_EARLY_GPU_FRAMES;
+ updateOffsetsNeeded = true;
+ } else if (mEarlyGpuFrames > 0) {
+ mEarlyGpuFrames--;
+ updateOffsetsNeeded = true;
+ }
+
+ if (!updateOffsetsNeeded) return std::nullopt;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::getVsyncConfig() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mVsyncConfig;
+}
+
+const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
+ // Early offsets are used if we're in the middle of a refresh rate
+ // change, or if we recently begin a transaction.
+ if (mExplicitEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd ||
+ mEarlyTransactionFrames > 0 || mRefreshRateChangePending) {
+ return mVsyncConfigSet.early;
+ } else if (mEarlyGpuFrames > 0) {
+ return mVsyncConfigSet.earlyGpu;
+ } else {
+ return mVsyncConfigSet.late;
+ }
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfig() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return updateVsyncConfigLocked();
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
+ const VsyncConfig& offsets = getNextVsyncConfig();
+ mVsyncConfig = offsets;
+
+ if (mTraceDetailedInfo) {
+ const bool isEarly = &offsets == &mVsyncConfigSet.early;
+ const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu;
+ const bool isLate = &offsets == &mVsyncConfigSet.late;
+
+ ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
+ ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu);
+ ATRACE_INT("Vsync-LateOffsetsOn", isLate);
+ }
+
+ return offsets;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
new file mode 100644
index 0000000..355a14a
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <mutex>
+#include <optional>
+
+#include <android-base/thread_annotations.h>
+#include <utils/Timers.h>
+
+namespace android::scheduler {
+
+// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
+// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
+// fixed number of frames, respectively.
+enum class TransactionSchedule {
+ Late, // Default.
+ Early, // Deprecated.
+ EarlyStart,
+ EarlyEnd
+};
+
+// Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
+class VsyncModulator {
+public:
+ // Number of frames to keep early offsets after an early transaction or GPU composition.
+ // This acts as a low-pass filter in case subsequent transactions are delayed, or if the
+ // composition strategy alternates on subsequent frames.
+ static constexpr int MIN_EARLY_TRANSACTION_FRAMES = 2;
+ static constexpr int MIN_EARLY_GPU_FRAMES = 2;
+
+ // Duration to delay the MIN_EARLY_TRANSACTION_FRAMES countdown after an early transaction.
+ // This may keep early offsets for an extra frame, but avoids a race with transaction commit.
+ static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;
+
+ // Phase offsets and work durations for SF and app deadlines from VSYNC.
+ struct VsyncConfig {
+ nsecs_t sfOffset;
+ nsecs_t appOffset;
+ std::chrono::nanoseconds sfWorkDuration;
+ std::chrono::nanoseconds appWorkDuration;
+
+ bool operator==(const VsyncConfig& other) const {
+ return sfOffset == other.sfOffset && appOffset == other.appOffset &&
+ sfWorkDuration == other.sfWorkDuration &&
+ appWorkDuration == other.appWorkDuration;
+ }
+
+ bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
+ };
+
+ using VsyncConfigOpt = std::optional<VsyncConfig>;
+
+ struct VsyncConfigSet {
+ VsyncConfig early; // Used for early transactions, and during refresh rate change.
+ VsyncConfig earlyGpu; // Used during GPU composition.
+ VsyncConfig late; // Default.
+
+ bool operator==(const VsyncConfigSet& other) const {
+ return early == other.early && earlyGpu == other.earlyGpu && late == other.late;
+ }
+
+ bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
+ };
+
+ using Clock = std::chrono::steady_clock;
+ using TimePoint = Clock::time_point;
+ using Now = TimePoint (*)();
+
+ explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now);
+
+ VsyncConfig getVsyncConfig() const EXCLUDES(mMutex);
+
+ [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);
+
+ // Changes offsets in response to transaction flags or commit.
+ [[nodiscard]] VsyncConfigOpt setTransactionSchedule(TransactionSchedule);
+ [[nodiscard]] VsyncConfigOpt onTransactionCommit();
+
+ // Called when we send a refresh rate change to hardware composer, so that
+ // we can move into early offsets.
+ [[nodiscard]] VsyncConfigOpt onRefreshRateChangeInitiated();
+
+ // Called when we detect from VSYNC signals that the refresh rate changed.
+ // This way we can move out of early offsets if no longer necessary.
+ [[nodiscard]] VsyncConfigOpt onRefreshRateChangeCompleted();
+
+ [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
+
+private:
+ const VsyncConfig& getNextVsyncConfig() const REQUIRES(mMutex);
+ [[nodiscard]] VsyncConfig updateVsyncConfig() EXCLUDES(mMutex);
+ [[nodiscard]] VsyncConfig updateVsyncConfigLocked() REQUIRES(mMutex);
+
+ mutable std::mutex mMutex;
+ VsyncConfigSet mVsyncConfigSet GUARDED_BY(mMutex);
+
+ VsyncConfig mVsyncConfig GUARDED_BY(mMutex){mVsyncConfigSet.late};
+
+ using Schedule = TransactionSchedule;
+ std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
+ std::atomic<bool> mExplicitEarlyWakeup = false;
+
+ std::atomic<bool> mRefreshRateChangePending = false;
+
+ std::atomic<int> mEarlyTransactionFrames = 0;
+ std::atomic<int> mEarlyGpuFrames = 0;
+ std::atomic<TimePoint> mEarlyTransactionStartTime = TimePoint();
+ std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint();
+
+ const Now mNow;
+ const bool mTraceDetailedInfo;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bfd8438..f6028d5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -28,8 +28,10 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/1.0/IPower.h>
+#include <android/hardware/power/Boost.h>
#include <android/native_window.h>
+#include <android/os/BnSetInputWindowsListener.h>
+#include <android/os/IInputFlinger.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
@@ -58,7 +60,6 @@
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <hidl/ServiceManagement.h>
-#include <input/IInputFlinger.h>
#include <layerproto/LayerProtoParser.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -89,6 +90,7 @@
#include <functional>
#include <mutex>
#include <optional>
+#include <type_traits>
#include <unordered_map>
#include "BufferLayer.h"
@@ -103,10 +105,12 @@
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
+#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Effects/Daltonizer.h"
#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
+#include "LayerRenderArea.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "NativeWindowSurface.h"
@@ -115,12 +119,11 @@
#include "RegionSamplingThread.h"
#include "Scheduler/DispSync.h"
#include "Scheduler/DispSyncSource.h"
-#include "Scheduler/EventControlThread.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
+#include "Scheduler/VsyncConfiguration.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerProperties.h"
#include "SurfaceInterceptor.h"
@@ -149,7 +152,7 @@
using namespace android::hardware::configstore::V1_0;
using namespace android::sysprop;
-using android::hardware::power::V1_0::PowerHint;
+using android::hardware::power::Boost;
using base::StringAppendF;
using ui::ColorMode;
using ui::Dataspace;
@@ -254,6 +257,21 @@
} // namespace anonymous
+struct SetInputWindowsListener : os::BnSetInputWindowsListener {
+ explicit SetInputWindowsListener(std::function<void()> listenerCb) : mListenerCb(listenerCb) {}
+
+ binder::Status onSetInputWindowsFinished() override;
+
+ std::function<void()> mListenerCb;
+};
+
+binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
+ if (mListenerCb != nullptr) {
+ mListenerCb();
+ }
+ return binder::Status::ok();
+}
+
// ---------------------------------------------------------------------------
const String16 sHardwareTest("android.permission.HARDWARE_TEST");
@@ -319,7 +337,9 @@
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
- mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {}
+ mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {
+ mSetInputWindowsListener = new SetInputWindowsListener([&]() { setInputWindowsFinished(); });
+}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
ALOGI("SurfaceFlinger is starting");
@@ -397,10 +417,6 @@
int debugDdms = atoi(value);
ALOGI_IF(debugDdms, "DDMS debugging not supported");
- property_get("debug.sf.disable_backpressure", value, "0");
- mPropagateBackpressure = !atoi(value);
- ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation");
-
property_get("debug.sf.enable_gl_backpressure", value, "0");
mPropagateBackpressureClientComposition = atoi(value);
ALOGI_IF(mPropagateBackpressureClientComposition,
@@ -424,6 +440,10 @@
const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS;
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
+ mGraphicBufferProducerListSizeLogThreshold =
+ std::max(static_cast<int>(0.95 *
+ static_cast<double>(mMaxGraphicBufferProducerListSize)),
+ 1);
property_get("debug.sf.luma_sampling", value, "1");
mLumaSampling = atoi(value);
@@ -449,6 +469,8 @@
mKernelIdleTimerEnabled = mSupportKernelIdleTimer = sysprop::support_kernel_idle_timer(false);
base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
+
+ mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
}
SurfaceFlinger::~SurfaceFlinger() = default;
@@ -542,11 +564,11 @@
std::vector<PhysicalDisplayId> displayIds;
displayIds.reserve(mPhysicalDisplayTokens.size());
- displayIds.push_back(internalDisplayId->value);
+ displayIds.push_back(*internalDisplayId);
for (const auto& [id, token] : mPhysicalDisplayTokens) {
if (id != *internalDisplayId) {
- displayIds.push_back(id.value);
+ displayIds.push_back(id);
}
}
@@ -555,7 +577,7 @@
sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
Mutex::Autolock lock(mStateLock);
- return getPhysicalDisplayTokenLocked(DisplayId{displayId});
+ return getPhysicalDisplayTokenLocked(displayId);
}
status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const {
@@ -601,13 +623,6 @@
if (mWindowManager != 0) {
mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
}
- sp<IBinder> input(defaultServiceManager()->getService(
- String16("inputflinger")));
- if (input == nullptr) {
- ALOGE("Failed to link to input service");
- } else {
- mInputFlinger = interface_cast<IInputFlinger>(input);
- }
if (mVrFlinger) {
mVrFlinger->OnBootFinished();
@@ -622,7 +637,15 @@
LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
- static_cast<void>(schedule([this] {
+ sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
+
+ static_cast<void>(schedule([=] {
+ if (input == nullptr) {
+ ALOGE("Failed to link to input service");
+ } else {
+ mInputFlinger = interface_cast<os::IInputFlinger>(input);
+ }
+
readPersistentProperties();
mPowerAdvisor.onBootFinished();
mBootStage = BootStage::FINISHED;
@@ -714,10 +737,11 @@
signalTransaction();
}));
};
- mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(),
- getHwComposer()
- .fromPhysicalDisplayId(*display->getId())
- .value_or(0),
+ auto hwcDisplayId =
+ getHwComposer()
+ .fromPhysicalDisplayId(static_cast<PhysicalDisplayId>(*display->getId()))
+ .value_or(0);
+ mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(), hwcDisplayId,
vrFlingerRequestDisplayCallback);
if (!mVrFlinger) {
ALOGE("Failed to start vrflinger");
@@ -838,8 +862,9 @@
state->layerStack = display->getLayerStack();
state->orientation = display->getOrientation();
- const Rect viewport = display->getViewport();
- state->viewport = viewport.isValid() ? viewport.getSize() : display->getSize();
+ const Rect layerStackRect = display->getLayerStackSpaceRect();
+ state->layerStackSpaceRect =
+ layerStackRect.isValid() ? layerStackRect.getSize() : display->getSize();
return NO_ERROR;
}
@@ -872,7 +897,7 @@
info->density /= ACONFIGURATION_DENSITY_MEDIUM;
info->secure = display->isSecure();
- info->deviceProductInfo = getDeviceProductInfoLocked(*display);
+ info->deviceProductInfo = display->getDeviceProductInfo();
return NO_ERROR;
}
@@ -923,9 +948,10 @@
const nsecs_t period = hwConfig->getVsyncPeriod();
config.refreshRate = 1e9f / period;
- const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
- config.appVsyncOffset = offsets.late.app;
- config.sfVsyncOffset = offsets.late.sf;
+ const auto vsyncConfigSet =
+ mVsyncConfiguration->getConfigsForRefreshRate(config.refreshRate);
+ config.appVsyncOffset = vsyncConfigSet.late.appOffset;
+ config.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
config.configGroup = hwConfig->getConfigGroup();
// This is how far in advance a buffer must be queued for
@@ -1012,10 +1038,9 @@
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// DispSync model is locked.
- mVSyncModulator->onRefreshRateChangeInitiated();
+ modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ updatePhaseConfiguration(refreshRate);
mScheduler->setConfigChangePending(true);
}
@@ -1074,15 +1099,15 @@
if (refreshRate.getVsyncPeriod() != oldRefreshRate.getVsyncPeriod()) {
mTimeStats->incrementRefreshRateSwitches();
}
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ updatePhaseConfiguration(refreshRate);
ATRACE_INT("ActiveConfigFPS", refreshRate.getFps());
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId)
.getVsyncPeriod();
- mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle,
+ static_cast<PhysicalDisplayId>(*display->getId()),
mUpcomingActiveConfig.configId, vsyncPeriod);
}
}
@@ -1094,9 +1119,9 @@
const auto& refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
+
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ updatePhaseConfiguration(refreshRate);
mScheduler->setConfigChangePending(false);
}
@@ -1339,30 +1364,6 @@
return NO_ERROR;
}
-std::optional<DeviceProductInfo> SurfaceFlinger::getDeviceProductInfoLocked(
- const DisplayDevice& display) const {
- // TODO(b/149075047): Populate DeviceProductInfo on hotplug and store it in DisplayDevice to
- // avoid repetitive HAL IPC and EDID parsing.
- const auto displayId = display.getId();
- LOG_FATAL_IF(!displayId);
-
- const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
- LOG_FATAL_IF(!hwcDisplayId);
-
- uint8_t port;
- DisplayIdentificationData data;
- if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
- ALOGV("%s: No identification data.", __FUNCTION__);
- return {};
- }
-
- const auto info = parseDisplayIdentificationData(port, data);
- if (!info) {
- return {};
- }
- return info->deviceProductInfo;
-}
-
status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
@@ -1530,10 +1531,10 @@
.get();
}
-status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) {
- PowerHint powerHint = static_cast<PowerHint>(hintId);
+status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
+ Boost powerBoost = static_cast<Boost>(boostId);
- if (powerHint == PowerHint::INTERACTION) {
+ if (powerBoost == Boost::INTERACTION) {
mScheduler->notifyTouchEvent();
}
@@ -1599,7 +1600,7 @@
bool periodFlushed = false;
mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
if (periodFlushed) {
- mVSyncModulator->onRefreshRateChangeCompleted();
+ modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
}
}
@@ -1679,25 +1680,18 @@
repaintEverythingForHWC();
}
-void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
+void SurfaceFlinger::setVsyncEnabled(bool enabled) {
ATRACE_CALL();
- // Enable / Disable HWVsync from the main thread to avoid race conditions with
- // display power state.
- static_cast<void>(schedule([=]() MAIN_THREAD { setPrimaryVsyncEnabledInternal(enabled); }));
-}
+ // On main thread to avoid race conditions with display power state.
+ static_cast<void>(schedule([=]() MAIN_THREAD {
+ mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
-void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) {
- ATRACE_CALL();
-
- mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
-
- if (const auto displayId = getInternalDisplayIdLocked()) {
- sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
- if (display && display->isPoweredOn()) {
- getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
+ if (const auto display = getDefaultDisplayDeviceLocked();
+ display && display->isPoweredOn()) {
+ getHwComposer().setVsyncEnabled(*display->getId(), mHWCVsyncPendingState);
}
- }
+ }));
}
void SurfaceFlinger::resetDisplayState() {
@@ -1796,8 +1790,8 @@
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
}
bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
@@ -1829,7 +1823,8 @@
mScheduler->getDisplayStatInfo(&stats);
const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(now);
// Inflate the expected present time if we're targetting the next vsync.
- return mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod;
+ return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? presentTime
+ : presentTime + stats.vsyncPeriod;
}
void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
@@ -1860,10 +1855,7 @@
// for the present fence to fire instead of just giving up on this frame to handle cases
// where present fence is just about to get signaled.
const int graceTimeForPresentFenceMs =
- (mPropagateBackpressure &&
- (mPropagateBackpressureClientComposition || !mHadClientComposition))
- ? 1
- : 0;
+ (mPropagateBackpressureClientComposition || !mHadClientComposition) ? 1 : 0;
// Pending frames may trigger backpressure propagation.
const TracedOrdinal<bool> framePending = {"PrevFramePending",
@@ -1922,7 +1914,7 @@
ON_MAIN_THREAD(setActiveConfigInternal());
}
- if (framePending && mPropagateBackpressure) {
+ if (framePending) {
if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
signalLayerUpdate();
return;
@@ -1970,6 +1962,12 @@
mTracingEnabledChanged = false;
}
+ if (mRefreshRateOverlaySpinner) {
+ if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
+ mRefreshRateOverlay->onInvalidate();
+ }
+ }
+
bool refreshNeeded;
{
ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
@@ -2123,7 +2121,8 @@
}
// TODO: b/160583065 Enable skip validation when SF caches all client composition layers
- mVSyncModulator->onRefreshed(mHadClientComposition || mReusedClientComposition);
+ const bool usedGpuComposition = mHadClientComposition || mReusedClientComposition;
+ modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
mLayersWithQueuedFrames.clear();
if (mVisibleRegionsDirty) {
@@ -2185,12 +2184,12 @@
nsecs_t compositeToPresentLatency) {
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
- nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+ nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0)
? (stats.vsyncPeriod -
- (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
- : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
+ (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % stats.vsyncPeriod))
+ : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % stats.vsyncPeriod);
- // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
+ // Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval.
if (idealLatency <= 0) {
idealLatency = stats.vsyncPeriod;
}
@@ -2200,7 +2199,7 @@
// Reducing jitter is important if an app attempts to extrapolate
// something (such as user input) to an accurate diasplay time.
// Snapping also allows an app to precisely calculate
- // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
+ // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval).
nsecs_t bias = stats.vsyncPeriod / 2;
int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
nsecs_t snappedCompositeToPresentLatency =
@@ -2371,7 +2370,7 @@
}
FloatRect SurfaceFlinger::getLayerClipBoundsForDisplay(const DisplayDevice& displayDevice) const {
- return displayDevice.getViewport().toFloatRect();
+ return displayDevice.getLayerStackSpaceRect().toFloatRect();
}
void SurfaceFlinger::computeLayerBounds() {
@@ -2419,7 +2418,7 @@
// with mStateLock held to guarantee that mCurrentState won't change
// until the transaction is committed.
- mVSyncModulator->onTransactionHandled();
+ modulateVsync(&VsyncModulator::onTransactionCommit);
transactionFlags = getTransactionFlags(eTransactionMask);
handleTransactionLocked(transactionFlags);
@@ -2437,7 +2436,7 @@
continue;
}
- const DisplayId displayId = info->id;
+ const auto displayId = info->id;
const auto it = mPhysicalDisplayTokens.find(displayId);
if (event.connection == hal::Connection::CONNECTED) {
@@ -2449,8 +2448,10 @@
}
DisplayDeviceState state;
- state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId),
- event.hwcDisplayId};
+ state.physical = {.id = displayId,
+ .type = getHwComposer().getDisplayConnectionType(displayId),
+ .hwcDisplayId = event.hwcDisplayId,
+ .deviceProductInfo = info->deviceProductInfo};
state.isSecure = true; // All physical displays are currently considered secure.
state.displayName = info->name;
@@ -2566,10 +2567,12 @@
LOG_ALWAYS_FATAL_IF(!displayId);
auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
display->setActiveConfig(activeConfigId);
+ display->setDeviceProductInfo(state.physical->deviceProductInfo);
}
display->setLayerStack(state.layerStack);
- display->setProjection(state.orientation, state.viewport, state.frame);
+ display->setProjection(state.orientation, state.layerStackSpaceRect,
+ state.orientedDisplaySpaceRect);
display->setDisplayName(state.displayName);
return display;
@@ -2648,7 +2651,7 @@
mDisplays.emplace(displayToken, display);
if (!state.isVirtual()) {
LOG_FATAL_IF(!displayId);
- dispatchDisplayHotplugEvent(displayId->value, true);
+ dispatchDisplayHotplugEvent(static_cast<PhysicalDisplayId>(*displayId), true);
}
if (display->isPrimary()) {
@@ -2664,7 +2667,7 @@
if (!display->isVirtual()) {
LOG_FATAL_IF(!displayId);
- dispatchDisplayHotplugEvent(displayId->value, false);
+ dispatchDisplayHotplugEvent(static_cast<PhysicalDisplayId>(*displayId), false);
}
}
@@ -2698,10 +2701,10 @@
display->setLayerStack(currentState.layerStack);
}
if ((currentState.orientation != drawingState.orientation) ||
- (currentState.viewport != drawingState.viewport) ||
- (currentState.frame != drawingState.frame)) {
- display->setProjection(currentState.orientation, currentState.viewport,
- currentState.frame);
+ (currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) ||
+ (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
+ display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
+ currentState.orientedDisplaySpaceRect);
}
if (currentState.width != drawingState.width ||
currentState.height != drawingState.height) {
@@ -2913,19 +2916,23 @@
}
void SurfaceFlinger::updateInputWindowInfo() {
- std::vector<InputWindowInfo> inputHandles;
+ std::vector<InputWindowInfo> inputInfos;
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (layer->needsInputInfo()) {
// When calculating the screen bounds we ignore the transparent region since it may
// result in an unwanted offset.
- inputHandles.push_back(layer->fillInputInfo());
+ inputInfos.push_back(layer->fillInputInfo());
}
});
- mInputFlinger->setInputWindows(inputHandles,
- mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
- : nullptr);
+ mInputFlinger->setInputWindows(inputInfos,
+ mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
+ : nullptr);
+ for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
+ mInputFlinger->setFocusedWindow(focusRequest);
+ }
+ mInputWindowCommands.focusRequests.clear();
}
void SurfaceFlinger::commitInputWindowCommands() {
@@ -2954,7 +2961,7 @@
changeRefreshRateLocked(refreshRate, event);
}
-void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
+void SurfaceFlinger::initScheduler(PhysicalDisplayId primaryDisplayId) {
if (mScheduler) {
// In practice it's not allowed to hotplug in/out the primary display once it's been
// connected during startup, but some tests do it, so just warn and return.
@@ -2972,24 +2979,23 @@
currentConfig, hal::PowerMode::OFF);
mRefreshRateStats->setConfigMode(currentConfig);
- mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+ mVsyncConfiguration = getFactory().createVsyncConfiguration(*mRefreshRateConfigs);
+ mVsyncModulator.emplace(mVsyncConfiguration->getCurrentConfigs());
// start the EventThread
- mScheduler =
- getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
- *mRefreshRateConfigs, *this);
+ mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
mAppConnectionHandle =
- mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
+ mScheduler->createConnection("app",
+ mVsyncConfiguration->getCurrentConfigs().late.appOffset,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
+ mScheduler->createConnection("sf",
+ mVsyncConfiguration->getCurrentConfigs().late.sfOffset,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
- mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
- mPhaseConfiguration->getCurrentOffsets());
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
@@ -3003,12 +3009,26 @@
// anyway since there are no connected apps at this point.
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
- mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId.value,
- currentConfig, vsyncPeriod);
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId, currentConfig,
+ vsyncPeriod);
+ static auto ignorePresentFences =
+ base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false);
+ mScheduler->setIgnorePresentFences(
+ ignorePresentFences ||
+ getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE));
}
-void SurfaceFlinger::commitTransaction()
-{
+void SurfaceFlinger::updatePhaseConfiguration(const RefreshRate& refreshRate) {
+ mVsyncConfiguration->setRefreshRateFps(refreshRate.getFps());
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()));
+}
+
+void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config) {
+ mScheduler->setPhaseOffset(mAppConnectionHandle, config.appOffset);
+ mScheduler->setPhaseOffset(mSfConnectionHandle, config.sfOffset);
+}
+
+void SurfaceFlinger::commitTransaction() {
commitTransactionLocked();
mTransactionPending = false;
mAnimTransactionPending = false;
@@ -3044,15 +3064,19 @@
// clear the "changed" flags in current state
mCurrentState.colorMatrixChanged = false;
- mDrawingState.traverse([&](Layer* layer) {
- layer->commitChildList();
-
- // If the layer can be reached when traversing mDrawingState, then the layer is no
- // longer offscreen. Remove the layer from the offscreenLayer set.
- if (mOffscreenLayers.count(layer)) {
- mOffscreenLayers.erase(layer);
- }
- });
+ for (const auto& rootLayer : mDrawingState.layersSortedByZ) {
+ rootLayer->commitChildList();
+ }
+ // TODO(b/163019109): See if this traversal is needed at all...
+ if (!mOffscreenLayers.empty()) {
+ mDrawingState.traverse([&](Layer* layer) {
+ // If the layer can be reached when traversing mDrawingState, then the layer is no
+ // longer offscreen. Remove the layer from the offscreenLayer set.
+ if (mOffscreenLayers.count(layer)) {
+ mOffscreenLayers.erase(layer);
+ }
+ });
+ }
commitOffscreenLayers();
mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
@@ -3209,6 +3233,11 @@
"Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
mGraphicBufferProducerList.size(),
mMaxGraphicBufferProducerListSize, mNumLayers.load());
+ if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) {
+ ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
+ mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
+ mNumLayers.load());
+ }
}
if (const auto display = getDefaultDisplayDeviceLocked()) {
@@ -3243,16 +3272,13 @@
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
- return setTransactionFlags(flags, Scheduler::TransactionStart::Normal);
+ return setTransactionFlags(flags, TransactionSchedule::Late);
}
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
- Scheduler::TransactionStart transactionStart) {
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule) {
uint32_t old = mTransactionFlags.fetch_or(flags);
- mVSyncModulator->setTransactionStart(transactionStart);
- if ((old & flags)==0) { // wake the server up
- signalTransaction();
- }
+ modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
+ if ((old & flags) == 0) signalTransaction();
return old;
}
@@ -3285,7 +3311,8 @@
mPendingInputWindowCommands, transaction.desiredPresentTime,
transaction.buffer, transaction.postTime,
transaction.privileged, transaction.hasListenerCallbacks,
- transaction.listenerCallbacks, /*isMainThread*/ true);
+ transaction.listenerCallbacks, transaction.originPID,
+ transaction.originUID, /*isMainThread*/ true);
transactionQueue.pop();
flushedATransaction = true;
}
@@ -3365,17 +3392,22 @@
mExpectedPresentTime = calculateExpectedPresentTime(systemTime());
}
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int originPID = ipc->getCallingPid();
+ const int originUID = ipc->getCallingUid();
+
if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
uncacheBuffer, postTime, privileged,
- hasListenerCallbacks, listenerCallbacks);
+ hasListenerCallbacks, listenerCallbacks, originPID,
+ originUID);
setTransactionFlags(eTransactionFlushNeeded);
return;
}
applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
uncacheBuffer, postTime, privileged, hasListenerCallbacks,
- listenerCallbacks);
+ listenerCallbacks, originPID, originUID, /*isMainThread*/ false);
}
void SurfaceFlinger::applyTransactionState(
@@ -3383,7 +3415,7 @@
const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- bool isMainThread) {
+ int originPID, int originUID, bool isMainThread) {
uint32_t transactionFlags = 0;
if (flags & eAnimation) {
@@ -3460,22 +3492,17 @@
mForceTraversal = true;
}
- const auto transactionStart = [](uint32_t flags) {
- if (flags & eEarlyWakeup) {
- return Scheduler::TransactionStart::Early;
- }
- if (flags & eExplicitEarlyWakeupEnd) {
- return Scheduler::TransactionStart::EarlyEnd;
- }
- if (flags & eExplicitEarlyWakeupStart) {
- return Scheduler::TransactionStart::EarlyStart;
- }
- return Scheduler::TransactionStart::Normal;
+ const auto schedule = [](uint32_t flags) {
+ if (flags & eEarlyWakeup) return TransactionSchedule::Early;
+ if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+ if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+ return TransactionSchedule::Late;
}(flags);
if (transactionFlags) {
if (mInterceptor->isEnabled()) {
- mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
+ mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
+ originPID, originUID);
}
// TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
@@ -3489,7 +3516,7 @@
}
// this triggers the transaction
- setTransactionFlags(transactionFlags, transactionStart);
+ setTransactionFlags(transactionFlags, schedule);
if (flags & eAnimation) {
mAnimTransactionPending = true;
@@ -3527,11 +3554,10 @@
}
}
} else {
- // even if a transaction is not needed, we need to update VsyncModulator
- // about explicit early indications
- if (transactionStart == Scheduler::TransactionStart::EarlyStart ||
- transactionStart == Scheduler::TransactionStart::EarlyEnd) {
- mVSyncModulator->setTransactionStart(transactionStart);
+ // Update VsyncModulator state machine even if transaction is not needed.
+ if (schedule == TransactionSchedule::EarlyStart ||
+ schedule == TransactionSchedule::EarlyEnd) {
+ modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
}
}
}
@@ -3561,12 +3587,12 @@
state.orientation = s.orientation;
flags |= eDisplayTransactionNeeded;
}
- if (state.frame != s.frame) {
- state.frame = s.frame;
+ if (state.orientedDisplaySpaceRect != s.orientedDisplaySpaceRect) {
+ state.orientedDisplaySpaceRect = s.orientedDisplaySpaceRect;
flags |= eDisplayTransactionNeeded;
}
- if (state.viewport != s.viewport) {
- state.viewport = s.viewport;
+ if (state.layerStackSpaceRect != s.layerStackSpaceRect) {
+ state.layerStackSpaceRect = s.layerStackSpaceRect;
flags |= eDisplayTransactionNeeded;
}
}
@@ -3801,7 +3827,7 @@
if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eFrameChanged) {
- if (layer->setFrame(s.frame)) flags |= eTraversalNeeded;
+ if (layer->setFrame(s.orientedDisplaySpaceRect)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eAcquireFenceChanged) {
if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
@@ -3823,7 +3849,7 @@
}
if (what & layer_state_t::eInputInfoChanged) {
if (privileged) {
- layer->setInputInfo(s.inputInfo);
+ layer->setInputInfo(*s.inputHandle->getInfo());
flags |= eTraversalNeeded;
} else {
ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
@@ -3911,13 +3937,8 @@
}
uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
- uint32_t flags = 0;
- if (inputWindowCommands.syncInputWindows) {
- flags |= eTraversalNeeded;
- }
-
- mPendingInputWindowCommands.merge(inputWindowCommands);
- return flags;
+ const bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands);
+ return hasChanges ? eTraversalNeeded : 0;
}
status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
@@ -3979,7 +4000,12 @@
if (metadata.has(METADATA_WINDOW_TYPE)) {
int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
if (windowType == 441731) {
- metadata.setInt32(METADATA_WINDOW_TYPE, InputWindowInfo::TYPE_NAVIGATION_BAR_PANEL);
+ using U = std::underlying_type_t<InputWindowInfo::Type>;
+ // TODO(b/129481165): This static assert can be safely removed once conversion warnings
+ // are re-enabled.
+ static_assert(std::is_same_v<U, int32_t>);
+ metadata.setInt32(METADATA_WINDOW_TYPE,
+ static_cast<U>(InputWindowInfo::Type::NAVIGATION_BAR_PANEL));
primaryDisplayOnly = true;
}
}
@@ -4184,8 +4210,8 @@
d.token = token;
d.layerStack = 0;
d.orientation = ui::ROTATION_0;
- d.frame.makeInvalid();
- d.viewport.makeInvalid();
+ d.orientedDisplaySpaceRect.makeInvalid();
+ d.layerStackSpaceRect.makeInvalid();
d.width = 0;
d.height = 0;
displays.add(d);
@@ -4439,7 +4465,7 @@
mRefreshRateStats->dump(result);
result.append("\n");
- mPhaseConfiguration->dump(result);
+ mVsyncConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
@@ -4547,7 +4573,8 @@
if (!displayId) {
continue;
}
- const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
+ const auto hwcDisplayId =
+ getHwComposer().fromPhysicalDisplayId(static_cast<PhysicalDisplayId>(*displayId));
if (!hwcDisplayId) {
continue;
}
@@ -4733,7 +4760,7 @@
StringAppendF(&result, "Composition layers\n");
mDrawingState.traverseInZOrder([&](Layer* layer) {
auto* compositionState = layer->getCompositionState();
- if (!compositionState) return;
+ if (!compositionState || !compositionState->isVisible) return;
android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
layer->getDebugName() ? layer->getDebugName()
@@ -4897,7 +4924,7 @@
case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
case GET_DISPLAYED_CONTENT_SAMPLE:
- case NOTIFY_POWER_HINT:
+ case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
// ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
@@ -4950,11 +4977,13 @@
// special permissions.
case SET_FRAME_RATE:
case GET_DISPLAY_BRIGHTNESS_SUPPORT:
+ // captureLayers and captureDisplay will handle the permission check in the function
+ case CAPTURE_LAYERS:
+ case CAPTURE_DISPLAY:
case SET_DISPLAY_BRIGHTNESS: {
return OK;
}
- case CAPTURE_LAYERS:
- case CAPTURE_SCREEN:
+
case ADD_REGION_SAMPLING_LISTENER:
case REMOVE_REGION_SAMPLING_LISTENER: {
// codes that require permission check
@@ -4968,7 +4997,7 @@
}
return OK;
}
- case CAPTURE_SCREEN_BY_ID: {
+ case CAPTURE_DISPLAY_BY_ID: {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
@@ -5307,7 +5336,8 @@
case 1035: {
n = data.readInt32();
mDebugDisplayConfigSetByBackdoor = false;
- if (n >= 0) {
+ const auto numConfigs = mRefreshRateConfigs->getAllRefreshRates().size();
+ if (n >= 0 && n < numConfigs) {
const auto displayToken = getInternalDisplayToken();
status_t result = setActiveConfig(displayToken, n);
if (result != NO_ERROR) {
@@ -5413,45 +5443,6 @@
const int mApi;
};
-status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
- sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers,
- Dataspace reqDataspace, ui::PixelFormat reqPixelFormat,
- const Rect& sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, bool captureSecureLayers) {
- ATRACE_CALL();
-
- if (!displayToken) return BAD_VALUE;
-
- auto renderAreaRotation = ui::Transform::toRotationFlags(rotation);
- if (renderAreaRotation == ui::Transform::ROT_INVALID) {
- ALOGE("%s: Invalid rotation: %s", __FUNCTION__, toCString(rotation));
- renderAreaRotation = ui::Transform::ROT_0;
- }
-
- sp<DisplayDevice> display;
- {
- Mutex::Autolock lock(mStateLock);
-
- display = getDisplayDeviceLocked(displayToken);
- if (!display) return NAME_NOT_FOUND;
-
- // set the requested width/height to the logical display viewport size
- // by default
- if (reqWidth == 0 || reqHeight == 0) {
- reqWidth = uint32_t(display->getViewport().width());
- reqHeight = uint32_t(display->getViewport().height());
- }
- }
-
- DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
- renderAreaRotation, captureSecureLayers);
- auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
- std::placeholders::_1);
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
- useIdentityTransform, outCapturedSecureLayers);
-}
-
static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
switch (colorMode) {
case ColorMode::DISPLAY_P3:
@@ -5464,6 +5455,67 @@
}
}
+static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if (uid == AID_GRAPHICS || PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
+ return OK;
+ }
+
+ // If the caller doesn't have the correct permissions but is only attempting to screenshot
+ // itself, we allow it to continue.
+ if (captureArgs.uid == uid) {
+ return OK;
+ }
+
+ ALOGE("Permission Denial: can't take screenshot pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+}
+
+status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
+ ScreenCaptureResults& captureResults) {
+ ATRACE_CALL();
+
+ status_t validate = validateScreenshotPermissions(args);
+ if (validate != OK) {
+ return validate;
+ }
+
+ if (!args.displayToken) return BAD_VALUE;
+
+ wp<DisplayDevice> displayWeak;
+ ui::LayerStack layerStack;
+ ui::Size reqSize(args.width, args.height);
+ ui::Dataspace dataspace;
+ {
+ Mutex::Autolock lock(mStateLock);
+ sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken);
+ if (!display) return NAME_NOT_FOUND;
+ displayWeak = display;
+ layerStack = display->getLayerStack();
+
+ // set the requested width/height to the logical display layer stack rect size by default
+ if (args.width == 0 || args.height == 0) {
+ reqSize = display->getLayerStackSpaceRect().getSize();
+ }
+
+ const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
+ dataspace = pickDataspaceFromColorMode(colorMode);
+ }
+
+ RenderAreaFuture renderAreaFuture = promise::defer([=] {
+ return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, dataspace,
+ args.useIdentityTransform, args.captureSecureLayers);
+ });
+
+ auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, args.uid, visitor);
+ };
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+ args.pixelFormat, captureResults);
+}
+
status_t SurfaceFlinger::setSchedFifo(bool enabled) {
static constexpr int kFifoPriority = 2;
static constexpr int kOtherPriority = 0;
@@ -5485,8 +5537,8 @@
}
sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
- const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack});
- if (displayToken) {
+ if (const sp<IBinder> displayToken =
+ getPhysicalDisplayTokenLocked(PhysicalDisplayId{displayOrLayerStack})) {
return getDisplayDeviceLocked(displayToken);
}
// Couldn't find display by displayId. Try to get display by layerStack since virtual displays
@@ -5503,154 +5555,62 @@
return nullptr;
}
-status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer) {
- sp<DisplayDevice> display;
- uint32_t width;
- uint32_t height;
- ui::Transform::RotationFlags captureOrientation;
+status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack,
+ ScreenCaptureResults& captureResults) {
+ ui::LayerStack layerStack;
+ wp<DisplayDevice> displayWeak;
+ ui::Size size;
+ ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
- display = getDisplayByIdOrLayerStack(displayOrLayerStack);
+ sp<DisplayDevice> display = getDisplayByIdOrLayerStack(displayOrLayerStack);
if (!display) {
return NAME_NOT_FOUND;
}
+ layerStack = display->getLayerStack();
+ displayWeak = display;
- width = uint32_t(display->getViewport().width());
- height = uint32_t(display->getViewport().height());
+ size = display->getLayerStackSpaceRect().getSize();
- const auto orientation = display->getOrientation();
- captureOrientation = ui::Transform::toRotationFlags(orientation);
-
- switch (captureOrientation) {
- case ui::Transform::ROT_90:
- captureOrientation = ui::Transform::ROT_270;
- break;
-
- case ui::Transform::ROT_270:
- captureOrientation = ui::Transform::ROT_90;
- break;
-
- case ui::Transform::ROT_INVALID:
- ALOGE("%s: Invalid orientation: %s", __FUNCTION__, toCString(orientation));
- captureOrientation = ui::Transform::ROT_0;
- break;
-
- default:
- break;
- }
- *outDataspace =
+ dataspace =
pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
}
- DisplayRenderArea renderArea(display, Rect(), width, height, *outDataspace, captureOrientation,
- false /* captureSecureLayers */);
+ RenderAreaFuture renderAreaFuture = promise::defer([=] {
+ return DisplayRenderArea::create(displayWeak, Rect(), size,
+ captureResults.capturedDataspace,
+ false /* useIdentityTransform */,
+ false /* captureSecureLayers */);
+ });
- auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
- std::placeholders::_1);
- bool ignored = false;
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, ui::PixelFormat::RGBA_8888,
- false /* useIdentityTransform */,
- ignored /* outCapturedSecureLayers */);
-}
-
-status_t SurfaceFlinger::captureLayers(
- const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
- const Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles,
- float frameScale, bool childrenOnly) {
- ATRACE_CALL();
-
- class LayerRenderArea : public RenderArea {
- public:
- LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
- int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
- bool childrenOnly, const Rect& displayViewport)
- : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
- mLayer(layer),
- mCrop(crop),
- mNeedsFiltering(false),
- mFlinger(flinger),
- mChildrenOnly(childrenOnly) {}
- const ui::Transform& getTransform() const override { return mTransform; }
- Rect getBounds() const override { return mLayer->getBufferSize(mLayer->getDrawingState()); }
- int getHeight() const override {
- return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
- }
- int getWidth() const override {
- return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
- }
- bool isSecure() const override { return false; }
- bool needsFiltering() const override { return mNeedsFiltering; }
- sp<const DisplayDevice> getDisplayDevice() const override { return nullptr; }
- Rect getSourceCrop() const override {
- if (mCrop.isEmpty()) {
- return getBounds();
- } else {
- return mCrop;
- }
- }
- class ReparentForDrawing {
- public:
- const sp<Layer>& oldParent;
- const sp<Layer>& newParent;
-
- ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
- const Rect& drawingBounds)
- : oldParent(oldParent), newParent(newParent) {
- // Compute and cache the bounds for the new parent layer.
- newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
- 0.f /* shadowRadius */);
- oldParent->setChildrenDrawingParent(newParent);
- }
- ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
- };
-
- void render(std::function<void()> drawLayers) override {
- const Rect sourceCrop = getSourceCrop();
- // no need to check rotation because there is none
- mNeedsFiltering = sourceCrop.width() != getReqWidth() ||
- sourceCrop.height() != getReqHeight();
-
- if (!mChildrenOnly) {
- mTransform = mLayer->getTransform().inverse();
- drawLayers();
- } else {
- uint32_t w = static_cast<uint32_t>(getWidth());
- uint32_t h = static_cast<uint32_t>(getHeight());
- // In the "childrenOnly" case we reparent the children to a screenshot
- // layer which has no properties set and which does not draw.
- sp<ContainerLayer> screenshotParentLayer =
- mFlinger->getFactory().createContainerLayer({mFlinger, nullptr,
- "Screenshot Parent"s, w, h, 0,
- LayerMetadata()});
-
- ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
- drawLayers();
- }
- }
-
- private:
- const sp<Layer> mLayer;
- const Rect mCrop;
-
- ui::Transform mTransform;
- bool mNeedsFiltering;
-
- SurfaceFlinger* mFlinger;
- const bool mChildrenOnly;
+ auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
};
- int reqWidth = 0;
- int reqHeight = 0;
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
+ ui::PixelFormat::RGBA_8888, captureResults);
+}
+
+status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
+ ScreenCaptureResults& captureResults) {
+ ATRACE_CALL();
+
+ status_t validate = validateScreenshotPermissions(args);
+ if (validate != OK) {
+ return validate;
+ }
+
+ ui::Size reqSize;
sp<Layer> parent;
- Rect crop(sourceCrop);
+ Rect crop(args.sourceCrop);
std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
- Rect displayViewport;
+ Rect layerStackSpaceRect;
+ ui::Dataspace dataspace;
+ bool captureSecureLayers;
{
Mutex::Autolock lock(mStateLock);
- parent = fromHandleLocked(layerHandleBinder).promote();
+ parent = fromHandleLocked(args.layerHandle).promote();
if (parent == nullptr || parent->isRemovedFromCurrentState()) {
ALOGE("captureLayers called with an invalid or removed parent");
return NAME_NOT_FOUND;
@@ -5664,25 +5624,24 @@
}
Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState());
- if (sourceCrop.width() <= 0) {
+ if (args.sourceCrop.width() <= 0) {
crop.left = 0;
crop.right = parentSourceBounds.getWidth();
}
- if (sourceCrop.height() <= 0) {
+ if (args.sourceCrop.height() <= 0) {
crop.top = 0;
crop.bottom = parentSourceBounds.getHeight();
}
- if (crop.isEmpty() || frameScale <= 0.0f) {
+ if (crop.isEmpty() || args.frameScale <= 0.0f) {
// Error out if the layer has no source bounds (i.e. they are boundless) and a source
// crop was not specified, or an invalid frame scale was provided.
return BAD_VALUE;
}
- reqWidth = crop.width() * frameScale;
- reqHeight = crop.height() * frameScale;
+ reqSize = ui::Size(crop.width() * args.frameScale, crop.height() * args.frameScale);
- for (const auto& handle : excludeHandles) {
+ for (const auto& handle : args.excludeHandles) {
sp<Layer> excludeLayer = fromHandleLocked(handle).promote();
if (excludeLayer != nullptr) {
excludeLayers.emplace(excludeLayer);
@@ -5697,25 +5656,36 @@
return NAME_NOT_FOUND;
}
- displayViewport = display->getViewport();
+ layerStackSpaceRect = display->getLayerStackSpaceRect();
+
+ const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
+ dataspace = pickDataspaceFromColorMode(colorMode);
+
+ captureSecureLayers = args.captureSecureLayers && display->isSecure();
} // mStateLock
// really small crop or frameScale
- if (reqWidth <= 0) {
- reqWidth = 1;
+ if (reqSize.width <= 0) {
+ reqSize.width = 1;
}
- if (reqHeight <= 0) {
- reqHeight = 1;
+ if (reqSize.height <= 0) {
+ reqSize.height = 1;
}
- LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
- displayViewport);
- auto traverseLayers = [parent, childrenOnly,
- &excludeLayers](const LayerVector::Visitor& visitor) {
+ bool childrenOnly = args.childrenOnly;
+ RenderAreaFuture renderAreaFuture = promise::defer([=]() -> std::unique_ptr<RenderArea> {
+ return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
+ childrenOnly, layerStackSpaceRect,
+ captureSecureLayers);
+ });
+
+ auto traverseLayers = [parent, args, &excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
if (!layer->isVisible()) {
return;
- } else if (childrenOnly && layer == parent.get()) {
+ } else if (args.childrenOnly && layer == parent.get()) {
+ return;
+ } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
return;
}
@@ -5731,36 +5701,32 @@
});
};
- bool outCapturedSecureLayers = false;
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false,
- outCapturedSecureLayers);
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+ args.pixelFormat, captureResults);
}
-status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>* outBuffer,
- const ui::PixelFormat reqPixelFormat,
- bool useIdentityTransform,
- bool& outCapturedSecureLayers) {
+ ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
+ ScreenCaptureResults& captureResults) {
ATRACE_CALL();
// TODO(b/116112787) Make buffer usage a parameter.
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
- *outBuffer =
- getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
- static_cast<android_pixel_format>(reqPixelFormat), 1,
- usage, "screenshot");
+ sp<GraphicBuffer> buffer =
+ getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+ static_cast<android_pixel_format>(reqPixelFormat),
+ 1 /* layerCount */, usage, "screenshot");
- return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform,
- false /* regionSampling */, outCapturedSecureLayers);
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+ false /* regionSampling */, captureResults);
}
-status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer,
- bool useIdentityTransform, bool regionSampling,
- bool& outCapturedSecureLayers) {
+ const sp<GraphicBuffer>& buffer, bool regionSampling,
+ ScreenCaptureResults& captureResults) {
const int uid = IPCThreadState::self()->getCallingUid();
const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
@@ -5769,23 +5735,28 @@
do {
std::tie(result, syncFd) =
- schedule([&] {
+ schedule([&]() -> std::pair<status_t, int> {
if (mRefreshPending) {
- ATRACE_NAME("Skipping screenshot for now");
- return std::make_pair(EAGAIN, -1);
+ 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 fd = -1;
Mutex::Autolock lock(mStateLock);
- renderArea.render([&] {
- result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(),
- useIdentityTransform, forSystem, &fd,
- regionSampling, outCapturedSecureLayers);
+ renderArea->render([&] {
+ result = renderScreenImplLocked(*renderArea, traverseLayers, buffer.get(),
+ forSystem, &fd, regionSampling,
+ captureResults);
});
- return std::make_pair(result, fd);
+ return {result, fd};
}).get();
} while (result == EAGAIN);
@@ -5797,18 +5768,35 @@
return result;
}
-void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
- TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
- bool regionSampling, int* outSyncFd) {
+status_t SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers,
+ const sp<GraphicBuffer>& buffer, bool forSystem,
+ int* outSyncFd, bool regionSampling,
+ ScreenCaptureResults& captureResults) {
ATRACE_CALL();
+ traverseLayers([&](Layer* layer) {
+ captureResults.capturedSecureLayers =
+ captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
+ });
+
+ // We allow the system server to take screenshots of secure layers for
+ // use in situations like the Screen-rotation animation and place
+ // the impetus on WindowManager to not persist them.
+ if (captureResults.capturedSecureLayers && !forSystem) {
+ ALOGW("FB is protected: PERMISSION_DENIED");
+ return PERMISSION_DENIED;
+ }
+
+ captureResults.buffer = buffer;
+ captureResults.capturedDataspace = renderArea.getReqDataSpace();
+
const auto reqWidth = renderArea.getReqWidth();
const auto reqHeight = renderArea.getReqHeight();
const auto sourceCrop = renderArea.getSourceCrop();
const auto transform = renderArea.getTransform();
const auto rotation = renderArea.getRotationFlags();
- const auto& displayViewport = renderArea.getDisplayViewport();
+ const auto& layerStackSpaceRect = renderArea.getLayerStackSpaceRect();
renderengine::DisplaySettings clientCompositionDisplay;
std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers;
@@ -5840,13 +5828,12 @@
Region clip(renderArea.getBounds());
compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
clip,
- useIdentityTransform,
layer->needsFilteringForScreenshots(display.get(), transform) ||
renderArea.needsFiltering(),
renderArea.isSecure(),
supportProtectedContent,
clearRegion,
- displayViewport,
+ layerStackSpaceRect,
clientCompositionDisplay.outputDataspace,
true, /* realContentIsVisible */
false, /* clearContent */
@@ -5894,30 +5881,7 @@
layer->onLayerDisplayed(releaseFence);
}
}
-}
-status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
- TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer,
- bool useIdentityTransform, bool forSystem,
- int* outSyncFd, bool regionSampling,
- bool& outCapturedSecureLayers) {
- ATRACE_CALL();
-
- traverseLayers([&](Layer* layer) {
- outCapturedSecureLayers =
- outCapturedSecureLayers || (layer->isVisible() && layer->isSecure());
- });
-
- // We allow the system server to take screenshots of secure layers for
- // use in situations like the Screen-rotation animation and place
- // the impetus on WindowManager to not persist them.
- if (outCapturedSecureLayers && !forSystem) {
- ALOGW("FB is protected: PERMISSION_DENIED");
- return PERMISSION_DENIED;
- }
- renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, regionSampling,
- outSyncFd);
return NO_ERROR;
}
@@ -5943,22 +5907,25 @@
layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
}
-void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor) {
+void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const int32_t uid,
+ const LayerVector::Visitor& visitor) {
// We loop through the first level of layers without traversing,
// as we need to determine which layers belong to the requested display.
for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
+ if (!layer->belongsToDisplay(layerStack)) {
continue;
}
// relative layers are traversed in Layer::traverseInZOrder
layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
+ if (layer->getPrimaryDisplayOnly()) {
return;
}
if (!layer->isVisible()) {
return;
}
+ if (uid != CaptureArgs::UNSET_UID && layer->getOwnerUid() != uid) {
+ return;
+ }
visitor(layer);
});
}
@@ -5999,7 +5966,9 @@
const nsecs_t vsyncPeriod = getHwComposer()
.getConfigs(*displayId)[policy->defaultConfig.value()]
->getVsyncPeriod();
- mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+ mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle,
+ static_cast<PhysicalDisplayId>(
+ *display->getId()),
policy->defaultConfig, vsyncPeriod);
return NO_ERROR;
}
@@ -6031,7 +6000,8 @@
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig())
.getVsyncPeriod();
- mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle,
+ static_cast<PhysicalDisplayId>(*display->getId()),
display->getActiveConfig(), vsyncPeriod);
toggleKernelIdleTimer();
@@ -6135,10 +6105,6 @@
}
}
-void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
- mFlinger->setInputWindowsFinished();
-}
-
wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
Mutex::Autolock _l(mStateLock);
return fromHandleLocked(handle);
@@ -6315,7 +6281,7 @@
static_cast<void>(schedule([=] {
std::unique_ptr<RefreshRateOverlay> overlay;
if (enable) {
- overlay = std::make_unique<RefreshRateOverlay>(*this);
+ overlay = std::make_unique<RefreshRateOverlay>(*this, mRefreshRateOverlaySpinner);
}
{
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c727574..24837b8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -33,7 +33,6 @@
#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
#include <gui/OccupancyTracker.h>
-#include <input/ISetInputWindowsListener.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/mat4.h>
#include <renderengine/LayerSettings.h>
@@ -59,7 +58,7 @@
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
-#include "Scheduler/VSyncModulator.h"
+#include "Scheduler/VsyncModulator.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceTracing.h"
#include "TracedOrdinal.h"
@@ -89,15 +88,20 @@
class Client;
class EventThread;
class HWComposer;
+struct SetInputWindowsListener;
class IGraphicBufferProducer;
-class IInputFlinger;
class Layer;
class MessageBase;
class RefreshRateOverlay;
class RegionSamplingThread;
+class RenderArea;
class TimeStats;
class FrameTracer;
+namespace os {
+ class IInputFlinger;
+}
+
namespace compositionengine {
class DisplaySurface;
class OutputLayer;
@@ -296,13 +300,6 @@
// utility function to delete a texture on the main thread
void deleteTextureAsync(uint32_t texture);
- // enable/disable h/w composer event
- // TODO: this should be made accessible only to EventThread
- void setPrimaryVsyncEnabled(bool enabled);
-
- // main thread function to enable/disable h/w composer event
- void setPrimaryVsyncEnabledInternal(bool enabled) REQUIRES(mStateLock);
-
// called on the main thread by MessageQueue when an internal message
// is received
// TODO: this should be made accessible only to MessageQueue
@@ -335,6 +332,7 @@
// If set, disables reusing client composition buffers. This can be set by
// debug.sf.disable_client_composition_cache
bool mDisableClientCompositionCache = false;
+ void setInputWindowsFinished();
private:
friend class BufferLayer;
@@ -428,19 +426,12 @@
ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
ISurfaceComposer::ConfigChanged configChanged =
ISurfaceComposer::eConfigChangedSuppress) override;
- status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
- ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
- ui::Rotation rotation, bool captureSecureLayers) override;
- status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
- sp<GraphicBuffer>* outBuffer) override;
- status_t captureLayers(
- const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
- const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
- const Rect& sourceCrop,
- const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& exclude,
- float frameScale, bool childrenOnly) override;
+ status_t captureDisplay(const DisplayCaptureArgs& args,
+ ScreenCaptureResults& captureResults) override;
+ status_t captureDisplay(uint64_t displayOrLayerStack,
+ ScreenCaptureResults& captureResults) override;
+ status_t captureLayers(const LayerCaptureArgs& args,
+ ScreenCaptureResults& captureResults) override;
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) override;
@@ -498,7 +489,7 @@
status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const override;
status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override;
- status_t notifyPowerHint(int32_t hintId) override;
+ status_t notifyPowerBoost(int32_t boostId) override;
status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
@@ -530,8 +521,12 @@
/* ------------------------------------------------------------------------
* ISchedulerCallback
*/
+
+ // Toggles hardware VSYNC by calling into HWC.
+ void setVsyncEnabled(bool) override;
+ // Initiates a refresh rate change to be applied on invalidate.
void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
- // force full composition on all displays without resetting the scheduler idle timer.
+ // Forces full composition on all displays without resetting the scheduler idle timer.
void repaintEverythingForHWC() override;
// Called when kernel idle timer has expired. Used to update the refresh rate overlay.
void kernelTimerChanged(bool expired) override;
@@ -542,6 +537,9 @@
bool mKernelIdleTimerEnabled = false;
// Keeps track of whether the kernel timer is supported on the SF side.
bool mSupportKernelIdleTimer = false;
+ // Show spinner with refresh rate overlay
+ bool mRefreshRateOverlaySpinner = false;
+
/* ------------------------------------------------------------------------
* Message handling
*/
@@ -551,8 +549,6 @@
void signalLayerUpdate();
void signalRefresh();
- using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
-
struct ActiveConfigInfo {
HwcConfigIndexType configId;
Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
@@ -605,9 +601,14 @@
void updateInputFlinger();
void updateInputWindowInfo();
void commitInputWindowCommands() REQUIRES(mStateLock);
- void setInputWindowsFinished();
void updateCursorAsync();
- void initScheduler(DisplayId primaryDisplayId);
+
+ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
+ using VsyncModulator = scheduler::VsyncModulator;
+
+ void initScheduler(PhysicalDisplayId primaryDisplayId);
+ void updatePhaseConfiguration(const RefreshRate&);
+ void setVsyncConfig(const VsyncModulator::VsyncConfig&);
/* handlePageFlip - latch a new buffer if available and compute the dirty
* region. Returns whether a new buffer has been latched, i.e., whether it
@@ -618,6 +619,8 @@
/* ------------------------------------------------------------------------
* Transactions
*/
+ using TransactionSchedule = scheduler::TransactionSchedule;
+
void applyTransactionState(const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
@@ -625,7 +628,8 @@
const client_cache_t& uncacheBuffer, const int64_t postTime,
bool privileged, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
- bool isMainThread = false) REQUIRES(mStateLock);
+ int originPID, int originUID, bool isMainThread = false)
+ REQUIRES(mStateLock);
// Returns true if at least one transaction was flushed
bool flushTransactionQueues();
// Returns true if there is at least one transaction that needs to be flushed
@@ -640,7 +644,7 @@
// but there is no need to try and wake up immediately to do it. Rather we rely on
// onFrameAvailable or another layer update to wake us up.
void setTraversalNeeded();
- uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
+ uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule);
void commitTransaction() REQUIRES(mStateLock);
void commitOffscreenLayers();
bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
@@ -718,25 +722,22 @@
void startBootAnim();
using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+ using RenderAreaFuture = std::future<std::unique_ptr<RenderArea>>;
- void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
- bool regionSampling, int* outSyncFd);
- status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
- bool useIdentityTransform, bool& outCapturedSecureLayers);
- status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
- bool regionSampling, bool& outCapturedSecureLayers);
+ status_t renderScreenImplLocked(const RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers,
+ const sp<GraphicBuffer>& buffer, bool forSystem, int* outSyncFd,
+ bool regionSampling, ScreenCaptureResults& captureResults);
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
+ ui::PixelFormat, ScreenCaptureResults& captureResults);
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, const sp<GraphicBuffer>&,
+ bool regionSampling, ScreenCaptureResults& captureResults);
sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
- status_t captureScreenImplLocked(const RenderArea& renderArea,
- TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
- bool forSystem, int* outSyncFd, bool regionSampling,
- bool& outCapturedSecureLayers);
- void traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor);
+
+ // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
+ // matching ownerUid
+ void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, const LayerVector::Visitor&);
sp<StartPropertySetThread> mStartPropertySetThread;
@@ -783,8 +784,6 @@
return getDefaultDisplayDeviceLocked();
}
- std::optional<DeviceProductInfo> getDeviceProductInfoLocked(const DisplayDevice&) const;
-
// mark a region of a layer stack dirty. this updates the dirty
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
@@ -850,14 +849,13 @@
void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
/* ------------------------------------------------------------------------
- * VSync
+ * VSYNC
*/
nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
// Sets the refresh rate by switching active configs, if they are available for
// the desired refresh rate.
- void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent event)
- REQUIRES(mStateLock);
+ void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent) REQUIRES(mStateLock);
bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
@@ -884,7 +882,8 @@
/*
* Display identification
*/
- sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const REQUIRES(mStateLock) {
+ sp<IBinder> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
+ REQUIRES(mStateLock) {
const auto it = mPhysicalDisplayTokens.find(displayId);
return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
}
@@ -905,7 +904,7 @@
return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
}
- std::optional<DisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
+ std::optional<PhysicalDisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
}
@@ -1017,6 +1016,10 @@
// Can't be unordered_set because wp<> isn't hashable
std::set<wp<IBinder>> mGraphicBufferProducerList;
size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
+ // If there are more GraphicBufferProducers tracked by SurfaceFlinger than
+ // this threshold, then begin logging.
+ size_t mGraphicBufferProducerListSizeLogThreshold =
+ static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS));
void removeGraphicBufferProducerAsync(const wp<IBinder>&);
@@ -1070,7 +1073,8 @@
// this may only be written from the main thread with mStateLock held
// it may be read from other threads with mStateLock held
std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
- std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens GUARDED_BY(mStateLock);
+ std::unordered_map<PhysicalDisplayId, sp<IBinder>> mPhysicalDisplayTokens
+ GUARDED_BY(mStateLock);
std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
@@ -1080,7 +1084,6 @@
bool mDebugDisableTransformHint = false;
volatile nsecs_t mDebugInTransaction = 0;
bool mForceFullDamage = false;
- bool mPropagateBackpressure = true;
bool mPropagateBackpressureClientComposition = false;
std::unique_ptr<SurfaceInterceptor> mInterceptor;
@@ -1132,7 +1135,8 @@
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)
+ std::vector<ListenerCallbacks> listenerCallbacks, int originPID,
+ int originUID)
: states(composerStates),
displays(displayStates),
flags(transactionFlags),
@@ -1141,7 +1145,9 @@
postTime(postTime),
privileged(privileged),
hasListenerCallbacks(hasListenerCallbacks),
- listenerCallbacks(listenerCallbacks) {}
+ listenerCallbacks(listenerCallbacks),
+ originPID(originPID),
+ originUID(originUID) {}
Vector<ComposerState> states;
Vector<DisplayState> displays;
@@ -1152,6 +1158,8 @@
bool privileged;
bool hasListenerCallbacks;
std::vector<ListenerCallbacks> listenerCallbacks;
+ int originPID;
+ int originUID;
};
std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
@@ -1206,10 +1214,10 @@
scheduler::ConnectionHandle mSfConnectionHandle;
// Stores phase offsets configured per refresh rate.
- std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
+ std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
- // Optional to defer construction until scheduler connections are created.
- std::optional<scheduler::VSyncModulator> mVSyncModulator;
+ // Optional to defer construction until PhaseConfiguration is created.
+ std::optional<scheduler::VsyncModulator> mVsyncModulator;
std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
@@ -1217,6 +1225,14 @@
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
*/
@@ -1252,21 +1268,12 @@
const float mInternalDisplayDensity;
const float mEmulatedDisplayDensity;
- sp<IInputFlinger> mInputFlinger;
+ sp<os::IInputFlinger> mInputFlinger;
InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
// Should only be accessed by the main thread.
InputWindowCommands mInputWindowCommands;
- struct SetInputWindowsListener : BnSetInputWindowsListener {
- explicit SetInputWindowsListener(sp<SurfaceFlinger> flinger)
- : mFlinger(std::move(flinger)) {}
-
- void onSetInputWindowsFinished() override;
-
- const sp<SurfaceFlinger> mFlinger;
- };
-
- const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this);
+ sp<SetInputWindowsListener> mSetInputWindowsListener;
bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
Hwc2::impl::PowerAdvisor mPowerAdvisor;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index ddd20a5..54ce60e 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -38,10 +38,9 @@
#include "DisplayHardware/ComposerHal.h"
#include "Scheduler/DispSync.h"
-#include "Scheduler/EventControlThread.h"
#include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
+#include "Scheduler/VsyncConfiguration.h"
namespace android::surfaceflinger {
@@ -51,11 +50,6 @@
return std::make_unique<android::impl::DispSync>(name, hasSyncFramework);
}
-std::unique_ptr<EventControlThread> DefaultFactory::createEventControlThread(
- SetVSyncEnabled setVSyncEnabled) {
- return std::make_unique<android::impl::EventControlThread>(std::move(setVSyncEnabled));
-}
-
std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& serviceName) {
return std::make_unique<android::impl::HWComposer>(serviceName);
}
@@ -64,21 +58,18 @@
return std::make_unique<android::impl::MessageQueue>();
}
-std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration(
+std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
const scheduler::RefreshRateConfigs& refreshRateConfigs) {
if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
- return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs);
+ return std::make_unique<scheduler::impl::WorkDuration>(refreshRateConfigs);
} else {
return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
}
}
std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
- SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
- ISchedulerCallback& schedulerCallback) {
- return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
- property_get_bool("debug.sf.use_content_detection_v2", true),
- sysprop::use_content_detection_for_refresh_rate(false));
+ const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback) {
+ return std::make_unique<Scheduler>(configs, callback);
}
std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index bd40cfb..04af8b0 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -27,13 +27,11 @@
virtual ~DefaultFactory();
std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) override;
- std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) override;
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
std::unique_ptr<MessageQueue> createMessageQueue() override;
- std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
const scheduler::RefreshRateConfigs&) override;
- std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
- const scheduler::RefreshRateConfigs&,
+ std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) override;
std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override;
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 6f4fcc6..784a77b 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -35,12 +35,10 @@
class ContainerLayer;
class DisplayDevice;
class DispSync;
-class EventControlThread;
class GraphicBuffer;
class HWComposer;
class IGraphicBufferConsumer;
class IGraphicBufferProducer;
-class ISchedulerCallback;
class Layer;
class MessageQueue;
class Scheduler;
@@ -49,6 +47,7 @@
class SurfaceInterceptor;
struct DisplayDeviceCreationArgs;
+struct ISchedulerCallback;
struct LayerCreationArgs;
namespace compositionengine {
@@ -56,7 +55,7 @@
} // namespace compositionengine
namespace scheduler {
-class PhaseConfiguration;
+class VsyncConfiguration;
class RefreshRateConfigs;
} // namespace scheduler
@@ -68,16 +67,12 @@
// of each interface.
class Factory {
public:
- using SetVSyncEnabled = std::function<void(bool)>;
-
virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) = 0;
- virtual std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) = 0;
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
- virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
const scheduler::RefreshRateConfigs&) = 0;
- virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
- const scheduler::RefreshRateConfigs&,
+ virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) = 0;
virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 80102bd..c15d0df 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -143,7 +143,7 @@
addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
- display.viewport, display.frame);
+ display.layerStackSpaceRect, display.orientedDisplaySpaceRect);
}
status_t SurfaceInterceptor::writeProtoFileLocked() {
@@ -221,6 +221,13 @@
protoRect->set_bottom(rect.bottom);
}
+void SurfaceInterceptor::setTransactionOriginLocked(Transaction* transaction, int32_t pid,
+ int32_t uid) {
+ Origin* origin(transaction->mutable_origin());
+ origin->set_pid(pid);
+ origin->set_uid(uid);
+}
+
void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
float x, float y)
{
@@ -483,18 +490,20 @@
}
if (state.what & DisplayState::eDisplayProjectionChanged) {
addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation),
- state.viewport, state.frame);
+ state.layerStackSpaceRect, state.orientedDisplaySpaceRect);
}
}
-void SurfaceInterceptor::addTransactionLocked(Increment* increment,
- const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags)
-{
+void SurfaceInterceptor::addTransactionLocked(
+ Increment* increment, const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags, int originPID,
+ int originUID) {
Transaction* transaction(increment->mutable_transaction());
transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
+ setTransactionOriginLocked(transaction, originPID, originUID);
+
for (const auto& compState: stateUpdates) {
addSurfaceChangesLocked(transaction, compState.state);
}
@@ -613,17 +622,17 @@
powerModeUpdate->set_mode(mode);
}
-void SurfaceInterceptor::saveTransaction(const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags)
-{
+void SurfaceInterceptor::saveTransaction(
+ const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPID, int originUID) {
if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
return;
}
ATRACE_CALL();
std::lock_guard<std::mutex> protoGuard(mTraceMutex);
addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
- flags);
+ flags, originPID, originUID);
}
void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 896bdcc..1798b5a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -62,7 +62,8 @@
virtual void saveTransaction(
const Vector<ComposerState>& stateUpdates,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags) = 0;
+ const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPID,
+ int originUID) = 0;
// Intercept surface data
virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
@@ -97,7 +98,8 @@
// Intercept display and surface transactions
void saveTransaction(const Vector<ComposerState>& stateUpdates,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags) override;
+ const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPID,
+ int originUID) override;
// Intercept surface data
void saveSurfaceCreation(const sp<const Layer>& layer) override;
@@ -160,8 +162,9 @@
int32_t overrideScalingMode);
void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
+ const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays,
+ uint32_t transactionFlags, int originPID, int originUID);
void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached);
@@ -182,6 +185,8 @@
void addDisplayChangesLocked(Transaction* transaction,
const DisplayState& state, int32_t sequenceId);
+ // Add transaction origin to trace
+ void setTransactionOriginLocked(Transaction* transaction, int32_t pid, int32_t uid);
bool mEnabled {false};
std::string mOutputFileName {DEFAULT_FILENAME};
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index 8fce0c9..aef670d 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -115,6 +115,7 @@
}
layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop());
layer.shadowRadius = layerProto.shadow_radius();
+ layer.ownerUid = layerProto.owner_uid();
return layer;
}
@@ -276,7 +277,7 @@
std::string LayerProtoParser::Layer::to_string() const {
std::string result;
- StringAppendF(&result, "+ %s (%s)\n", type.c_str(), name.c_str());
+ StringAppendF(&result, "+ %s (%s) uid=%d\n", type.c_str(), name.c_str(), ownerUid);
result.append(transparentRegion.to_string("TransparentRegion").c_str());
result.append(visibleRegion.to_string("VisibleRegion").c_str());
result.append(damageRegion.to_string("SurfaceDamageRegion").c_str());
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 52b9165..c48354f 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -114,6 +114,7 @@
LayerMetadata metadata;
LayerProtoParser::FloatRect cornerRadiusCrop;
float shadowRadius;
+ uid_t ownerUid;
std::string to_string() const;
};
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 7f1f542..41d6d08 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -123,6 +123,8 @@
bool is_relative_of = 51;
// Layer's background blur radius in pixels.
int32 background_blur_radius = 52;
+
+ uint32 owner_uid = 53;
}
message PositionProto {
@@ -196,12 +198,13 @@
bool has_wallpaper = 9;
float global_scale_factor = 10;
- float window_x_scale = 11;
- float window_y_scale = 12;
+ float window_x_scale = 11 [deprecated=true];
+ float window_y_scale = 12 [deprecated=true];
uint32 crop_layer_id = 13;
bool replace_touchable_region_with_crop = 14;
RectProto touchable_region_crop = 15;
+ TransformProto transform = 16;
}
message ColorTransformProto {
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index fe2af80..719c46e 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -21,11 +21,13 @@
"CommonTypes_test.cpp",
"Credentials_test.cpp",
"DereferenceSurfaceControl_test.cpp",
+ "DetachChildren_test.cpp",
"DisplayConfigs_test.cpp",
"EffectLayer_test.cpp",
"InvalidHandles_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
+ "LayerState_test.cpp",
"LayerTransaction_test.cpp",
"LayerTypeAndRenderTypeTransaction_test.cpp",
"LayerTypeTransaction_test.cpp",
@@ -33,6 +35,7 @@
"MirrorLayer_test.cpp",
"MultiDisplayLayerBounds_test.cpp",
"RelativeZ_test.cpp",
+ "ScreenCapture_test.cpp",
"SetFrameRate_test.cpp",
"SetGeometry_test.cpp",
"Stress_test.cpp",
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index c136708..10a517e 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -18,7 +18,6 @@
namespace {
const String8 DISPLAY_NAME("Credentials Display Test");
const String8 SURFACE_NAME("Test Surface Name");
-const float FRAME_SCALE = 1.0f;
} // namespace
/**
@@ -260,9 +259,10 @@
const auto display = SurfaceComposerClient::getInternalDisplayToken();
std::function<status_t()> condition = [=]() {
sp<GraphicBuffer> outBuffer;
- return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/,
- 0 /*reqHeight*/, false, ui::ROTATION_0, &outBuffer);
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = display;
+ ScreenCaptureResults captureResults;
+ return ScreenshotClient::captureDisplay(captureArgs, captureResults);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
@@ -271,10 +271,12 @@
setupBackgroundSurface();
sp<GraphicBuffer> outBuffer;
std::function<status_t()> condition = [=]() {
- sp<GraphicBuffer> outBuffer;
- return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
- Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+ captureArgs.sourceCrop = {0, 0, 1, 1};
+
+ ScreenCaptureResults captureResults;
+ return ScreenshotClient::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
new file mode 100644
index 0000000..b6c2fe2
--- /dev/null
+++ b/services/surfaceflinger/tests/DetachChildren_test.cpp
@@ -0,0 +1,322 @@
+/*
+ * 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 clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class DetachChildren : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+
+ mMainSurface = createLayer(String8("Main Test Surface"), mMainSurfaceBounds.width(),
+ mMainSurfaceBounds.height(), 0, mBlackBgSurface.get());
+
+ ASSERT_TRUE(mMainSurface != nullptr);
+ ASSERT_TRUE(mMainSurface->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(mMainSurface, mMainSurfaceColor);
+
+ asTransaction([&](Transaction& t) {
+ t.setLayer(mMainSurface, INT32_MAX - 1)
+ .setPosition(mMainSurface, mMainSurfaceBounds.left, mMainSurfaceBounds.top)
+ .show(mMainSurface);
+ });
+ }
+
+ virtual void TearDown() {
+ LayerTransactionTest::TearDown();
+ mMainSurface = 0;
+ }
+
+ sp<SurfaceControl> mMainSurface;
+ Color mMainSurfaceColor = {195, 63, 63, 255};
+ Rect mMainSurfaceBounds = Rect(64, 64, 128, 128);
+ std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(DetachChildren, RelativesAreNotDetached) {
+ Color relativeColor = {10, 10, 10, 255};
+ Rect relBounds = Rect(64, 64, 74, 74);
+
+ sp<SurfaceControl> relative =
+ createLayer(String8("relativeTestSurface"), relBounds.width(), relBounds.height(), 0);
+ TransactionUtils::fillSurfaceRGBA8(relative, relativeColor);
+
+ Transaction{}
+ .setRelativeLayer(relative, mMainSurface->getHandle(), 1)
+ .setPosition(relative, relBounds.left, relBounds.top)
+ .apply();
+
+ {
+ // The relative should be on top of the FG control.
+ mCapture = screenshot();
+ mCapture->expectColor(relBounds, relativeColor);
+ }
+ Transaction{}.detachChildren(mMainSurface).apply();
+
+ {
+ // Nothing should change at this point.
+ mCapture = screenshot();
+ mCapture->expectColor(relBounds, relativeColor);
+ }
+
+ Transaction{}.hide(relative).apply();
+
+ {
+ // Ensure that the relative was actually hidden, rather than
+ // being left in the detached but visible state.
+ mCapture = screenshot();
+ mCapture->expectColor(mMainSurfaceBounds, mMainSurfaceColor);
+ }
+}
+
+TEST_F(DetachChildren, DetachChildrenSameClient) {
+ Color childColor = {200, 200, 200, 255};
+ Rect childBounds = Rect(74, 74, 84, 84);
+ sp<SurfaceControl> child = createLayer(String8("Child surface"), childBounds.width(),
+ childBounds.height(), 0, mMainSurface.get());
+ ASSERT_TRUE(child->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(child, childColor);
+
+ asTransaction([&](Transaction& t) {
+ t.show(child);
+ t.setPosition(child, childBounds.left - mMainSurfaceBounds.left,
+ childBounds.top - mMainSurfaceBounds.top);
+ });
+
+ {
+ mCapture = screenshot();
+ // Expect main color around the child surface
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+
+ asTransaction([&](Transaction& t) { t.detachChildren(mMainSurface); });
+
+ asTransaction([&](Transaction& t) { t.hide(child); });
+
+ // Since the child has the same client as the parent, it will not get
+ // detached and will be hidden.
+ {
+ mCapture = screenshot();
+ mCapture->expectColor(mMainSurfaceBounds, mMainSurfaceColor);
+ }
+}
+
+TEST_F(DetachChildren, DetachChildrenDifferentClient) {
+ 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);
+
+ asTransaction([&](Transaction& t) {
+ t.show(childNewClient);
+ t.setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left,
+ childBounds.top - mMainSurfaceBounds.top);
+ });
+
+ {
+ mCapture = screenshot();
+ // Expect main color around the child surface
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+
+ asTransaction([&](Transaction& t) { t.detachChildren(mMainSurface); });
+
+ asTransaction([&](Transaction& t) { t.hide(childNewClient); });
+
+ // Nothing should have changed.
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+}
+
+TEST_F(DetachChildren, DetachChildrenThenAttach) {
+ 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();
+ // Expect main color around the child surface
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+
+ Transaction().detachChildren(mMainSurface).apply();
+ Transaction().hide(childNewClient).apply();
+
+ // Nothing should have changed.
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+
+ Color newParentColor = Color::RED;
+ Rect newParentBounds = Rect(20, 20, 52, 52);
+ sp<SurfaceControl> newParentSurface =
+ createLayer(String8("New Parent Surface"), newParentBounds.width(),
+ newParentBounds.height(), 0);
+ TransactionUtils::fillSurfaceRGBA8(newParentSurface, newParentColor);
+ Transaction()
+ .setLayer(newParentSurface, INT32_MAX - 1)
+ .show(newParentSurface)
+ .setPosition(newParentSurface, newParentBounds.left, newParentBounds.top)
+ .reparent(childNewClient, newParentSurface->getHandle())
+ .apply();
+ {
+ mCapture = screenshot();
+ // Child is now hidden.
+ mCapture->expectColor(newParentBounds, newParentColor);
+ }
+}
+
+TEST_F(DetachChildren, DetachChildrenWithDeferredTransaction) {
+ 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()
+ .deferTransactionUntil_legacy(childNewClient, mMainSurface->getHandle(),
+ mMainSurface->getSurface()->getNextFrameNumber())
+ .apply();
+ Transaction().detachChildren(mMainSurface).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);
+ Color grandchildColor = Color::RED;
+ Rect grandchildBounds = Rect(80, 80, 90, 90);
+
+ sp<SurfaceComposerClient> newClient1 = new SurfaceComposerClient;
+ sp<SurfaceComposerClient> newClient2 = new SurfaceComposerClient;
+
+ sp<SurfaceControl> childSurface =
+ createSurface(newClient1, "Child surface", childBounds.width(), childBounds.height(),
+ PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+ sp<SurfaceControl> grandchildSurface =
+ createSurface(newClient2, "Grandchild Surface", grandchildBounds.width(),
+ grandchildBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, childSurface.get());
+
+ TransactionUtils::fillSurfaceRGBA8(childSurface, childColor);
+ TransactionUtils::fillSurfaceRGBA8(grandchildSurface, grandchildColor);
+
+ Transaction()
+ .show(childSurface)
+ .show(grandchildSurface)
+ .setPosition(childSurface, childBounds.left - mMainSurfaceBounds.left,
+ childBounds.top - mMainSurfaceBounds.top)
+ .setPosition(grandchildSurface, grandchildBounds.left - childBounds.left,
+ grandchildBounds.top - childBounds.top)
+ .apply();
+
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectBorder(grandchildBounds, childColor);
+ mCapture->expectColor(grandchildBounds, grandchildColor);
+ }
+
+ Transaction().detachChildren(childSurface).apply();
+
+ // Remove main surface offscreen
+ Transaction().reparent(mMainSurface, nullptr).apply();
+ {
+ mCapture = screenshot();
+ mCapture->expectColor(mMainSurfaceBounds, Color::BLACK);
+ }
+
+ Transaction().reparent(mMainSurface, mBlackBgSurface->getHandle()).apply();
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectBorder(grandchildBounds, childColor);
+ mCapture->expectColor(grandchildBounds, grandchildColor);
+ }
+
+ Transaction().hide(grandchildSurface).apply();
+
+ // grandchild is still detached so it will not hide
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectBorder(grandchildBounds, childColor);
+ mCapture->expectColor(grandchildBounds, grandchildColor);
+ }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 42d1f5a..300e9c7 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -57,10 +57,12 @@
TEST_F(InvalidHandleTest, captureLayersInvalidHandle) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NAME_NOT_FOUND,
- sf->captureLayers(mNotSc->getHandle(), &outBuffer, Rect::EMPTY_RECT, 1.0f));
+ LayerCaptureArgs args;
+ args.layerHandle = mNotSc->getHandle();
+
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(args, captureResults));
}
} // namespace
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
new file mode 100644
index 0000000..785c2c3
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+
+#include <gui/LayerState.h>
+
+namespace android {
+namespace test {
+
+TEST(LayerStateTest, ParcellingDisplayCaptureArgs) {
+ DisplayCaptureArgs args;
+ args.pixelFormat = ui::PixelFormat::RGB_565;
+ args.sourceCrop = Rect(0, 0, 500, 200);
+ args.frameScale = 2;
+ args.captureSecureLayers = true;
+ args.displayToken = new BBinder();
+ args.width = 10;
+ args.height = 20;
+ args.useIdentityTransform = true;
+
+ Parcel p;
+ args.write(p);
+ p.setDataPosition(0);
+
+ DisplayCaptureArgs args2;
+ args2.read(p);
+
+ ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
+ ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
+ ASSERT_EQ(args.frameScale, args2.frameScale);
+ ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers);
+ ASSERT_EQ(args.displayToken, args2.displayToken);
+ ASSERT_EQ(args.width, args2.width);
+ ASSERT_EQ(args.height, args2.height);
+ ASSERT_EQ(args.useIdentityTransform, args2.useIdentityTransform);
+}
+
+TEST(LayerStateTest, ParcellingLayerCaptureArgs) {
+ LayerCaptureArgs args;
+ args.pixelFormat = ui::PixelFormat::RGB_565;
+ args.sourceCrop = Rect(0, 0, 500, 200);
+ args.frameScale = 2;
+ args.captureSecureLayers = true;
+ args.layerHandle = new BBinder();
+ args.excludeHandles = {new BBinder(), new BBinder()};
+ args.childrenOnly = false;
+
+ Parcel p;
+ args.write(p);
+ p.setDataPosition(0);
+
+ LayerCaptureArgs args2;
+ args2.read(p);
+
+ ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
+ ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
+ ASSERT_EQ(args.frameScale, args2.frameScale);
+ ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers);
+ ASSERT_EQ(args.layerHandle, args2.layerHandle);
+ ASSERT_EQ(args.excludeHandles, args2.excludeHandles);
+ ASSERT_EQ(args.childrenOnly, args2.childrenOnly);
+}
+
+TEST(LayerStateTest, ParcellingScreenCaptureResults) {
+ ScreenCaptureResults results;
+ results.buffer = new GraphicBuffer(100, 200, PIXEL_FORMAT_RGBA_8888, 1, 0);
+ results.capturedSecureLayers = true;
+ results.capturedDataspace = ui::Dataspace::DISPLAY_P3;
+
+ Parcel p;
+ results.write(p);
+ p.setDataPosition(0);
+
+ ScreenCaptureResults results2;
+ results2.read(p);
+
+ // GraphicBuffer object is reallocated so compare the data in the graphic buffer
+ // rather than the object itself
+ ASSERT_EQ(results.buffer->getWidth(), results2.buffer->getWidth());
+ ASSERT_EQ(results.buffer->getHeight(), results2.buffer->getHeight());
+ ASSERT_EQ(results.buffer->getPixelFormat(), results2.buffer->getPixelFormat());
+ ASSERT_EQ(results.capturedSecureLayers, results2.capturedSecureLayers);
+ ASSERT_EQ(results.capturedDataspace, results2.capturedDataspace);
+}
+
+} // namespace test
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index f3e11d8..d4e952a 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -40,6 +40,8 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
+
+ mCaptureArgs.displayToken = mDisplay;
}
virtual void TearDown() {
@@ -249,6 +251,9 @@
sp<SurfaceControl> mBlackBgSurface;
bool mColorManagementUsed;
+ DisplayCaptureArgs mCaptureArgs;
+ ScreenCaptureResults mCaptureResults;
+
private:
void SetUpDisplay() {
mDisplay = mClient->getInternalDisplayToken();
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index 1f8f7ed..8d715e1 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -18,7 +18,6 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-#include <private/android_filesystem_config.h>
#include <thread>
#include "LayerTransactionTest.h"
@@ -26,43 +25,6 @@
using android::hardware::graphics::common::V1_1::BufferUsage;
-TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
- sp<GraphicBuffer> outBuffer;
- Transaction()
- .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
- .apply(true);
- ASSERT_EQ(PERMISSION_DENIED,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
- 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->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
- {
- SCOPED_TRACE("as system");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-
- // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
- // to receive them...we are expected to take care with the results.
- bool outCapturedSecureLayers;
- ASSERT_EQ(NO_ERROR,
- composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers,
- ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0,
- 0, false, ui::ROTATION_0, true));
- ASSERT_EQ(true, outCapturedSecureLayers);
- ScreenCapture sc(outBuffer);
- sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index 84780ba..e3b9489 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -61,8 +61,10 @@
std::unique_ptr<ScreenCapture> screenshot;
// only layerB is in this range
- sp<IBinder> parentHandle = parent->getHandle();
- ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = parent->getHandle();
+ captureArgs.sourceCrop = {0, 0, 32, 32};
+ ScreenCapture::captureLayers(&screenshot, captureArgs);
screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
}
@@ -170,11 +172,15 @@
Transaction()
.setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
.apply(true);
- ASSERT_EQ(PERMISSION_DENIED,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+ DisplayCaptureArgs args;
+ args.displayToken = mDisplay;
+
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(PERMISSION_DENIED, composer->captureDisplay(args, captureResults));
Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
- ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+ ASSERT_EQ(NO_ERROR, composer->captureDisplay(args, captureResults));
}
TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
sp<SurfaceControl> layer;
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index cdd9d92..c56d473 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -103,41 +103,6 @@
sp<SurfaceControl> mSyncSurfaceControl;
};
-TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
- std::unique_ptr<ScreenCapture> sc;
-
- sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
- TransactionUtils::fillSurfaceRGBA8(relative, 10, 10, 10);
- waitForPostedBuffers();
-
- Transaction{}
- .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
- .setPosition(relative, 64, 64)
- .apply();
-
- {
- // The relative should be on top of the FG control.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(64, 64, 10, 10, 10);
- }
- Transaction{}.detachChildren(mFGSurfaceControl).apply();
-
- {
- // Nothing should change at this point.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(64, 64, 10, 10, 10);
- }
-
- Transaction{}.hide(relative).apply();
-
- {
- // Ensure that the relative was actually hidden, rather than
- // being left in the detached but visible state.
- ScreenCapture::captureScreen(&sc);
- sc->expectFGColor(64, 64);
- }
-}
-
class GeometryLatchingTest : public LayerUpdateTest {
protected:
void EXPECT_INITIAL_STATE(const char* trace) {
@@ -588,174 +553,6 @@
}
}
-TEST_F(ChildLayerTest, DetachChildrenSameClient) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
- asTransaction([&](Transaction& t) { t.hide(mChild); });
-
- // Since the child has the same client as the parent, it will not get
- // detached and will be hidden.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectFGColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
- sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> mChildNewClient =
- createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(mChildNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
-
- asTransaction([&](Transaction& t) {
- t.hide(mChild);
- t.show(mChildNewClient);
- t.setPosition(mChildNewClient, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
- asTransaction([&](Transaction& t) { t.hide(mChildNewClient); });
-
- // Nothing should have changed.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectChildColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
- sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> childNewClient =
- newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(childNewClient != nullptr);
- ASSERT_TRUE(childNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
- Transaction()
- .hide(mChild)
- .show(childNewClient)
- .setPosition(childNewClient, 10, 10)
- .setPosition(mFGSurfaceControl, 64, 64)
- .apply();
-
- {
- mCapture = screenshot();
- // Top left of foreground must now be visible
- mCapture->expectFGColor(64, 64);
- // But 10 pixels in we should see the child surface
- mCapture->expectChildColor(74, 74);
- // And 10 more pixels we should be back to the foreground surface
- mCapture->expectFGColor(84, 84);
- }
-
- Transaction().detachChildren(mFGSurfaceControl).apply();
- Transaction().hide(childNewClient).apply();
-
- // Nothing should have changed.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectChildColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-
- sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
- fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
- 32);
- Transaction()
- .setLayer(newParentSurface, INT32_MAX - 1)
- .show(newParentSurface)
- .setPosition(newParentSurface, 20, 20)
- .reparent(childNewClient, newParentSurface->getHandle())
- .apply();
- {
- mCapture = screenshot();
- // Child is now hidden.
- mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
- }
-}
-TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) {
- sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> childNewClient =
- newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(childNewClient != nullptr);
- ASSERT_TRUE(childNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
- Transaction()
- .hide(mChild)
- .show(childNewClient)
- .setPosition(childNewClient, 10, 10)
- .setPosition(mFGSurfaceControl, 64, 64)
- .apply();
-
- {
- mCapture = screenshot();
- Rect rect = Rect(74, 74, 84, 84);
- mCapture->expectBorder(rect, Color{195, 63, 63, 255});
- mCapture->expectColor(rect, Color{200, 200, 200, 255});
- }
-
- Transaction()
- .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
- mFGSurfaceControl->getSurface()->getNextFrameNumber())
- .apply();
- Transaction().detachChildren(mFGSurfaceControl).apply();
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
-
- // BufferLayer can still dequeue buffers even though there's a detached layer with a
- // deferred transaction.
- {
- SCOPED_TRACE("new buffer");
- mCapture = screenshot();
- Rect rect = Rect(74, 74, 84, 84);
- mCapture->expectBorder(rect, Color::RED);
- mCapture->expectColor(rect, Color{200, 200, 200, 255});
- }
-}
-
TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
asTransaction([&](Transaction& t) {
t.show(mChild);
@@ -1287,432 +1084,6 @@
}
}
-class ScreenCaptureTest : public LayerUpdateTest {
-protected:
- std::unique_ptr<ScreenCapture> mCapture;
-};
-
-TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
- auto bgHandle = mBGSurfaceControl->getHandle();
- ScreenCapture::captureLayers(&mCapture, bgHandle);
- mCapture->expectBGColor(0, 0);
- // Doesn't capture FG layer which is at 64, 64
- mCapture->expectBGColor(64, 64);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- // Captures mFGSurfaceControl layer and its child.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- // Captures mFGSurfaceControl's child
- ScreenCapture::captureChildLayers(&mCapture, fgHandle);
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- .show(child2)
- .setLayer(child, 1)
- .setLayer(child2, 2)
- .apply(true);
-
- // Child2 would be visible but its excluded, so we should see child1 color instead.
- ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-// Like the last test but verifies that children are also exclude.
-TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
- sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, child2.get());
- TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- .show(child2)
- .show(child3)
- .setLayer(child, 1)
- .setLayer(child2, 2)
- .apply(true);
-
- // Child2 would be visible but its excluded, so we should see child1 color instead.
- ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-TEST_F(ScreenCaptureTest, CaptureTransparent) {
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- auto childHandle = child->getHandle();
-
- // Captures child
- ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20});
- mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
- // Area outside of child's bounds is transparent.
- mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
-}
-
-TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- ASSERT_NE(nullptr, child.get()) << "failed to create surface";
- sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- // Set relative layer above fg layer so should be shown above when computing all layers.
- .setRelativeLayer(relative, fgHandle, 1)
- .show(relative)
- .apply(true);
-
- // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- // Set relative layer below fg layer but relative to child layer so it should be shown
- // above child layer.
- .setLayer(relative, -1)
- .setRelativeLayer(relative, child->getHandle(), 1)
- .show(relative)
- .apply(true);
-
- // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
- // relative value should be taken into account, placing it above child layer.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- // Relative layer is showing on top of child layer
- mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
-}
-
-TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
- sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop(0, 0, 10, 10);
- ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
- ScreenCapture sc(outBuffer);
-
- sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
-}
-
-TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
- sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
- Rect layerCrop(0, 0, 10, 10);
- SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
-
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop = Rect();
- ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
- ScreenCapture sc(outBuffer);
-
- sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
-}
-
-TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
- sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
- SurfaceComposerClient::Transaction().show(child).apply(true);
-
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<GraphicBuffer> outBuffer;
- Rect sourceCrop = Rect();
-
- ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
-}
-
-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;
- Rect sourceCrop = Rect();
- ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
-
- TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
- SurfaceComposerClient::Transaction().apply(true);
- ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
- ScreenCapture sc(outBuffer);
- sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
-}
-
-// In the following tests we verify successful skipping of a parent layer,
-// so we use the same verification logic and only change how we mutate
-// the parent layer to verify that various properties are ignored.
-class ScreenCaptureChildOnlyTest : public LayerUpdateTest {
-public:
- void SetUp() override {
- LayerUpdateTest::SetUp();
-
- mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
- mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
-
- SurfaceComposerClient::Transaction().show(mChild).apply(true);
- }
-
- void verify(std::function<void()> verifyStartingState) {
- // Verify starting state before a screenshot is taken.
- verifyStartingState();
-
- // Verify child layer does not inherit any of the properties of its
- // parent when its screenshot is captured.
- auto fgHandle = mFGSurfaceControl->getHandle();
- ScreenCapture::captureChildLayers(&mCapture, fgHandle);
- mCapture->checkPixel(10, 10, 0, 0, 0);
- mCapture->expectChildColor(0, 0);
-
- // Verify all assumptions are still true after the screenshot is taken.
- verifyStartingState();
- }
-
- std::unique_ptr<ScreenCapture> mCapture;
- sp<SurfaceControl> mChild;
-};
-
-// Regression test b/76099859
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
- SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
-
- // Even though the parent is hidden we should still capture the child.
-
- // Before and after reparenting, verify child is properly hidden
- // when rendering full-screen.
- verify([&] { screenshot()->expectBGColor(64, 64); });
-}
-
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
- SurfaceComposerClient::Transaction()
- .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
- .apply(true);
-
- // Even though the parent is cropped out we should still capture the child.
-
- // Before and after reparenting, verify child is cropped by parent.
- verify([&] { screenshot()->expectBGColor(65, 65); });
-}
-
-// Regression test b/124372894
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
- SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
-
- // We should not inherit the parent scaling.
-
- // Before and after reparenting, verify child is properly scaled.
- verify([&] { screenshot()->expectChildColor(80, 80); });
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
-
- sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
- PIXEL_FORMAT_RGBA_8888, 0, child.get());
-
- TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
- SurfaceComposerClient::Transaction()
- .show(child)
- .setPosition(grandchild, 5, 5)
- .show(grandchild)
- .apply(true);
-
- // Captures mFGSurfaceControl, its child, and the grandchild.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
- mCapture->expectFGColor(10, 10);
- mCapture->expectChildColor(0, 0);
- mCapture->checkPixel(5, 5, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureChildOnly) {
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- auto childHandle = child->getHandle();
-
- SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
-
- // Captures only the child layer, and not the parent.
- ScreenCapture::captureLayers(&mCapture, childHandle);
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(9, 9);
-}
-
-TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
- sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- auto childHandle = child->getHandle();
-
- sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
- PIXEL_FORMAT_RGBA_8888, 0, child.get());
- TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
-
- SurfaceComposerClient::Transaction()
- .show(child)
- .setPosition(grandchild, 5, 5)
- .show(grandchild)
- .apply(true);
-
- auto grandchildHandle = grandchild->getHandle();
-
- // Captures only the grandchild.
- ScreenCapture::captureLayers(&mCapture, grandchildHandle);
- mCapture->checkPixel(0, 0, 50, 50, 50);
- mCapture->checkPixel(4, 4, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureCrop) {
- sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
- sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
- PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
- SurfaceComposerClient::Transaction()
- .setLayer(redLayer, INT32_MAX - 1)
- .show(redLayer)
- .show(blueLayer)
- .apply(true);
-
- auto redLayerHandle = redLayer->getHandle();
-
- // Capturing full screen should have both red and blue are visible.
- ScreenCapture::captureLayers(&mCapture, redLayerHandle);
- mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
- // red area below the blue area
- mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
- // red area to the right of the blue area
- mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
- const Rect crop = Rect(0, 0, 30, 30);
- ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop);
- // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
- // area visible.
- mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
- mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureSize) {
- sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
- sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
- PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
- SurfaceComposerClient::Transaction()
- .setLayer(redLayer, INT32_MAX - 1)
- .show(redLayer)
- .show(blueLayer)
- .apply(true);
-
- auto redLayerHandle = redLayer->getHandle();
-
- // Capturing full screen should have both red and blue are visible.
- ScreenCapture::captureLayers(&mCapture, redLayerHandle);
- mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
- // red area below the blue area
- mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
- // red area to the right of the blue area
- mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
- ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5);
- // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
- mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
- // red area below the blue area
- mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
- // red area to the right of the blue area
- mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
- mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
- sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-
- auto redLayerHandle = redLayer->getHandle();
- Transaction().reparent(redLayer, nullptr).apply();
- redLayer.clear();
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
-
- // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0));
-}
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 06e8761..db0c56f 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -90,7 +90,7 @@
};
TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
- createDisplay(mMainDisplayState.viewport, 1 /* layerStack */);
+ createDisplay(mMainDisplayState.layerStackSpaceRect, 1 /* layerStack */);
createColorLayer(1 /* layerStack */);
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
@@ -111,9 +111,9 @@
// Create a display and set its layer stack to the main display's layer stack so
// the contents of the main display are mirrored on to the virtual display.
- // Assumption here is that the new mirrored display has the same viewport as the
+ // Assumption here is that the new mirrored display has the same layer stack rect as the
// primary display that it is mirroring.
- createDisplay(mMainDisplayState.viewport, 0 /* layerStack */);
+ createDisplay(mMainDisplayState.layerStackSpaceRect, 0 /* layerStack */);
createColorLayer(0 /* layerStack */);
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
new file mode 100644
index 0000000..690f758
--- /dev/null
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -0,0 +1,723 @@
+/*
+ * 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 <private/android_filesystem_config.h>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class ScreenCaptureTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ DisplayConfig config;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+ const ui::Size& resolution = config.resolution;
+
+ // Background surface
+ mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
+ resolution.getHeight(), 0);
+ ASSERT_TRUE(mBGSurfaceControl != nullptr);
+ ASSERT_TRUE(mBGSurfaceControl->isValid());
+ TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
+
+ // Foreground surface
+ mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+
+ ASSERT_TRUE(mFGSurfaceControl != nullptr);
+ ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+
+ asTransaction([&](Transaction& t) {
+ t.setDisplayLayerStack(display, 0);
+
+ t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
+
+ t.setLayer(mFGSurfaceControl, INT32_MAX - 1)
+ .setPosition(mFGSurfaceControl, 64, 64)
+ .show(mFGSurfaceControl);
+ });
+ }
+
+ virtual void TearDown() {
+ LayerTransactionTest::TearDown();
+ mBGSurfaceControl = 0;
+ mFGSurfaceControl = 0;
+ }
+
+ sp<SurfaceControl> mBGSurfaceControl;
+ sp<SurfaceControl> mFGSurfaceControl;
+ std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32,
+ ISurfaceComposerClient::eSecure |
+ ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
+
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ ASSERT_EQ(PERMISSION_DENIED, composer->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));
+
+ {
+ SCOPED_TRACE("as system");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
+ // to receive them...we are expected to take care with the results.
+ DisplayCaptureArgs args;
+ args.displayToken = mDisplay;
+ args.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, composer->captureDisplay(args, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer);
+ sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectBGColor(0, 0);
+ // Doesn't capture FG layer which is at 64, 64
+ mCapture->expectBGColor(64, 64);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ // Captures mFGSurfaceControl layer and its child.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ // Captures mFGSurfaceControl's child
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = fgHandle;
+ captureArgs.childrenOnly = true;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .show(child2)
+ .setLayer(child, 1)
+ .setLayer(child2, 2)
+ .apply(true);
+
+ // Child2 would be visible but its excluded, so we should see child1 color instead.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = fgHandle;
+ captureArgs.childrenOnly = true;
+ captureArgs.excludeHandles = {child2->getHandle()};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+// Like the last test but verifies that children are also exclude.
+TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+ sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, child2.get());
+ TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .show(child2)
+ .show(child3)
+ .setLayer(child, 1)
+ .setLayer(child2, 2)
+ .apply(true);
+
+ // Child2 would be visible but its excluded, so we should see child1 color instead.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = fgHandle;
+ captureArgs.childrenOnly = true;
+ captureArgs.excludeHandles = {child2->getHandle()};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+TEST_F(ScreenCaptureTest, CaptureTransparent) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ // Captures child
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ captureArgs.sourceCrop = {0, 0, 10, 20};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
+ // Area outside of child's bounds is transparent.
+ mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
+}
+
+TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ ASSERT_NE(nullptr, child.get()) << "failed to create surface";
+ sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ // Set relative layer above fg layer so should be shown above when computing all layers.
+ .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
+ .show(relative)
+ .apply(true);
+
+ // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ // Set relative layer below fg layer but relative to child layer so it should be shown
+ // above child layer.
+ .setLayer(relative, -1)
+ .setRelativeLayer(relative, child->getHandle(), 1)
+ .show(relative)
+ .apply(true);
+
+ // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
+ // relative value should be taken into account, placing it above child layer.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectFGColor(10, 10);
+ // Relative layer is showing on top of child layer
+ mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ captureArgs.sourceCrop = {0, 0, 10, 10};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ Rect layerCrop(0, 0, 10, 10);
+ SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
+ 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));
+}
+
+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;
+ args.layerHandle = child->getHandle();
+ args.childrenOnly = false;
+
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(BAD_VALUE, sf->captureLayers(args, captureResults));
+
+ TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
+ SurfaceComposerClient::Transaction().apply(true);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(args, captureResults));
+ ScreenCapture sc(captureResults.buffer);
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
+
+ TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .setPosition(grandchild, 5, 5)
+ .show(grandchild)
+ .apply(true);
+
+ // Captures mFGSurfaceControl, its child, and the grandchild.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+ mCapture->checkPixel(5, 5, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureChildOnly) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
+
+ // Captures only the child layer, and not the parent.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 9);
+}
+
+TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+ auto childHandle = child->getHandle();
+
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
+ TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .setPosition(grandchild, 5, 5)
+ .show(grandchild)
+ .apply(true);
+
+ // Captures only the grandchild.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = grandchild->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->checkPixel(0, 0, 50, 50, 50);
+ mCapture->checkPixel(4, 4, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureCrop) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+ sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+ PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+ SurfaceComposerClient::Transaction()
+ .setLayer(redLayer, INT32_MAX - 1)
+ .show(redLayer)
+ .show(blueLayer)
+ .apply(true);
+
+ // Capturing full screen should have both red and blue are visible.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = redLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+ // red area below the blue area
+ mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+ // red area to the right of the blue area
+ mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+ captureArgs.sourceCrop = {0, 0, 30, 30};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
+ // area visible.
+ mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+ mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureSize) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+ sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+ PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+ SurfaceComposerClient::Transaction()
+ .setLayer(redLayer, INT32_MAX - 1)
+ .show(redLayer)
+ .show(blueLayer)
+ .apply(true);
+
+ // Capturing full screen should have both red and blue are visible.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = redLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+ // red area below the blue area
+ mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+ // red area to the right of the blue area
+ mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+ captureArgs.frameScale = 0.5f;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
+ mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
+ // red area below the blue area
+ mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
+ // red area to the right of the blue area
+ mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
+ mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+
+ auto redLayerHandle = redLayer->getHandle();
+ Transaction().reparent(redLayer, nullptr).apply();
+ redLayer.clear();
+ SurfaceComposerClient::Transaction().apply(true);
+
+ LayerCaptureArgs args;
+ 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));
+}
+
+TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+ sp<SurfaceControl> secureLayer =
+ createLayer(String8("Secure surface"), 30, 30,
+ ISurfaceComposerClient::eSecure |
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ redLayer.get());
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(secureLayer, Color::BLUE, 30, 30));
+
+ auto redLayerHandle = redLayer->getHandle();
+ Transaction()
+ .show(redLayer)
+ .show(secureLayer)
+ .setLayerStack(redLayer, 0)
+ .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));
+
+ UIDFaker f(AID_SYSTEM);
+
+ // From system request, only red layer will be screenshot since the blue layer is secure.
+ // Black will be present where the secure layer is.
+ ScreenCapture::captureLayers(&mCapture, args);
+ mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLACK);
+ mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
+
+ // Passing flag secure so the blue layer should be screenshot too.
+ args.captureSecureLayers = true;
+ ScreenCapture::captureLayers(&mCapture, args);
+ mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLUE);
+ mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayWithUid) {
+ uid_t fakeUid = 12345;
+
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+
+ // Make sure red layer with the background layer is screenshot.
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+
+ // 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));
+
+ // Make screenshot request with current uid set. No layers were created with the current
+ // uid so screenshot will be black.
+ captureArgs.uid = fakeUid;
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+
+ sp<SurfaceControl> layerWithFakeUid;
+ // Create a new layer with the current uid
+ ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
+ createLayer("new test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
+ Transaction()
+ .show(layerWithFakeUid)
+ .setLayer(layerWithFakeUid, INT32_MAX)
+ .setPosition(layerWithFakeUid, 128, 128)
+ .apply();
+
+ // Screenshot from the fakeUid caller with the uid requested allows the layer
+ // with that uid to be screenshotted. Everything else is black
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), Color::BLACK);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithUid) {
+ uid_t fakeUid = 12345;
+
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+ captureArgs.childrenOnly = false;
+
+ // Make sure red layer with the background layer is screenshot.
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+
+ // 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));
+
+ // Make screenshot request with current uid set. No layers were created with the current
+ // uid so screenshot will be black.
+ captureArgs.uid = fakeUid;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::TRANSPARENT);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), Color::TRANSPARENT);
+
+ sp<SurfaceControl> layerWithFakeUid;
+ // Create a new layer with the current uid
+ ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
+ createLayer("new test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
+ Transaction()
+ .show(layerWithFakeUid)
+ .setLayer(layerWithFakeUid, INT32_MAX)
+ .setPosition(layerWithFakeUid, 128, 128)
+ // reparent a layer that was created with a different uid to the new layer.
+ .reparent(layer, layerWithFakeUid->getHandle())
+ .apply();
+
+ // Screenshot from the fakeUid caller with the uid requested allows the layer
+ // with that uid to be screenshotted. The child layer is skipped since it was created
+ // from a different uid.
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT);
+
+ // Clear fake calling uid so it's back to system.
+ uidFaker = nullptr;
+ // Screenshot from the test caller with the uid requested allows the layer
+ // with that uid to be screenshotted. The child layer is skipped since it was created
+ // from a different uid.
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT);
+
+ // Screenshot from the fakeUid caller with no uid requested allows everything to be screenshot.
+ captureArgs.uid = -1;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::RED);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), {63, 63, 195, 255});
+}
+
+// In the following tests we verify successful skipping of a parent layer,
+// so we use the same verification logic and only change how we mutate
+// the parent layer to verify that various properties are ignored.
+class ScreenCaptureChildOnlyTest : public ScreenCaptureTest {
+public:
+ void SetUp() override {
+ ScreenCaptureTest::SetUp();
+
+ mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
+ TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction().show(mChild).apply(true);
+ }
+
+ void verify(std::function<void()> verifyStartingState) {
+ // Verify starting state before a screenshot is taken.
+ verifyStartingState();
+
+ // Verify child layer does not inherit any of the properties of its
+ // parent when its screenshot is captured.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ captureArgs.childrenOnly = true;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->expectChildColor(0, 0);
+
+ // Verify all assumptions are still true after the screenshot is taken.
+ verifyStartingState();
+ }
+
+ std::unique_ptr<ScreenCapture> mCapture;
+ sp<SurfaceControl> mChild;
+};
+
+// Regression test b/76099859
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
+ SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
+
+ // Even though the parent is hidden we should still capture the child.
+
+ // Before and after reparenting, verify child is properly hidden
+ // when rendering full-screen.
+ verify([&] { screenshot()->expectBGColor(64, 64); });
+}
+
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
+ SurfaceComposerClient::Transaction()
+ .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
+ .apply(true);
+
+ // Even though the parent is cropped out we should still capture the child.
+
+ // Before and after reparenting, verify child is cropped by parent.
+ verify([&] { screenshot()->expectBGColor(65, 65); });
+}
+
+// Regression test b/124372894
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
+ SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
+
+ // We should not inherit the parent scaling.
+
+ // Before and after reparenting, verify child is properly scaled.
+ verify([&] { screenshot()->expectChildColor(80, 80); });
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index f0af363..01badf4 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -65,7 +65,7 @@
t.setDisplaySurface(vDisplay, producer);
t.setDisplayLayerStack(vDisplay, 0);
t.setDisplayProjection(vDisplay, displayState.orientation,
- Rect(displayState.viewport), Rect(resolution));
+ Rect(displayState.layerStackSpaceRect), Rect(resolution));
t.apply();
SurfaceComposerClient::Transaction().apply(true);
BufferItem item;
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index a03fd89..87fc08a 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -72,6 +72,9 @@
///////////////////////////////////////////////
+constexpr PhysicalDisplayId kPrimaryDisplayId = PhysicalDisplayId::fromPort(PRIMARY_DISPLAY);
+constexpr PhysicalDisplayId kExternalDisplayId = PhysicalDisplayId::fromPort(EXTERNAL_DISPLAY);
+
struct TestColor {
public:
uint8_t r;
@@ -272,6 +275,10 @@
mFakeComposerClient->runVSyncAndWait();
}
+ bool waitForHotplugEvent(Display displayId, bool connected) {
+ return waitForHotplugEvent(PhysicalDisplayId(displayId), connected);
+ }
+
bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
int waitCount = 20;
while (waitCount--) {
@@ -280,9 +287,8 @@
mReceivedDisplayEvents.pop_front();
ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
- "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
- ", connected %d\t",
- event.header.displayId, event.hotplug.connected);
+ "event hotplug: displayId %s, connected %d\t",
+ to_string(event.header.displayId).c_str(), event.hotplug.connected);
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
event.header.displayId == displayId && event.hotplug.connected == connected) {
@@ -295,7 +301,8 @@
return false;
}
- bool waitForConfigChangedEvent(PhysicalDisplayId displayId, int32_t configId) {
+ bool waitForConfigChangedEvent(Display display, int32_t configId) {
+ PhysicalDisplayId displayId(display);
int waitCount = 20;
while (waitCount--) {
while (!mReceivedDisplayEvents.empty()) {
@@ -303,9 +310,8 @@
mReceivedDisplayEvents.pop_front();
ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED,
- "event config: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
- ", configId %d\t",
- event.header.displayId, event.config.configId);
+ "event config: displayId %s, configId %d\t",
+ to_string(event.header.displayId).c_str(), event.config.configId);
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
event.header.displayId == displayId && event.config.configId == configId) {
@@ -335,7 +341,7 @@
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
{
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
EXPECT_FALSE(display == nullptr);
DisplayConfig config;
@@ -367,7 +373,7 @@
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
{
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
EXPECT_TRUE(display == nullptr);
DisplayConfig config;
@@ -396,7 +402,7 @@
waitForDisplayTransaction();
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
EXPECT_FALSE(display == nullptr);
DisplayConfig config;
@@ -503,7 +509,7 @@
waitForDisplayTransaction();
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
EXPECT_FALSE(display == nullptr);
DisplayConfig config;
@@ -619,7 +625,7 @@
waitForDisplayTransaction();
EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
EXPECT_FALSE(display == nullptr);
DisplayConfig config;
@@ -808,7 +814,7 @@
EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
{
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
EXPECT_TRUE(display == nullptr);
DisplayConfig config;
@@ -834,7 +840,7 @@
EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
{
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
EXPECT_FALSE(display == nullptr);
DisplayConfig config;
@@ -988,7 +994,7 @@
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
ALOGI("TransactionTest::SetUp - display");
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
ASSERT_FALSE(display == nullptr);
DisplayConfig config;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 3c4a791..7cc9cef 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -40,14 +40,12 @@
"DispSyncSourceTest.cpp",
"DisplayIdentificationTest.cpp",
"DisplayTransactionTest.cpp",
- "EventControlThreadTest.cpp",
"EventThreadTest.cpp",
"HWComposerTest.cpp",
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
"LayerHistoryTestV2.cpp",
"LayerMetadataTest.cpp",
- "PhaseOffsetsTest.cpp",
"PromiseTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
@@ -62,14 +60,14 @@
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
- "VSyncModulatorTest.cpp",
+ "VsyncModulatorTest.cpp",
"VSyncPredictorTest.cpp",
"VSyncReactorTest.cpp",
+ "VsyncConfigurationTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/MockDispSync.cpp",
- "mock/MockEventControlThread.cpp",
"mock/MockEventThread.cpp",
"mock/MockMessageQueue.cpp",
"mock/MockNativeWindowSurface.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 32d722e..4843f05 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -35,13 +35,13 @@
#include <utils/String8.h>
#include "BufferQueueLayer.h"
+#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
#include "mock/MockTimeStats.h"
@@ -80,7 +80,7 @@
constexpr hal::HWLayerId HWC_LAYER = 5000;
constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
-constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(42);
constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
@@ -149,9 +149,13 @@
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(primaryDispSync),
- std::make_unique<mock::EventControlThread>(),
- std::move(eventThread), std::move(sfEventThread));
+ constexpr ISchedulerCallback* kCallback = nullptr;
+ constexpr bool kHasMultipleConfigs = true;
+ mFlinger.setupScheduler(std::move(primaryDispSync), std::move(eventThread),
+ std::move(sfEventThread), kCallback, kHasMultipleConfigs);
+
+ // Layer history should be created if there are multiple configs.
+ ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
}
void setupForceGeometryDirty() {
@@ -229,28 +233,27 @@
LayerCase::setupForScreenCapture(this);
const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
- constexpr bool useIdentityTransform = true;
constexpr bool forSystem = true;
constexpr bool regionSampling = false;
- DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB,
- ui::Transform::ROT_0);
+ auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
+ ui::Dataspace::V0_SRGB, ui::Transform::ROT_0);
auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
- return mFlinger.traverseLayersInDisplay(mDisplay, visitor);
+ return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(),
+ CaptureArgs::UNSET_UID, visitor);
};
// TODO: Eliminate expensive/real allocation if possible.
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
- mCaptureScreenBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
+ mCaptureScreenBuffer = new GraphicBuffer(renderArea->getReqWidth(), renderArea->getReqHeight(),
HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
int fd = -1;
status_t result =
- mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(),
- useIdentityTransform, forSystem, &fd, regionSampling);
+ mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer.get(),
+ forSystem, &fd, regionSampling);
if (fd >= 0) {
close(fd);
}
@@ -348,7 +351,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -397,7 +400,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -648,7 +651,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -697,7 +700,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -775,7 +778,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -891,7 +894,7 @@
static FlingerLayerType createLayer(CompositionTest* test) {
FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
return new EffectLayer(
- LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), "test-layer",
+ LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::LAYER_FLAGS, LayerMetadata()));
});
@@ -930,8 +933,7 @@
FlingerLayerType layer =
Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
- sp<Client> client;
- LayerCreationArgs args(test->mFlinger.mFlinger.get(), client, "test-layer",
+ LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::LAYER_FLAGS, LayerMetadata());
args.textureName = test->mFlinger.mutableTexturePool().back();
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index 2a0e913..02ce079 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -22,6 +22,8 @@
#include "DisplayHardware/DisplayIdentification.h"
+using ::testing::ElementsAre;
+
namespace android {
namespace {
@@ -312,93 +314,92 @@
using ManufactureYear = DeviceProductInfo::ManufactureYear;
using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear;
using ModelYear = DeviceProductInfo::ModelYear;
- using RelativeAddress = DeviceProductInfo::RelativeAddress;
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getInternalEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("", info.name.data());
+ EXPECT_EQ("", info.name);
EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
- EXPECT_STREQ("12610", info.productId.data());
+ EXPECT_EQ("12610", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
EXPECT_EQ(2011, std::get<ManufactureYear>(info.manufactureOrModelDate).year);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("HP ZR30w", info.name.data());
+ EXPECT_EQ("HP ZR30w", info.name);
EXPECT_STREQ("HWP", info.manufacturerPnpId.data());
- EXPECT_STREQ("10348", info.productId.data());
+ EXPECT_EQ("10348", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2012, date.year);
EXPECT_EQ(2, date.week);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEedid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("SAMSUNG", info.name.data());
+ EXPECT_EQ("SAMSUNG", info.name);
EXPECT_STREQ("SAM", info.manufacturerPnpId.data());
- EXPECT_STREQ("2302", info.productId.data());
+ EXPECT_EQ("2302", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2011, date.year);
EXPECT_EQ(41, date.week);
- EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getPanasonicTvEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("Panasonic-TV", info.name.data());
+ EXPECT_EQ("Panasonic-TV", info.name);
EXPECT_STREQ("MEI", info.manufacturerPnpId.data());
- EXPECT_STREQ("41622", info.productId.data());
+ EXPECT_EQ("41622", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureYear>(info.manufactureOrModelDate);
EXPECT_EQ(2019, date.year);
- EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getHisenseTvEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("Hisense", info.name.data());
+ EXPECT_EQ("Hisense", info.name);
EXPECT_STREQ("HEC", info.manufacturerPnpId.data());
- EXPECT_STREQ("0", info.productId.data());
+ EXPECT_EQ("0", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2019, date.year);
EXPECT_EQ(18, date.week);
- EXPECT_EQ((RelativeAddress{{1, 2, 3, 4}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(1, 2, 3, 4));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getCtlDisplayEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("LP2361", info.name.data());
+ EXPECT_EQ("LP2361", info.name);
EXPECT_STREQ("CTL", info.manufacturerPnpId.data());
- EXPECT_STREQ("9373", info.productId.data());
+ EXPECT_EQ("9373", info.productId);
ASSERT_TRUE(std::holds_alternative<ModelYear>(info.manufactureOrModelDate));
EXPECT_EQ(2013, std::get<ModelYear>(info.manufactureOrModelDate).year);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
}
-TEST(DisplayIdentificationTest, getFallbackDisplayId) {
+TEST(DisplayIdentificationTest, fromPort) {
// Manufacturer ID should be invalid.
- ASSERT_FALSE(getPnpId(getFallbackDisplayId(0)));
- ASSERT_FALSE(getPnpId(getFallbackDisplayId(0xffu)));
+ ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0)));
+ ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0xffu)));
}
TEST(DisplayIdentificationTest, getVirtualDisplayId) {
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 06bdcdc..7fade0d 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -23,6 +23,7 @@
#include <type_traits>
+#include <android/hardware/power/Boost.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/impl/Display.h>
@@ -45,10 +46,10 @@
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
#include "mock/MockNativeWindowSurface.h"
+#include "mock/MockSchedulerCallback.h"
#include "mock/MockSurfaceInterceptor.h"
#include "mock/system/window/MockNativeWindow.h"
@@ -57,6 +58,8 @@
namespace hal = android::hardware::graphics::composer::hal;
+using android::hardware::power::Boost;
+
using testing::_;
using testing::AnyNumber;
using testing::DoAll;
@@ -153,7 +156,7 @@
mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
mock::DispSync* mPrimaryDispSync = new mock::DispSync;
- mock::EventControlThread* mEventControlThread = new mock::EventControlThread;
+ mock::SchedulerCallback mSchedulerCallback;
mock::EventThread* mEventThread = new mock::EventThread;
mock::EventThread* mSFEventThread = new mock::EventThread;
@@ -211,9 +214,8 @@
ISurfaceComposer::eConfigChangedSuppress)));
mFlinger.setupScheduler(std::unique_ptr<DispSync>(mPrimaryDispSync),
- std::unique_ptr<EventControlThread>(mEventControlThread),
std::unique_ptr<EventThread>(mEventThread),
- std::unique_ptr<EventThread>(mSFEventThread));
+ std::unique_ptr<EventThread>(mSFEventThread), &mSchedulerCallback);
}
void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) {
@@ -250,7 +252,7 @@
sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
- constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
+ constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(777);
constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
@@ -329,10 +331,10 @@
*/
template <typename PhysicalDisplay>
-struct PhysicalDisplayId {};
+struct PhysicalDisplayIdType {};
-template <DisplayId::Type displayId>
-using VirtualDisplayId = std::integral_constant<DisplayId::Type, displayId>;
+template <uint64_t displayId>
+using VirtualDisplayIdType = std::integral_constant<uint64_t, displayId>;
struct NoDisplayId {};
@@ -340,18 +342,18 @@
struct IsPhysicalDisplayId : std::bool_constant<false> {};
template <typename PhysicalDisplay>
-struct IsPhysicalDisplayId<PhysicalDisplayId<PhysicalDisplay>> : std::bool_constant<true> {};
+struct IsPhysicalDisplayId<PhysicalDisplayIdType<PhysicalDisplay>> : std::bool_constant<true> {};
template <typename>
struct DisplayIdGetter;
template <typename PhysicalDisplay>
-struct DisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
- static std::optional<DisplayId> get() {
+struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static std::optional<PhysicalDisplayId> get() {
if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
- return getFallbackDisplayId(static_cast<bool>(PhysicalDisplay::PRIMARY)
- ? LEGACY_DISPLAY_TYPE_PRIMARY
- : LEGACY_DISPLAY_TYPE_EXTERNAL);
+ return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
+ ? LEGACY_DISPLAY_TYPE_PRIMARY
+ : LEGACY_DISPLAY_TYPE_EXTERNAL);
}
const auto info =
@@ -361,9 +363,10 @@
}
};
-template <DisplayId::Type displayId>
-struct DisplayIdGetter<VirtualDisplayId<displayId>> {
- static std::optional<DisplayId> get() { return DisplayId{displayId}; }
+template <uint64_t displayId>
+struct DisplayIdGetter<VirtualDisplayIdType<displayId>> {
+ // TODO(b/160679868) Use VirtualDisplayId
+ static std::optional<PhysicalDisplayId> get() { return PhysicalDisplayId{displayId}; }
};
template <>
@@ -377,7 +380,7 @@
};
template <typename PhysicalDisplay>
-struct DisplayConnectionTypeGetter<PhysicalDisplayId<PhysicalDisplay>> {
+struct DisplayConnectionTypeGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
};
@@ -388,19 +391,19 @@
constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
-template <DisplayId::Type displayId>
-struct HwcDisplayIdGetter<VirtualDisplayId<displayId>> {
+template <uint64_t displayId>
+struct HwcDisplayIdGetter<VirtualDisplayIdType<displayId>> {
static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
};
template <typename PhysicalDisplay>
-struct HwcDisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
+struct HwcDisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
};
// DisplayIdType can be:
-// 1) PhysicalDisplayId<...> for generated ID of physical display backed by HWC.
-// 2) VirtualDisplayId<...> for hard-coded ID of virtual display backed by HWC.
+// 1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
+// 2) VirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
// 3) NoDisplayId for virtual display without HWC backing.
template <typename DisplayIdType, int width, int height, Critical critical, Async async,
Secure secure, Primary primary, int grallocUsage>
@@ -628,10 +631,11 @@
template <typename PhysicalDisplay, int width, int height, Critical critical>
struct PhysicalDisplayVariant
- : DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height, critical, Async::FALSE,
- Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+ : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
+ Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+ GRALLOC_USAGE_PHYSICAL_DISPLAY>,
HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
- DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height,
+ DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
critical, Async::FALSE, Secure::TRUE,
PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
PhysicalDisplay> {};
@@ -717,14 +721,14 @@
template <int width, int height, Secure secure>
struct HwcVirtualDisplayVariant
- : DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, secure,
- Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
- HwcDisplayVariant<
- HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
- DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
- using Base = DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
+ : DisplayVariant<VirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
+ secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
+ HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
+ DisplayVariant<VirtualDisplayIdType<42>, width, height, Critical::FALSE,
+ Async::TRUE, secure, Primary::FALSE,
+ GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+ using Base = DisplayVariant<VirtualDisplayIdType<42>, width, height, Critical::FALSE,
+ Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
using Self = HwcVirtualDisplayVariant<width, height, secure>;
static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
@@ -1319,7 +1323,7 @@
// Call Expectations
// The call disable vsyncs
- EXPECT_CALL(*mEventControlThread, setVsyncEnabled(false)).Times(1);
+ EXPECT_CALL(mSchedulerCallback, setVsyncEnabled(false)).Times(1);
// The call ends any display resyncs
EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1);
@@ -1347,6 +1351,30 @@
}
/* ------------------------------------------------------------------------
+ * SurfaceFlinger::notifyPowerBoost
+ */
+
+TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+ mFlinger.scheduler()->replaceTouchTimer(100);
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
+ std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
+}
+
+/* ------------------------------------------------------------------------
* DisplayDevice::GetBestColorMode
*/
class GetBestColorModeTest : public DisplayTransactionTest {
@@ -1495,10 +1523,9 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -1508,11 +1535,12 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.sourceClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- // For 90, the frame and viewport have the hardware display size width and height swapped
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+ // size width and height swapped
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)),
+ compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -1522,10 +1550,8 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -1535,11 +1561,12 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.sourceClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- // For 270, the frame and viewport have the hardware display size width and height swapped
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+ // size width and height swapped
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)),
+ compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -1794,7 +1821,9 @@
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
ASSERT_TRUE(hwcDisplayId);
- state.physical = {*displayId, *connectionType, *hwcDisplayId};
+ state.physical = {.id = static_cast<PhysicalDisplayId>(*displayId),
+ .type = *connectionType,
+ .hwcDisplayId = *hwcDisplayId};
}
state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -1970,7 +1999,9 @@
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
ASSERT_TRUE(hwcDisplayId);
- expectedPhysical = {*displayId, *connectionType, *hwcDisplayId};
+ expectedPhysical = {.id = static_cast<PhysicalDisplayId>(*displayId),
+ .type = *connectionType,
+ .hwcDisplayId = *hwcDisplayId};
}
// The display should have been set up in the current display state
@@ -2457,11 +2488,11 @@
EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
}
-TEST_F(HandleTransactionLockedTest, processesDisplayViewportChanges) {
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
using Case = NonHwcVirtualDisplayCase;
- const Rect oldViewport(0, 0, 0, 0);
- const Rect newViewport(0, 0, 123, 456);
+ const Rect oldLayerStackRect(0, 0, 0, 0);
+ const Rect newLayerStackRect(0, 0, 123, 456);
// --------------------------------------------------------------------
// Preconditions
@@ -2470,9 +2501,9 @@
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.inject();
- // There is a change to the viewport state
- display.mutableDrawingDisplayState().viewport = oldViewport;
- display.mutableCurrentDisplayState().viewport = newViewport;
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect;
// --------------------------------------------------------------------
// Invocation
@@ -2482,7 +2513,7 @@
// --------------------------------------------------------------------
// Postconditions
- EXPECT_EQ(newViewport, display.mutableDisplayDevice()->getViewport());
+ EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
}
TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
@@ -2498,9 +2529,9 @@
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.inject();
- // There is a change to the viewport state
- display.mutableDrawingDisplayState().frame = oldFrame;
- display.mutableCurrentDisplayState().frame = newFrame;
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame;
// --------------------------------------------------------------------
// Invocation
@@ -2510,7 +2541,7 @@
// --------------------------------------------------------------------
// Postconditions
- EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getFrame());
+ EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
}
TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
@@ -2541,7 +2572,7 @@
EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
display.inject();
- // There is a change to the viewport state
+ // There is a change to the layerStackSpaceRect state
display.mutableDrawingDisplayState().width = oldWidth;
display.mutableDrawingDisplayState().height = oldHeight;
display.mutableCurrentDisplayState().width = newWidth;
@@ -2586,7 +2617,7 @@
EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
display.inject();
- // There is a change to the viewport state
+ // There is a change to the layerStackSpaceRect state
display.mutableDrawingDisplayState().width = oldWidth;
display.mutableDrawingDisplayState().height = oldHeight;
display.mutableCurrentDisplayState().width = oldWidth;
@@ -2807,8 +2838,8 @@
TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
using Case = SimplePrimaryDisplayCase;
constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
- const Rect initialFrame = {1, 2, 3, 4};
- const Rect initialViewport = {5, 6, 7, 8};
+ const Rect initialOrientedDisplayRect = {1, 2, 3, 4};
+ const Rect initialLayerStackRect = {5, 6, 7, 8};
// --------------------------------------------------------------------
// Preconditions
@@ -2819,16 +2850,16 @@
// The current display state projection state is all set
display.mutableCurrentDisplayState().orientation = initialOrientation;
- display.mutableCurrentDisplayState().frame = initialFrame;
- display.mutableCurrentDisplayState().viewport = initialViewport;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
// The incoming request sets the same projection state
DisplayState state;
state.what = DisplayState::eDisplayProjectionChanged;
state.token = display.token();
state.orientation = initialOrientation;
- state.frame = initialFrame;
- state.viewport = initialViewport;
+ state.orientedDisplaySpaceRect = initialOrientedDisplayRect;
+ state.layerStackSpaceRect = initialLayerStackRect;
// --------------------------------------------------------------------
// Invocation
@@ -2844,8 +2875,9 @@
// The current display state is unchanged
EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
- EXPECT_EQ(initialFrame, display.getCurrentDisplayState().frame);
- EXPECT_EQ(initialViewport, display.getCurrentDisplayState().viewport);
+ EXPECT_EQ(initialOrientedDisplayRect,
+ display.getCurrentDisplayState().orientedDisplaySpaceRect);
+ EXPECT_EQ(initialLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
}
TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
@@ -2886,8 +2918,8 @@
TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
using Case = SimplePrimaryDisplayCase;
- const Rect initialFrame = {0, 0, 0, 0};
- const Rect desiredFrame = {5, 6, 7, 8};
+ const Rect initialOrientedDisplayRect = {0, 0, 0, 0};
+ const Rect desiredOrientedDisplayRect = {5, 6, 7, 8};
// --------------------------------------------------------------------
// Preconditions
@@ -2896,14 +2928,14 @@
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.inject();
- // The current display state does not have a frame
- display.mutableCurrentDisplayState().frame = initialFrame;
+ // The current display state does not have a orientedDisplaySpaceRect
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
- // The incoming request sets a frame
+ // The incoming request sets a orientedDisplaySpaceRect
DisplayState state;
state.what = DisplayState::eDisplayProjectionChanged;
state.token = display.token();
- state.frame = desiredFrame;
+ state.orientedDisplaySpaceRect = desiredOrientedDisplayRect;
// --------------------------------------------------------------------
// Invocation
@@ -2917,13 +2949,14 @@
EXPECT_EQ(eDisplayTransactionNeeded, flags);
// The current display state has the new value.
- EXPECT_EQ(desiredFrame, display.getCurrentDisplayState().frame);
+ EXPECT_EQ(desiredOrientedDisplayRect,
+ display.getCurrentDisplayState().orientedDisplaySpaceRect);
}
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfViewportChanged) {
+TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackRectChanged) {
using Case = SimplePrimaryDisplayCase;
- const Rect initialViewport = {0, 0, 0, 0};
- const Rect desiredViewport = {5, 6, 7, 8};
+ const Rect initialLayerStackRect = {0, 0, 0, 0};
+ const Rect desiredLayerStackRect = {5, 6, 7, 8};
// --------------------------------------------------------------------
// Preconditions
@@ -2932,14 +2965,14 @@
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.inject();
- // The current display state does not have a viewport
- display.mutableCurrentDisplayState().viewport = initialViewport;
+ // The current display state does not have a layerStackSpaceRect
+ display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
- // The incoming request sets a viewport
+ // The incoming request sets a layerStackSpaceRect
DisplayState state;
state.what = DisplayState::eDisplayProjectionChanged;
state.token = display.token();
- state.viewport = desiredViewport;
+ state.layerStackSpaceRect = desiredLayerStackRect;
// --------------------------------------------------------------------
// Invocation
@@ -2953,7 +2986,7 @@
EXPECT_EQ(eDisplayTransactionNeeded, flags);
// The current display state has the new value.
- EXPECT_EQ(desiredViewport, display.getCurrentDisplayState().viewport);
+ EXPECT_EQ(desiredLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
}
TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
@@ -3115,11 +3148,11 @@
// The orientation state should be set to zero
EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
- // The frame state should be set to INVALID
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.frame);
+ // The orientedDisplaySpaceRect state should be set to INVALID
+ EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
- // The viewport state should be set to INVALID
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.viewport);
+ // The layerStackSpaceRect state should be set to INVALID
+ EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
// The width and height should both be zero
EXPECT_EQ(0u, primaryDisplayState.width);
@@ -3130,7 +3163,7 @@
auto displayDevice = primaryDisplay.mutableDisplayDevice();
EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
- // The display refresh period should be set in the frame tracker.
+ // The display refresh period should be set in the orientedDisplaySpaceRect tracker.
FrameStats stats;
mFlinger.getAnimFrameTracker().getStats(&stats);
EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano);
@@ -3183,9 +3216,9 @@
};
struct EventThreadBaseSupportedVariant {
- static void setupEventAndEventControlThreadNoCallExpectations(DisplayTransactionTest* test) {
- // The event control thread should not be notified.
- EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(_)).Times(0);
+ static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
+ // The callback should not be notified to toggle VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(_)).Times(0);
// The event thread should not be notified.
EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
@@ -3198,29 +3231,29 @@
// These calls are only expected for the primary display.
// Instead expect no calls.
- setupEventAndEventControlThreadNoCallExpectations(test);
+ setupVsyncAndEventThreadNoCallExpectations(test);
}
static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
// These calls are only expected for the primary display.
// Instead expect no calls.
- setupEventAndEventControlThreadNoCallExpectations(test);
+ setupVsyncAndEventThreadNoCallExpectations(test);
}
};
struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // The event control thread should be notified to enable vsyncs
- EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(true)).Times(1);
+ // The callback should be notified to enable VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(true)).Times(1);
// The event thread should be notified that the screen was acquired.
EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
}
static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // There should be a call to setVsyncEnabled(false)
- EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(false)).Times(1);
+ // The callback should be notified to disable VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(false)).Times(1);
// The event thread should not be notified that the screen was released.
EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
@@ -3279,7 +3312,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
- Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
Case::setupRepaintEverythingCallExpectations(test);
}
@@ -3306,7 +3339,7 @@
: public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
}
@@ -3318,7 +3351,7 @@
struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
}
};
@@ -3336,7 +3369,7 @@
struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
}
};
@@ -3365,7 +3398,7 @@
: public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
Case::setupNoComposerPowerModeCallExpectations(test);
}
};
diff --git a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
deleted file mode 100644
index 9dc4193..0000000
--- a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
+++ /dev/null
@@ -1,119 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <log/log.h>
-
-#include "AsyncCallRecorder.h"
-#include "Scheduler/EventControlThread.h"
-
-namespace android {
-namespace {
-
-using namespace std::chrono_literals;
-using testing::_;
-
-class EventControlThreadTest : public testing::Test {
-protected:
- EventControlThreadTest();
- ~EventControlThreadTest() override;
-
- void createThread();
-
- void expectVSyncEnableCallbackCalled(bool enable);
-
- AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
-
- std::unique_ptr<EventControlThread> mThread;
-};
-
-EventControlThreadTest::EventControlThreadTest() {
- 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());
-}
-
-EventControlThreadTest::~EventControlThreadTest() {
- 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 EventControlThreadTest::createThread() {
- mThread = std::make_unique<android::impl::EventControlThread>(
- mVSyncSetEnabledCallRecorder.getInvocable());
-}
-
-void EventControlThreadTest::expectVSyncEnableCallbackCalled(bool expectedEnabled) {
- auto args = mVSyncSetEnabledCallRecorder.waitForCall();
- ASSERT_TRUE(args.has_value());
- EXPECT_EQ(std::get<0>(args.value()), expectedEnabled);
-}
-
-/* ------------------------------------------------------------------------
- * Test cases
- */
-
-TEST_F(EventControlThreadTest, signalsVSyncDisabledOnStartup) {
- createThread();
-
- // On thread start, there should be an automatic explicit call to disable
- // vsyncs
- expectVSyncEnableCallbackCalled(false);
-}
-
-TEST_F(EventControlThreadTest, signalsVSyncDisabledOnce) {
- createThread();
- expectVSyncEnableCallbackCalled(false);
-
- mThread->setVsyncEnabled(false);
-
- EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
-}
-
-TEST_F(EventControlThreadTest, signalsVSyncEnabledThenDisabled) {
- createThread();
- expectVSyncEnableCallbackCalled(false);
-
- mThread->setVsyncEnabled(true);
-
- expectVSyncEnableCallbackCalled(true);
-
- mThread->setVsyncEnabled(false);
-
- expectVSyncEnableCallbackCalled(false);
-}
-
-TEST_F(EventControlThreadTest, signalsVSyncEnabledOnce) {
- createThread();
- expectVSyncEnableCallbackCalled(false);
-
- mThread->setVsyncEnabled(true);
-
- expectVSyncEnableCallbackCalled(true);
-
- mThread->setVsyncEnabled(true);
-
- EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index b90b566..aab6d01 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -36,9 +36,9 @@
namespace {
-constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
-constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = 222;
-constexpr PhysicalDisplayId DISPLAY_ID_64BIT = 0xabcd12349876fedcULL;
+constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID(111);
+constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID(222);
+constexpr PhysicalDisplayId DISPLAY_ID_64BIT(0xabcd12349876fedcULL);
class MockVSyncSource : public VSyncSource {
public:
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
deleted file mode 100644
index b50ddf5..0000000
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "Scheduler/PhaseOffsets.h"
-
-namespace android::scheduler {
-
-struct FakePhaseOffsets : PhaseConfiguration {
- static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
-
- Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
-
- Offsets getCurrentOffsets() const override {
- return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
- }
-
- void setRefreshRateFps(float) override {}
- void dump(std::string&) const override {}
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
new file mode 100644
index 0000000..4cd1e0a
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VsyncConfiguration.h"
+
+namespace android::scheduler {
+
+struct FakePhaseOffsets : VsyncConfiguration {
+ static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+ static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0);
+
+ VsyncConfigSet getConfigsForRefreshRate(float) const override { return getCurrentConfigs(); }
+
+ VsyncConfigSet getCurrentConfigs() const override {
+ return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS}};
+ }
+
+ void setRefreshRateFps(float) override {}
+ void dump(std::string&) const override {}
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 91b304c..fa12315 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -23,7 +23,13 @@
#include <vector>
+// StrictMock<T> derives from T and is not marked final, so the destructor of T is expected to be
+// virtual in case StrictMock<T> is used as a polymorphic base class. That is not the case here.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#include <gmock/gmock.h>
+#pragma clang diagnostic pop
+
#include <gui/LayerMetadata.h>
#include <log/log.h>
@@ -45,27 +51,20 @@
using ::testing::SetArgPointee;
using ::testing::StrictMock;
-struct MockHWC2ComposerCallback : public HWC2::ComposerCallback {
- ~MockHWC2ComposerCallback() = default;
-
- MOCK_METHOD3(onHotplugReceived,
- void(int32_t sequenceId, hal::HWDisplayId display, hal::Connection connection));
- MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hal::HWDisplayId display));
+struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
+ MOCK_METHOD3(onHotplugReceived, void(int32_t sequenceId, hal::HWDisplayId, hal::Connection));
+ MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hal::HWDisplayId));
MOCK_METHOD4(onVsyncReceived,
- void(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp,
- std::optional<hal::VsyncPeriodNanos> vsyncPeriod));
+ void(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp,
+ std::optional<hal::VsyncPeriodNanos>));
MOCK_METHOD3(onVsyncPeriodTimingChangedReceived,
- void(int32_t sequenceId, hal::HWDisplayId display,
- const hal::VsyncPeriodChangeTimeline& updatedTimeline));
- MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hal::HWDisplayId display));
+ void(int32_t sequenceId, hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&));
+ MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hal::HWDisplayId));
};
-struct HWComposerTest : public testing::Test {
+struct HWComposerSetConfigurationTest : testing::Test {
Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
-};
-
-struct HWComposerSetConfigurationTest : public HWComposerTest {
- StrictMock<MockHWC2ComposerCallback> mCallback;
+ MockHWC2ComposerCallback mCallback;
};
TEST_F(HWComposerSetConfigurationTest, loadsLayerMetadataSupport) {
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index cae317b..0fbe8ec 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -30,6 +30,7 @@
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/MockLayer.h"
+#include "mock/MockSchedulerCallback.h"
using testing::_;
using testing::Return;
@@ -49,6 +50,8 @@
LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
+ void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
@@ -73,7 +76,13 @@
.setConfigGroup(0)
.build()},
HwcConfigIndexType(0)};
- TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, false)};
+
+ mock::NoOpSchedulerCallback mSchedulerCallback;
+ static constexpr bool kUseContentDetectionV2 = false;
+
+ TestableScheduler* const mScheduler =
+ new TestableScheduler(mConfigs, mSchedulerCallback, kUseContentDetectionV2);
+
TestableSurfaceFlinger mFlinger;
const nsecs_t mTime = systemTime();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index afd2b71..cb376cd 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -27,6 +27,7 @@
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/MockLayer.h"
+#include "mock/MockSchedulerCallback.h"
using testing::_;
using testing::Return;
@@ -50,6 +51,8 @@
LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
+ void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
@@ -113,9 +116,14 @@
.setConfigGroup(0)
.build()},
HwcConfigIndexType(0)};
- TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, true)};
- TestableSurfaceFlinger mFlinger;
+ mock::NoOpSchedulerCallback mSchedulerCallback;
+ static constexpr bool kUseContentDetectionV2 = true;
+
+ TestableScheduler* const mScheduler =
+ new TestableScheduler(mConfigs, mSchedulerCallback, kUseContentDetectionV2);
+
+ TestableSurfaceFlinger mFlinger;
};
namespace {
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
deleted file mode 100644
index 0b74682..0000000
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// 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 "SchedulerUnittests"
-
-#include <gmock/gmock.h>
-#include <log/log.h>
-#include <thread>
-
-#include "Scheduler/PhaseOffsets.h"
-
-using namespace testing;
-
-namespace android {
-namespace scheduler {
-
-class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
-public:
- TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
- nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
- nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
- : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
- sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
- appEarlyGlDuration) {}
-};
-
-class PhaseDurationTest : public testing::Test {
-protected:
- PhaseDurationTest()
- : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
- 21'000'000) {}
-
- ~PhaseDurationTest() = default;
-
- TestablePhaseOffsetsAsDurations mPhaseDurations;
-};
-
-namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
- mPhaseDurations.setRefreshRateFps(60.0f);
- auto currentOffsets = mPhaseDurations.getCurrentOffsets();
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
-
- EXPECT_EQ(currentOffsets, offsets);
- EXPECT_EQ(offsets.late.sf, 6'166'667);
-
- EXPECT_EQ(offsets.late.app, 2'333'334);
-
- EXPECT_EQ(offsets.early.sf, 666'667);
-
- EXPECT_EQ(offsets.early.app, 833'334);
-
- EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
-
- EXPECT_EQ(offsets.earlyGl.app, 15'500'001);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
- mPhaseDurations.setRefreshRateFps(90.0f);
- auto currentOffsets = mPhaseDurations.getCurrentOffsets();
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
-
- EXPECT_EQ(currentOffsets, offsets);
- EXPECT_EQ(offsets.late.sf, 611'111);
-
- EXPECT_EQ(offsets.late.app, 2'333'333);
-
- EXPECT_EQ(offsets.early.sf, -4'888'889);
-
- EXPECT_EQ(offsets.early.app, 833'333);
-
- EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
-
- EXPECT_EQ(offsets.earlyGl.app, 9'944'444);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
- TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
-
- auto validateOffsets = [](auto& offsets) {
- EXPECT_EQ(offsets.late.sf, 1'000'000);
-
- EXPECT_EQ(offsets.late.app, 1'000'000);
-
- EXPECT_EQ(offsets.early.sf, 1'000'000);
-
- EXPECT_EQ(offsets.early.app, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
- };
-
- phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
- auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
- auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
- EXPECT_EQ(currentOffsets, offsets);
- validateOffsets(offsets);
-
- phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
- currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
- offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
- EXPECT_EQ(currentOffsets, offsets);
- validateOffsets(offsets);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
-
- EXPECT_EQ(offsets.late.sf, 57'527'208);
-
- EXPECT_EQ(offsets.late.app, 37'027'208);
-
- EXPECT_EQ(offsets.early.sf, 52'027'208);
-
- EXPECT_EQ(offsets.early.app, 35'527'208);
-
- EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
-
- EXPECT_EQ(offsets.earlyGl.app, 33'527'208);
-}
-
-} // namespace
-
-class TestablePhaseOffsets : public impl::PhaseOffsets {
-public:
- TestablePhaseOffsets()
- : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 1'000'000, 1'000'000, {}, {}, {}, {},
- 10'000'000) {}
-};
-
-class PhaseOffsetsTest : public testing::Test {
-protected:
- PhaseOffsetsTest() = default;
- ~PhaseOffsetsTest() = default;
-
- TestablePhaseOffsets mPhaseOffsets;
-};
-
-namespace {
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
-
- EXPECT_EQ(offsets.late.sf, 1'000'000);
-
- EXPECT_EQ(offsets.late.app, 1'000'000);
-
- EXPECT_EQ(offsets.early.sf, 1'000'000);
-
- EXPECT_EQ(offsets.early.app, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
-}
-
-} // namespace
-} // namespace scheduler
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 43b8e01..f6bf05a 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -32,7 +32,6 @@
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
namespace android {
@@ -146,8 +145,7 @@
EXPECT_CALL(*primaryDispSync, getPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(primaryDispSync),
- std::make_unique<mock::EventControlThread>(), std::move(eventThread),
+ mFlinger.setupScheduler(std::move(primaryDispSync), std::move(eventThread),
std::move(sfEventThread));
}
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1aa7320..39e793a 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -14,32 +14,28 @@
* 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 "SchedulerUnittests"
-
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
#include <mutex>
-#include "Scheduler/EventControlThread.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockEventThread.h"
+#include "mock/MockLayer.h"
+#include "mock/MockSchedulerCallback.h"
using testing::_;
using testing::Return;
namespace android {
+namespace {
-constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = 999;
+constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID(999);
class SchedulerTest : public testing::Test {
protected:
@@ -56,32 +52,32 @@
};
SchedulerTest();
- ~SchedulerTest() override;
- std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
- std::unique_ptr<TestableScheduler> mScheduler;
+ Hwc2::mock::Display mDisplay;
+ const scheduler::RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
+ .setVsyncPeriod(16'666'667)
+ .setConfigGroup(0)
+ .build()},
+ HwcConfigIndexType(0)};
+
+ mock::SchedulerCallback mSchedulerCallback;
+
+ // The scheduler should initially disable VSYNC.
+ struct ExpectDisableVsync {
+ ExpectDisableVsync(mock::SchedulerCallback& callback) {
+ EXPECT_CALL(callback, setVsyncEnabled(false)).Times(1);
+ }
+ } mExpectDisableVsync{mSchedulerCallback};
+
+ static constexpr bool kUseContentDetectionV2 = false;
+ TestableScheduler mScheduler{mConfigs, mSchedulerCallback, kUseContentDetectionV2};
Scheduler::ConnectionHandle mConnectionHandle;
mock::EventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
- Hwc2::mock::Display mDisplay;
};
SchedulerTest::SchedulerTest() {
- 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());
-
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
- HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(16666667))
- .setConfigGroup(0)
- .build()};
- mRefreshRateConfigs = std::make_unique<
- scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
-
- mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
-
auto eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
@@ -93,86 +89,90 @@
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillRepeatedly(Return(mEventThreadConnection));
- mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
+ mConnectionHandle = mScheduler.createConnection(std::move(eventThread));
EXPECT_TRUE(mConnectionHandle);
}
-SchedulerTest::~SchedulerTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+} // namespace
TEST_F(SchedulerTest, invalidConnectionHandle) {
Scheduler::ConnectionHandle handle;
- sp<IDisplayEventConnection> connection;
- ASSERT_NO_FATAL_FAILURE(
- connection = mScheduler->createDisplayEventConnection(handle,
- ISurfaceComposer::
- eConfigChangedSuppress));
+ const sp<IDisplayEventConnection> connection =
+ mScheduler.createDisplayEventConnection(handle,
+ ISurfaceComposer::eConfigChangedSuppress);
+
EXPECT_FALSE(connection);
- EXPECT_FALSE(mScheduler->getEventConnection(handle));
+ EXPECT_FALSE(mScheduler.getEventConnection(handle));
// The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
+ mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle));
+ mScheduler.onScreenAcquired(handle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle));
+ mScheduler.onScreenReleased(handle);
std::string output;
EXPECT_CALL(*mEventThread, dump(_)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output));
+ mScheduler.dump(handle, output);
EXPECT_TRUE(output.empty());
EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10));
+ mScheduler.setPhaseOffset(handle, 10);
}
TEST_F(SchedulerTest, validConnectionHandle) {
- sp<IDisplayEventConnection> connection;
- ASSERT_NO_FATAL_FAILURE(
- connection = mScheduler->createDisplayEventConnection(mConnectionHandle,
- ISurfaceComposer::
- eConfigChangedSuppress));
+ const sp<IDisplayEventConnection> connection =
+ mScheduler.createDisplayEventConnection(mConnectionHandle,
+ ISurfaceComposer::eConfigChangedSuppress);
+
ASSERT_EQ(mEventThreadConnection, connection);
- EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
+ EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
- ASSERT_NO_FATAL_FAILURE(
- mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+ mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
+ mScheduler.onScreenAcquired(mConnectionHandle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
+ mScheduler.onScreenReleased(mConnectionHandle);
std::string output("dump");
EXPECT_CALL(*mEventThread, dump(output)).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output));
+ mScheduler.dump(mConnectionHandle, output);
EXPECT_FALSE(output.empty());
EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+ mScheduler.setPhaseOffset(mConnectionHandle, 10);
static constexpr size_t kEventConnections = 5;
- ON_CALL(*mEventThread, getEventThreadConnectionCount())
- .WillByDefault(Return(kEventConnections));
- EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
+ EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections));
+ EXPECT_EQ(kEventConnections, mScheduler.getEventThreadConnectionCount(mConnectionHandle));
}
-} // namespace
-} // namespace android
+TEST_F(SchedulerTest, noLayerHistory) {
+ // Layer history should not be created if there is a single config.
+ ASSERT_FALSE(mScheduler.hasLayerHistory());
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+ TestableSurfaceFlinger flinger;
+ mock::MockLayer layer(flinger.flinger());
+
+ // Content detection should be no-op.
+ mScheduler.registerLayer(&layer);
+ mScheduler.recordLayerHistory(&layer, 0, LayerHistory::LayerUpdateType::Buffer);
+
+ constexpr bool kPowerStateNormal = true;
+ mScheduler.setDisplayPowerState(kPowerStateNormal);
+
+ constexpr uint32_t kDisplayArea = 999'999;
+ mScheduler.onPrimaryDisplayAreaChanged(kDisplayArea);
+
+ EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0);
+ mScheduler.chooseRefreshRateForContent();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 0d6c799..6c01f85 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -33,7 +33,6 @@
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
@@ -182,8 +181,7 @@
EXPECT_CALL(*primaryDispSync, getPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(primaryDispSync),
- std::make_unique<mock::EventControlThread>(), std::move(eventThread),
+ mFlinger.setupScheduler(std::move(primaryDispSync), std::move(eventThread),
std::move(sfEventThread));
}
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index d5ecae8..d78546d 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -23,76 +23,82 @@
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
+#include "Scheduler/VSyncTracker.h"
+#include "mock/MockDispSync.h"
namespace android {
-class TestableScheduler : public Scheduler, private ISchedulerCallback {
+class TestableScheduler : public Scheduler {
public:
- TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
- : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
- }
+ TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
+ bool useContentDetectionV2)
+ : TestableScheduler(std::make_unique<mock::DispSync>(), configs, callback,
+ useContentDetectionV2) {}
TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
- std::unique_ptr<EventControlThread> eventControlThread,
- const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
- : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
- useContentDetectionV2, true) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
- }
+ const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
+ bool useContentDetectionV2)
+ : Scheduler({std::move(primaryDispSync), nullptr, nullptr}, configs, callback,
+ createLayerHistory(configs, useContentDetectionV2),
+ {.supportKernelTimer = false,
+ .useContentDetection = true,
+ .useContentDetectionV2 = useContentDetectionV2,
+ .useVsyncPredictor = false}) {}
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
return Scheduler::createConnection(std::move(eventThread));
}
- size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
- if (mUseContentDetectionV2) {
- return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get())
- ->mLayerInfos.size();
- } else {
- return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())
- ->mLayerInfos.size();
- }
- }
-
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
*/
auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
- auto& mutableEventControlThread() { return mEventControlThread; }
- auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
- auto mutableLayerHistory() {
+
+ bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
+
+ auto* mutableLayerHistory() {
+ LOG_ALWAYS_FATAL_IF(mOptions.useContentDetectionV2);
return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
}
- auto mutableLayerHistoryV2() {
+
+ auto* mutableLayerHistoryV2() {
+ LOG_ALWAYS_FATAL_IF(!mOptions.useContentDetectionV2);
return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
}
+ size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
+ if (!mLayerHistory) return 0;
+ return mOptions.useContentDetectionV2 ? mutableLayerHistoryV2()->mLayerInfos.size()
+ : mutableLayerHistory()->mLayerInfos.size();
+ }
+
+ void replaceTouchTimer(int64_t millis) {
+ if (mTouchTimer) {
+ mTouchTimer.reset();
+ }
+ mTouchTimer.emplace(
+ std::chrono::milliseconds(millis),
+ [this] { touchTimerCallback(TimerState::Reset); },
+ [this] { touchTimerCallback(TimerState::Expired); });
+ mTouchTimer->start();
+ }
+
+ bool isTouchActive() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ return mFeatures.touch == Scheduler::TouchState::Active;
+ }
+
~TestableScheduler() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the Scheduler instance may
// still be referenced by something despite our best efforts to destroy
// it after each test is done.
- mutableEventControlThread().reset();
- mutablePrimaryDispSync().reset();
+ mVsyncSchedule.sync.reset();
mConnections.clear();
}
-
-private:
- void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
- void repaintEverythingForHWC() override {}
- void kernelTimerChanged(bool /*expired*/) override {}
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 38bc8a1..a1049d8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -29,7 +29,7 @@
#include "ContainerLayer.h"
#include "DisplayDevice.h"
#include "EffectLayer.h"
-#include "FakePhaseOffsets.h"
+#include "FakeVsyncConfiguration.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "Scheduler/MessageQueue.h"
@@ -69,11 +69,6 @@
return nullptr;
}
- std::unique_ptr<EventControlThread> createEventControlThread(
- std::function<void(bool)>) override {
- return nullptr;
- }
-
std::unique_ptr<HWComposer> createHWComposer(const std::string&) override {
return nullptr;
}
@@ -82,13 +77,12 @@
return std::make_unique<android::impl::MessageQueue>();
}
- std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
- std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>,
- const scheduler::RefreshRateConfigs&,
+ std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) override {
return nullptr;
}
@@ -175,7 +169,7 @@
} // namespace surfaceflinger::test
-class TestableSurfaceFlinger {
+class TestableSurfaceFlinger final : private ISchedulerCallback {
public:
using HotplugEvent = SurfaceFlinger::HotplugEvent;
@@ -198,37 +192,42 @@
mFlinger->mCompositionEngine->setTimeStats(timeStats);
}
+ // The ISchedulerCallback argument can be nullptr for a no-op implementation.
void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
- std::unique_ptr<EventControlThread> eventControlThread,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
- bool useContentDetectionV2 = false) {
+ ISchedulerCallback* callback = nullptr, bool hasMultipleConfigs = false) {
std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(16666667))
+ .setVsyncPeriod(16'666'667)
.setConfigGroup(0)
.build()};
+ if (hasMultipleConfigs) {
+ configs.emplace_back(HWC2::Display::Config::Builder(mDisplay, 1)
+ .setVsyncPeriod(11'111'111)
+ .setConfigGroup(0)
+ .build());
+ }
+
mFlinger->mRefreshRateConfigs = std::make_unique<
scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
mFlinger->mRefreshRateStats = std::make_unique<
scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
/*currentConfig=*/HwcConfigIndexType(0),
/*powerMode=*/hal::PowerMode::OFF);
- mFlinger->mPhaseConfiguration =
- mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
+ mFlinger->mVsyncConfiguration =
+ mFactory.createVsyncConfiguration(*mFlinger->mRefreshRateConfigs);
+ mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
+ constexpr bool kUseContentDetectionV2 = false;
mScheduler =
- new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
- *mFlinger->mRefreshRateConfigs, useContentDetectionV2);
+ new TestableScheduler(std::move(primaryDispSync), *mFlinger->mRefreshRateConfigs,
+ *(callback ?: this), kUseContentDetectionV2);
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
resetScheduler(mScheduler);
-
- mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
- mFlinger->mSfConnectionHandle,
- mFlinger->mPhaseConfiguration->getCurrentOffsets());
}
void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
@@ -321,6 +320,8 @@
return mFlinger->onInitializeDisplays();
}
+ auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
+
// Allow reading display state without locking, as if called on the SF main thread.
auto setPowerModeInternal(const sp<DisplayDevice>& display,
hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
@@ -329,19 +330,18 @@
auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what, systemTime()); }
- auto captureScreenImplLocked(const RenderArea& renderArea,
- SurfaceFlinger::TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
- bool forSystem, int* outSyncFd, bool regionSampling) {
- bool ignored;
- return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
- useIdentityTransform, forSystem, outSyncFd,
- regionSampling, ignored);
+ auto renderScreenImplLocked(const RenderArea& renderArea,
+ SurfaceFlinger::TraverseLayersFunction traverseLayers,
+ const sp<GraphicBuffer>& buffer, bool forSystem, int* outSyncFd,
+ bool regionSampling) {
+ ScreenCaptureResults captureResults;
+ return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer, forSystem,
+ outSyncFd, regionSampling, captureResults);
}
- auto traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor) {
- return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor);
+ auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
+ const LayerVector::Visitor& visitor) {
+ return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, uid, visitor);
}
auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
@@ -633,7 +633,9 @@
if (const auto type = mCreationArgs.connectionType) {
LOG_ALWAYS_FATAL_IF(!displayId);
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
- state.physical = {*displayId, *type, *mHwcDisplayId};
+ state.physical = {.id = static_cast<PhysicalDisplayId>(*displayId),
+ .type = *type,
+ .hwcDisplayId = *mHwcDisplayId};
}
state.isSecure = mCreationArgs.isSecure;
@@ -657,6 +659,12 @@
const std::optional<hal::HWDisplayId> mHwcDisplayId;
};
+private:
+ void setVsyncEnabled(bool) override {}
+ void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override {}
+ void repaintEverythingForHWC() override {}
+ void kernelTimerChanged(bool) override {}
+
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
TestableScheduler* mScheduler = nullptr;
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 2a48a22..44b3dc0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -32,7 +32,6 @@
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/MockDispSync.h"
-#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
@@ -81,7 +80,6 @@
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
mFlinger.setupScheduler(std::unique_ptr<mock::DispSync>(mPrimaryDispSync),
- std::make_unique<mock::EventControlThread>(),
std::move(eventThread), std::move(sfEventThread));
}
@@ -89,7 +87,6 @@
TestableSurfaceFlinger mFlinger;
std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
- mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
mock::DispSync* mPrimaryDispSync = new mock::DispSync();
diff --git a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
deleted file mode 100644
index 9c1ec07..0000000
--- a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-#define LOG_NDEBUG 0
-
-#include "Scheduler/VSyncModulator.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-using namespace testing;
-
-namespace android::scheduler {
-
-class MockScheduler : public IPhaseOffsetControl {
-public:
- void setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
- mPhaseOffset[handle] = phaseOffset;
- }
-
- nsecs_t getOffset(ConnectionHandle handle) { return mPhaseOffset[handle]; }
-
-private:
- std::unordered_map<ConnectionHandle, nsecs_t> mPhaseOffset;
-};
-
-class VSyncModulatorTest : public testing::Test {
-protected:
- static constexpr auto MIN_EARLY_FRAME_COUNT_TRANSACTION =
- VSyncModulator::MIN_EARLY_FRAME_COUNT_TRANSACTION;
- // Add a 1ms slack to avoid strange timer race conditions.
- static constexpr auto MARGIN_FOR_TX_APPLY = VSyncModulator::MARGIN_FOR_TX_APPLY + 1ms;
-
- // Used to enumerate the different offsets we have
- enum {
- SF_LATE,
- APP_LATE,
- SF_EARLY,
- APP_EARLY,
- SF_EARLY_GL,
- APP_EARLY_GL,
- };
-
- std::unique_ptr<VSyncModulator> mVSyncModulator;
- MockScheduler mMockScheduler;
- ConnectionHandle mAppConnection{1};
- ConnectionHandle mSfConnection{2};
- VSyncModulator::OffsetsConfig mOffsets = {{SF_EARLY, APP_EARLY},
- {SF_EARLY_GL, APP_EARLY_GL},
- {SF_LATE, APP_LATE}};
-
- void SetUp() override {
- mVSyncModulator = std::make_unique<VSyncModulator>(mMockScheduler, mAppConnection,
- mSfConnection, mOffsets);
- mVSyncModulator->setPhaseOffsets(mOffsets);
-
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
- };
-
- void TearDown() override { mVSyncModulator.reset(); }
-};
-
-TEST_F(VSyncModulatorTest, Normal) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
- }
-}
-
-TEST_F(VSyncModulatorTest, EarlyEnd) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStart) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartWithEarly) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Early);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartWithMoreTransactions) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEnd) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 6856612..c5cddf3 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -45,26 +45,6 @@
MOCK_CONST_METHOD1(dump, void(std::string&));
};
-class VSyncTrackerWrapper : public VSyncTracker {
-public:
- VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
-
- bool addVsyncTimestamp(nsecs_t timestamp) final {
- return mTracker->addVsyncTimestamp(timestamp);
- }
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
- return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
- }
- nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
- void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
- void resetModel() final { mTracker->resetModel(); }
- bool needsMoreSamples() const final { return mTracker->needsMoreSamples(); }
- void dump(std::string& result) const final { mTracker->dump(result); }
-
-private:
- std::shared_ptr<VSyncTracker> const mTracker;
-};
-
class MockClock : public Clock {
public:
MOCK_CONST_METHOD0(now, nsecs_t());
@@ -90,29 +70,6 @@
MOCK_CONST_METHOD1(dump, void(std::string&));
};
-class VSyncDispatchWrapper : public VSyncDispatch {
-public:
- VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
- CallbackToken registerCallback(std::function<void(nsecs_t, nsecs_t)> const& callbackFn,
- std::string callbackName) final {
- return mDispatch->registerCallback(callbackFn, callbackName);
- }
-
- void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
-
- ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
- nsecs_t earliestVsync) final {
- return mDispatch->schedule(token, workDuration, earliestVsync);
- }
-
- CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
-
- void dump(std::string& result) const final { return mDispatch->dump(result); }
-
-private:
- std::shared_ptr<VSyncDispatch> const mDispatch;
-};
-
std::shared_ptr<FenceTime> generateInvalidFence() {
sp<Fence> fence = new Fence();
return std::make_shared<FenceTime>(fence);
@@ -157,10 +114,8 @@
: mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
mMockClock(std::make_shared<NiceMock<MockClock>>()),
- mReactor(std::make_unique<ClockWrapper>(mMockClock),
- std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
- std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit,
- false /* supportKernelIdleTimer */) {
+ mReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockDispatch, *mMockTracker,
+ kPendingLimit, false /* supportKernelIdleTimer */) {
ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
}
@@ -745,10 +700,9 @@
TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
// Create a reactor which supports the kernel idle timer
- auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock),
- std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
- std::make_unique<VSyncTrackerWrapper>(mMockTracker),
- kPendingLimit, true /* supportKernelIdleTimer */);
+ auto idleReactor =
+ VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockDispatch, *mMockTracker,
+ kPendingLimit, true /* supportKernelIdleTimer */);
bool periodFlushed = true;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4);
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
new file mode 100644
index 0000000..72ee6db
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/VsyncConfiguration.h"
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class TestableWorkDuration : public impl::WorkDuration {
+public:
+ TestableWorkDuration(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+ nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+ : impl::WorkDuration({60.0f, 90.0f}, currentFps, sfDuration, appDuration, sfEarlyDuration,
+ appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration) {}
+};
+
+class WorkDurationTest : public testing::Test {
+protected:
+ WorkDurationTest()
+ : mWorkDuration(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
+ 21'000'000) {}
+
+ ~WorkDurationTest() = default;
+
+ TestableWorkDuration mWorkDuration;
+};
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_60Hz) {
+ mWorkDuration.setRefreshRateFps(60.0f);
+ auto currentOffsets = mWorkDuration.getCurrentConfigs();
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(60.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sfOffset, 6'166'667);
+ EXPECT_EQ(offsets.late.appOffset, 2'333'334);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 666'667);
+ EXPECT_EQ(offsets.early.appOffset, 833'334);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'166'667);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 15'500'001);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_90Hz) {
+ mWorkDuration.setRefreshRateFps(90.0f);
+ auto currentOffsets = mWorkDuration.getCurrentConfigs();
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(90.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sfOffset, 611'111);
+ EXPECT_EQ(offsets.late.appOffset, 2'333'333);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, -4'888'889);
+ EXPECT_EQ(offsets.early.appOffset, 833'333);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, -2'388'889);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 9'944'444);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_DefaultOffsets) {
+ TestableWorkDuration phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
+
+ auto validateOffsets = [](const auto& offsets, std::chrono::nanoseconds vsyncPeriod) {
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, vsyncPeriod);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, vsyncPeriod);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, vsyncPeriod);
+ };
+
+ const auto testForRefreshRate = [&](float refreshRate) {
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(refreshRate);
+ auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentConfigs();
+ auto offsets = phaseOffsetsWithDefaultValues.getConfigsForRefreshRate(refreshRate);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets,
+ std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / refreshRate)));
+ };
+
+ testForRefreshRate(90.0f);
+ testForRefreshRate(60.0f);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 57'527'208);
+ EXPECT_EQ(offsets.late.appOffset, 37'027'208);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 52'027'208);
+ EXPECT_EQ(offsets.early.appOffset, 35'527'208);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 54'527'208);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 33'527'208);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+ TestablePhaseOffsets(nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs,
+ std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs,
+ nsecs_t highFpsVsyncPhaseOffsetNs, nsecs_t highFpsSfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
+ nsecs_t thresholdForNextVsync)
+ : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs,
+ earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs,
+ earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
+ highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs,
+ highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs,
+ highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest() = default;
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsets mPhaseOffsets{2'000'000, 6'000'000, 7'000'000, 8'000'000, 3'000'000,
+ 4'000'000, 2'000'000, 1'000'000, 2'000'000, 3'000'000,
+ 3'000'000, 4'000'000, 10'000'000};
+};
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 62'027'208ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 72'027'208ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 7'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 61'027'208ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 72'027'208ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 60'027'208ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 72'027'208ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_60Hz) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(60.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'666'667ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'666'667ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 7'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 9'666'667ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 20'666'667ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'666'667ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 20'666'667ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_90Hz) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(90.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 2'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 9'111'111ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'111'111ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_60Hz) {
+ TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000,
+ 1'000'000, {}, {}, {}, {}, 10'000'000};
+ auto offsets = phaseOffsets.getConfigsForRefreshRate(60.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 16'666'667ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'666'667ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 16'666'667ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_90Hz) {
+ TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000,
+ 1'000'000, {}, {}, {}, {}, 10'000'000};
+ auto offsets = phaseOffsets.getConfigsForRefreshRate(90.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
new file mode 100644
index 0000000..106da81
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Scheduler/VsyncModulator.h"
+
+namespace android::scheduler {
+
+class VsyncModulatorTest : public testing::Test {
+ enum {
+ SF_OFFSET_LATE,
+ APP_OFFSET_LATE,
+ SF_DURATION_LATE,
+ APP_DURATION_LATE,
+ SF_OFFSET_EARLY,
+ APP_OFFSET_EARLY,
+ SF_DURATION_EARLY,
+ APP_DURATION_EARLY,
+ SF_OFFSET_EARLY_GPU,
+ APP_OFFSET_EARLY_GPU,
+ SF_DURATION_EARLY_GPU,
+ APP_DURATION_EARLY_GPU,
+ };
+
+ static VsyncModulator::TimePoint Now() {
+ static VsyncModulator::TimePoint now;
+ return now += VsyncModulator::MIN_EARLY_TRANSACTION_TIME;
+ }
+
+protected:
+ static constexpr auto MIN_EARLY_TRANSACTION_FRAMES =
+ VsyncModulator::MIN_EARLY_TRANSACTION_FRAMES;
+
+ using Schedule = scheduler::TransactionSchedule;
+ using nanos = std::chrono::nanoseconds;
+ const VsyncModulator::VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
+ nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
+ const VsyncModulator::VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
+ nanos(SF_DURATION_EARLY),
+ nanos(APP_DURATION_EARLY)};
+ const VsyncModulator::VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE,
+ nanos(SF_DURATION_EARLY_GPU),
+ nanos(APP_DURATION_EARLY_GPU)};
+
+ const VsyncModulator::VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate};
+ VsyncModulator mVsyncModulator{mOffsets, Now};
+
+ void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator.setVsyncConfigSet(mOffsets)); }
+};
+
+#define CHECK_COMMIT(result, configs) \
+ EXPECT_EQ(result, mVsyncModulator.onTransactionCommit()); \
+ EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig());
+
+#define CHECK_REFRESH(N, result, configs) \
+ for (int i = 0; i < N; i++) { \
+ EXPECT_EQ(result, mVsyncModulator.onDisplayRefresh(false)); \
+ EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig()); \
+ }
+
+TEST_F(VsyncModulatorTest, Late) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+
+ CHECK_COMMIT(std::nullopt, kLate);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyEnd) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStart) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartWithEarly) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::Early));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartWithMoreTransactions) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+
+ for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+ CHECK_REFRESH(1, std::nullopt, kEarly);
+ }
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEnd) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(1, kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(1, kEarly, kEarly);
+
+ for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+ CHECK_REFRESH(1, std::nullopt, kEarly);
+ }
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
deleted file mode 100644
index 6ef352a..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
+++ /dev/null
@@ -1,35 +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/EventControlThread.h"
-
-namespace android {
-namespace mock {
-
-class EventControlThread : public android::EventControlThread {
-public:
- EventControlThread();
- ~EventControlThread() override;
-
- MOCK_METHOD1(setVsyncEnabled, void(bool));
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
new file mode 100644
index 0000000..72bc89c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -0,0 +1,42 @@
+/*
+ * 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/Scheduler.h"
+
+namespace android::mock {
+
+struct SchedulerCallback final : ISchedulerCallback {
+ MOCK_METHOD1(setVsyncEnabled, void(bool));
+ MOCK_METHOD2(changeRefreshRate,
+ void(const scheduler::RefreshRateConfigs::RefreshRate&,
+ scheduler::RefreshRateConfigEvent));
+ MOCK_METHOD0(repaintEverythingForHWC, void());
+ MOCK_METHOD1(kernelTimerChanged, void(bool));
+};
+
+struct NoOpSchedulerCallback final : ISchedulerCallback {
+ void setVsyncEnabled(bool) override {}
+ void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
+ scheduler::RefreshRateConfigEvent) override {}
+ void repaintEverythingForHWC() override {}
+ void kernelTimerChanged(bool) override {}
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
index 5beee1c..ccb7bb9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
@@ -33,10 +33,10 @@
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
MOCK_METHOD0(disable, void());
MOCK_METHOD0(isEnabled, bool());
- MOCK_METHOD4(saveTransaction,
+ MOCK_METHOD6(saveTransaction,
void(const Vector<ComposerState>&,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&,
- const Vector<DisplayState>&, uint32_t));
+ const Vector<DisplayState>&, uint32_t, int, int));
MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 5480b00..081d18b 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -31,47 +31,30 @@
captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
}
- static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
+ static void captureDisplay(std::unique_ptr<ScreenCapture>* sc,
+ const DisplayCaptureArgs& captureArgs) {
const auto sf = ComposerService::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, sf->captureDisplay(captureArgs, captureResults));
+ *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
}
- static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
+ static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
+ DisplayCaptureArgs args;
+ args.displayToken = displayToken;
+ captureDisplay(sc, args);
+ }
+
+ static void captureLayers(std::unique_ptr<ScreenCapture>* sc,
+ const LayerCaptureArgs& captureArgs) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
SurfaceComposerClient::Transaction().apply(true);
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
- }
-
- static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
- }
-
- static void captureChildLayersExcluding(
- std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- SurfaceComposerClient::Transaction().apply(true);
-
- sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR,
- sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB,
- ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers,
- 1.0f, true));
- *sc = std::make_unique<ScreenCapture>(outBuffer);
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(captureArgs, captureResults));
+ *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
}
void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
new file mode 100644
index 0000000..c45a1a1
--- /dev/null
+++ b/services/vibratorservice/Android.bp
@@ -0,0 +1,54 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libvibratorservice",
+
+ srcs: [
+ "VibratorCallbackScheduler.cpp",
+ "VibratorHalController.cpp",
+ "VibratorHalWrapper.cpp",
+ ],
+
+ aidl: {
+ local_include_dirs: ["include"],
+ include_dirs: [
+ "hardware/interfaces/vibrator/aidl/android/hardware/vibrator",
+ ],
+ export_aidl_headers: true
+ },
+
+ shared_libs: [
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "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",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ local_include_dirs: ["include"],
+
+ export_include_dirs: ["include"],
+}
diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING
new file mode 100644
index 0000000..b033adb
--- /dev/null
+++ b/services/vibratorservice/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libvibratorservice_test"
+ }
+ ]
+}
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
new file mode 100644
index 0000000..3f8cd67
--- /dev/null
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <chrono>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+bool DelayedCallback::isExpired() const {
+ return mExpiration <= std::chrono::steady_clock::now();
+}
+
+DelayedCallback::Timestamp DelayedCallback::getExpiration() const {
+ return mExpiration;
+}
+
+void DelayedCallback::run() const {
+ mCallback();
+}
+
+bool DelayedCallback::operator<(const DelayedCallback& other) const {
+ return mExpiration < other.mExpiration;
+}
+
+bool DelayedCallback::operator>(const DelayedCallback& other) const {
+ return mExpiration > other.mExpiration;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+CallbackScheduler::~CallbackScheduler() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFinished = true;
+ }
+ mCondition.notify_all();
+ if (mCallbackThread && mCallbackThread->joinable()) {
+ mCallbackThread->join();
+ }
+}
+
+void CallbackScheduler::schedule(std::function<void()> callback, std::chrono::milliseconds delay) {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mCallbackThread == nullptr) {
+ mCallbackThread = std::make_unique<std::thread>(&CallbackScheduler::loop, this);
+ }
+ mQueue.emplace(DelayedCallback(callback, delay));
+ }
+ mCondition.notify_all();
+}
+
+void CallbackScheduler::loop() {
+ while (true) {
+ std::lock_guard<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();
+ mQueue.pop();
+ }
+ if (mQueue.empty()) {
+ // Wait until a new callback is scheduled.
+ mCondition.wait(mMutex);
+ } else {
+ // Wait until next callback expires, or a new one is scheduled.
+ mCondition.wait_until(mMutex, mQueue.top().getExpiration());
+ }
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
new file mode 100644
index 0000000..46175ad
--- /dev/null
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -0,0 +1,230 @@
+/*
+ * 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 "VibratorHalController"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+#include <hardware/vibrator.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+static constexpr int MAX_RETRIES = 1;
+
+std::shared_ptr<HalWrapper> HalConnector::connect(std::shared_ptr<CallbackScheduler> scheduler) {
+ static bool gHalExists = true;
+ if (!gHalExists) {
+ // We already tried to connect to all of the vibrator HAL versions and none was available.
+ return nullptr;
+ }
+
+ sp<Aidl::IVibrator> aidlHal = waitForVintfService<Aidl::IVibrator>();
+ if (aidlHal) {
+ ALOGV("Successfully connected to Vibrator HAL AIDL service.");
+ return std::make_shared<AidlHalWrapper>(std::move(scheduler), aidlHal);
+ }
+
+ sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService();
+ if (halV1_0 == nullptr) {
+ ALOGV("Vibrator HAL service not available.");
+ gHalExists = false;
+ return nullptr;
+ }
+
+ sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0);
+ if (halV1_3) {
+ ALOGV("Successfully connected to Vibrator HAL v1.3 service.");
+ return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3);
+ }
+ sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0);
+ if (halV1_2) {
+ ALOGV("Successfully connected to Vibrator HAL v1.2 service.");
+ return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2);
+ }
+ sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0);
+ if (halV1_1) {
+ ALOGV("Successfully connected to Vibrator HAL v1.1 service.");
+ return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1);
+ }
+ ALOGV("Successfully connected to Vibrator HAL v1.0 service.");
+ return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) {
+ if (result.isFailed()) {
+ ALOGE("%s failed: %s", functionName, result.errorMessage());
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ mConnectedHal->tryReconnect();
+ }
+ return result;
+}
+
+template <typename T>
+HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* functionName) {
+ std::shared_ptr<HalWrapper> hal = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ // Init was never called, so connect to HAL for the first time during this call.
+ mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+
+ if (mConnectedHal == nullptr) {
+ ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
+ return HalResult<T>::unsupported();
+ }
+ }
+ hal = mConnectedHal;
+ }
+
+ HalResult<T> ret = processHalResult(halFn(hal), functionName);
+ for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) {
+ ret = processHalResult(halFn(hal), functionName);
+ }
+
+ return ret;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void HalController::init() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+ }
+}
+
+HalResult<void> HalController::ping() {
+ hal_fn<void> pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
+ return apply(pingFn, "ping");
+}
+
+void HalController::tryReconnect() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+ } else {
+ mConnectedHal->tryReconnect();
+ }
+}
+
+HalResult<void> HalController::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ hal_fn<void> onFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->on(timeout, completionCallback);
+ };
+ return apply(onFn, "on");
+}
+
+HalResult<void> HalController::off() {
+ hal_fn<void> offFn = [](std::shared_ptr<HalWrapper> hal) { return hal->off(); };
+ return apply(offFn, "off");
+}
+
+HalResult<void> HalController::setAmplitude(int32_t amplitude) {
+ hal_fn<void> setAmplitudeFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->setAmplitude(amplitude);
+ };
+ return apply(setAmplitudeFn, "setAmplitude");
+}
+
+HalResult<void> HalController::setExternalControl(bool enabled) {
+ hal_fn<void> setExternalControlFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->setExternalControl(enabled);
+ };
+ return apply(setExternalControlFn, "setExternalControl");
+}
+
+HalResult<void> HalController::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
+ hal_fn<void> alwaysOnEnableFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->alwaysOnEnable(id, effect, strength);
+ };
+ return apply(alwaysOnEnableFn, "alwaysOnEnable");
+}
+
+HalResult<void> HalController::alwaysOnDisable(int32_t id) {
+ hal_fn<void> alwaysOnDisableFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->alwaysOnDisable(id);
+ };
+ return apply(alwaysOnDisableFn, "alwaysOnDisable");
+}
+
+HalResult<Capabilities> HalController::getCapabilities() {
+ hal_fn<Capabilities> getCapabilitiesFn = [](std::shared_ptr<HalWrapper> hal) {
+ return hal->getCapabilities();
+ };
+ return apply(getCapabilitiesFn, "getCapabilities");
+}
+
+HalResult<std::vector<Effect>> HalController::getSupportedEffects() {
+ hal_fn<std::vector<Effect>> getSupportedEffectsFn = [](std::shared_ptr<HalWrapper> hal) {
+ return hal->getSupportedEffects();
+ };
+ return apply(getSupportedEffectsFn, "getSupportedEffects");
+}
+
+HalResult<std::vector<CompositePrimitive>> HalController::getSupportedPrimitives() {
+ hal_fn<std::vector<CompositePrimitive>> getSupportedPrimitivesFn =
+ [](std::shared_ptr<HalWrapper> hal) { return hal->getSupportedPrimitives(); };
+ return apply(getSupportedPrimitivesFn, "getSupportedPrimitives");
+}
+
+HalResult<milliseconds> HalController::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->performEffect(effect, strength, completionCallback);
+ };
+ return apply(performEffectFn, "performEffect");
+}
+
+HalResult<void> HalController::performComposedEffect(
+ const std::vector<CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) {
+ hal_fn<void> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->performComposedEffect(primitiveEffects, completionCallback);
+ };
+ return apply(performComposedEffectFn, "performComposedEffect");
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
new file mode 100644
index 0000000..ce20aeb
--- /dev/null
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -0,0 +1,513 @@
+/*
+ * 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 "VibratorHalWrapper"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/BnVibratorCallback.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <hardware/vibrator.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+template <class T>
+HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
+ if (cache.has_value()) {
+ // Return copy of cached value.
+ return HalResult<T>::ok(*cache);
+ }
+ HalResult<T> ret = loadFn();
+ if (ret.isOk()) {
+ // Cache copy of returned value.
+ cache.emplace(ret.value());
+ }
+ return ret;
+}
+
+template <class T>
+bool isStaticCastValid(Effect effect) {
+ T castEffect = static_cast<T>(effect);
+ auto iter = hardware::hidl_enum_range<T>();
+ return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+const constexpr char* STATUS_T_ERROR_MESSAGE_PREFIX = "status_t = ";
+const constexpr char* STATUS_V_1_0_ERROR_MESSAGE_PREFIX =
+ "android::hardware::vibrator::V1_0::Status = ";
+
+template <typename T>
+HalResult<T> HalResult<T>::fromStatus(binder::Status status, T data) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(data);
+ }
+ return HalResult<T>::failed(std::string(status.toString8().c_str()));
+}
+
+template <typename T>
+HalResult<T> HalResult<T>::fromStatus(V1_0::Status status, T data) {
+ switch (status) {
+ case V1_0::Status::OK:
+ return HalResult<T>::ok(data);
+ case V1_0::Status::UNSUPPORTED_OPERATION:
+ return HalResult<T>::unsupported();
+ default:
+ return HalResult<T>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status));
+ }
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
+ return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, data)
+ : HalResult<T>::failed(ret.description());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HalResult<void>::fromStatus(status_t status) {
+ if (status == android::OK) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(STATUS_T_ERROR_MESSAGE_PREFIX + statusToString(status));
+}
+
+HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.toString8().c_str()));
+}
+
+HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) {
+ switch (status) {
+ case V1_0::Status::OK:
+ return HalResult<void>::ok();
+ case V1_0::Status::UNSUPPORTED_OPERATION:
+ return HalResult<void>::unsupported();
+ default:
+ return HalResult<void>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status));
+ }
+}
+
+template <typename R>
+HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class HalCallbackWrapper : public Aidl::BnVibratorCallback {
+public:
+ HalCallbackWrapper(std::function<void()> completionCallback)
+ : mCompletionCallback(completionCallback) {}
+
+ binder::Status onComplete() override {
+ mCompletionCallback();
+ return binder::Status::ok();
+ }
+
+private:
+ const std::function<void()> mCompletionCallback;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> AidlHalWrapper::ping() {
+ return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder());
+}
+
+void AidlHalWrapper::tryReconnect() {
+ sp<Aidl::IVibrator> newHandle = checkVintfService<Aidl::IVibrator>();
+ if (newHandle) {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ mHandle = std::move(newHandle);
+ }
+}
+
+HalResult<void> AidlHalWrapper::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ HalResult<Capabilities> capabilities = getCapabilities();
+ bool supportsCallback = capabilities.isOk() &&
+ static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK);
+ auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+
+ auto ret = HalResult<void>::fromStatus(getHal()->on(timeout.count(), cb));
+ if (!supportsCallback && ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, timeout);
+ }
+
+ return ret;
+}
+
+HalResult<void> AidlHalWrapper::off() {
+ return HalResult<void>::fromStatus(getHal()->off());
+}
+
+HalResult<void> AidlHalWrapper::setAmplitude(int32_t amplitude) {
+ float convertedAmplitude = static_cast<float>(amplitude) / std::numeric_limits<uint8_t>::max();
+ return HalResult<void>::fromStatus(getHal()->setAmplitude(convertedAmplitude));
+}
+
+HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
+ return HalResult<void>::fromStatus(getHal()->setExternalControl(enabled));
+}
+
+HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
+ return HalResult<void>::fromStatus(getHal()->alwaysOnEnable(id, effect, strength));
+}
+
+HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) {
+ return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id));
+}
+
+HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
+ std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+ return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this),
+ mCapabilities);
+}
+
+HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
+ std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
+ return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal,
+ this),
+ mSupportedEffects);
+}
+
+HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitives() {
+ std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex);
+ return loadCached<std::vector<
+ CompositePrimitive>>(std::bind(&AidlHalWrapper::getSupportedPrimitivesInternal, this),
+ mSupportedPrimitives);
+}
+
+HalResult<milliseconds> AidlHalWrapper::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ HalResult<Capabilities> capabilities = getCapabilities();
+ bool supportsCallback = capabilities.isOk() &&
+ static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK);
+ auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+
+ int32_t lengthMs;
+ auto result = getHal()->perform(effect, strength, cb, &lengthMs);
+ milliseconds length = milliseconds(lengthMs);
+
+ auto ret = HalResult<milliseconds>::fromStatus(result, length);
+ if (!supportsCallback && ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, length);
+ }
+
+ return ret;
+}
+
+HalResult<void> AidlHalWrapper::performComposedEffect(
+ const std::vector<CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) {
+ // This method should always support callbacks, so no need to double check.
+ auto cb = new HalCallbackWrapper(completionCallback);
+ return HalResult<void>::fromStatus(getHal()->compose(primitiveEffects, cb));
+}
+
+HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
+ int32_t capabilities = 0;
+ auto result = getHal()->getCapabilities(&capabilities);
+ return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
+}
+
+HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() {
+ std::vector<Effect> supportedEffects;
+ auto result = getHal()->getSupportedEffects(&supportedEffects);
+ return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
+}
+
+HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() {
+ std::vector<CompositePrimitive> supportedPrimitives;
+ auto result = getHal()->getSupportedPrimitives(&supportedPrimitives);
+ return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
+}
+
+sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ return mHandle;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::ping() {
+ auto result = getHal()->ping();
+ return HalResult<void>::fromReturn(result);
+}
+
+template <typename I>
+void HidlHalWrapper<I>::tryReconnect() {
+ sp<I> newHandle = I::tryGetService();
+ if (newHandle) {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ mHandle = std::move(newHandle);
+ }
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ auto result = getHal()->on(timeout.count());
+ auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+ if (ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, timeout);
+ }
+ return ret;
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::off() {
+ auto result = getHal()->off();
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setAmplitude(int32_t amplitude) {
+ auto result = getHal()->setAmplitude(static_cast<uint8_t>(amplitude));
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) {
+ ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnEnable(int32_t, Effect, EffectStrength) {
+ ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) {
+ ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<Capabilities> HidlHalWrapper<I>::getCapabilities() {
+ std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+ return loadCached<Capabilities>(std::bind(&HidlHalWrapper<I>::getCapabilitiesInternal, this),
+ mCapabilities);
+}
+
+template <typename I>
+HalResult<std::vector<Effect>> HidlHalWrapper<I>::getSupportedEffects() {
+ ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available");
+ return HalResult<std::vector<Effect>>::unsupported();
+}
+
+template <typename I>
+HalResult<std::vector<CompositePrimitive>> HidlHalWrapper<I>::getSupportedPrimitives() {
+ ALOGV("Skipped getSupportedPrimitives because Vibrator HAL AIDL is not available");
+ return HalResult<std::vector<CompositePrimitive>>::unsupported();
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::performComposedEffect(const std::vector<CompositeEffect>&,
+ const std::function<void()>&) {
+ ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() {
+ hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
+ Capabilities capabilities =
+ result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
+ return HalResult<Capabilities>::fromReturn(result, capabilities);
+}
+
+template <typename I>
+template <typename T>
+HalResult<milliseconds> HidlHalWrapper<I>::performInternal(
+ perform_fn<T> performFn, sp<I> handle, T effect, EffectStrength strength,
+ const std::function<void()>& completionCallback) {
+ V1_0::Status status;
+ int32_t lengthMs;
+ auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
+ status = retStatus;
+ lengthMs = retLengthMs;
+ };
+
+ V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
+ auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
+ milliseconds length = milliseconds(lengthMs);
+
+ auto ret = HalResult<milliseconds>::fromReturn(result, status, length);
+ if (ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, length);
+ }
+
+ return ret;
+}
+
+template <typename I>
+sp<I> HidlHalWrapper<I>::getHal() {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ return mHandle;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_0::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_1::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternal(&V1_1::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_2::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternal(&V1_2::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_2::Effect>(effect)) {
+ return performInternal(&V1_2::IVibrator::perform_1_2, getHal(),
+ static_cast<V1_2::Effect>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
+ auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled));
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_2::Effect>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform_1_2, getHal(),
+ static_cast<V1_2::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_3::Effect>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform_1_3, getHal(),
+ static_cast<V1_3::Effect>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
+ Capabilities capabilities = Capabilities::NONE;
+
+ sp<V1_3::IVibrator> hal = getHal();
+ auto amplitudeResult = hal->supportsAmplitudeControl();
+ if (!amplitudeResult.isOk()) {
+ return HalResult<Capabilities>::fromReturn(amplitudeResult, capabilities);
+ }
+
+ auto externalControlResult = hal->supportsExternalControl();
+ if (amplitudeResult.withDefault(false)) {
+ capabilities |= Capabilities::AMPLITUDE_CONTROL;
+ }
+ if (externalControlResult.withDefault(false)) {
+ capabilities |= Capabilities::EXTERNAL_CONTROL;
+ }
+
+ return HalResult<Capabilities>::fromReturn(externalControlResult, capabilities);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
new file mode 100644
index 0000000..2c194b5
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
@@ -0,0 +1,82 @@
+/*
+ * 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_VIBRATOR_CALLBACK_SCHEDULER_H
+#define ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
+
+#include <android-base/thread_annotations.h>
+#include <chrono>
+#include <condition_variable>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+namespace vibrator {
+
+// Wrapper for a callback to be executed after a delay.
+class DelayedCallback {
+public:
+ using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;
+
+ DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
+ : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
+ ~DelayedCallback() = default;
+
+ void run() const;
+ bool isExpired() const;
+ Timestamp getExpiration() const;
+
+ // Compare by expiration time, where A < B when A expires first.
+ bool operator<(const DelayedCallback& other) const;
+ bool operator>(const DelayedCallback& other) const;
+
+private:
+ std::function<void()> mCallback;
+ Timestamp mExpiration;
+};
+
+// Schedules callbacks to be executed after a delay.
+class CallbackScheduler {
+public:
+ CallbackScheduler() : mCallbackThread(nullptr), mFinished(false) {}
+ virtual ~CallbackScheduler();
+
+ virtual void schedule(std::function<void()> callback, std::chrono::milliseconds delay);
+
+private:
+ std::condition_variable_any mCondition;
+ std::mutex mMutex;
+
+ // Lazily instantiated only at the first time this scheduler is used.
+ std::unique_ptr<std::thread> mCallbackThread;
+
+ // Used to quit the callback thread when this instance is being destroyed.
+ bool mFinished GUARDED_BY(mMutex);
+
+ // Priority queue with reverse comparator, so tasks that expire first will be on top.
+ std::priority_queue<DelayedCallback, std::vector<DelayedCallback>,
+ std::greater<DelayedCallback>>
+ mQueue GUARDED_BY(mMutex);
+
+ void loop();
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
new file mode 100644
index 0000000..3b61f42
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H
+#define ANDROID_OS_VIBRATORHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+// Handles the connection to he underlying HAL implementation available.
+class HalConnector {
+public:
+ HalConnector() = default;
+ virtual ~HalConnector() = default;
+
+ virtual std::shared_ptr<HalWrapper> connect(std::shared_ptr<CallbackScheduler> scheduler);
+};
+
+// Controller for Vibrator HAL handle.
+// This relies on HalConnector to connect to the underlying Vibrator HAL service and reconnects to
+// it after each failed api call. This also ensures connecting to the service is thread-safe.
+class HalController : public HalWrapper {
+public:
+ HalController()
+ : HalController(std::make_unique<HalConnector>(), std::make_shared<CallbackScheduler>()) {
+ }
+ HalController(std::unique_ptr<HalConnector> halConnector,
+ std::shared_ptr<CallbackScheduler> callbackScheduler)
+ : HalWrapper(std::move(callbackScheduler)),
+ mHalConnector(std::move(halConnector)),
+ mConnectedHal(nullptr) {}
+ virtual ~HalController() = default;
+
+ void init();
+
+ HalResult<void> ping() final override;
+ void tryReconnect() final override;
+
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) final override;
+ HalResult<void> off() final override;
+
+ HalResult<void> setAmplitude(int32_t amplitude) final override;
+ HalResult<void> setExternalControl(bool enabled) final override;
+
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) final override;
+ HalResult<void> alwaysOnDisable(int32_t id) final override;
+
+ HalResult<Capabilities> getCapabilities() final override;
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() final override;
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+ final override;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) final override;
+
+ HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) final override;
+
+private:
+ std::unique_ptr<HalConnector> mHalConnector;
+ std::mutex mConnectedHalMutex;
+ // Shared pointer to allow local copies to be used by different threads.
+ std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
+
+ template <typename T>
+ HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
+
+ template <typename T>
+ using hal_fn = std::function<HalResult<T>(std::shared_ptr<HalWrapper>)>;
+
+ template <typename T>
+ HalResult<T> apply(hal_fn<T>& halFn, const char* functionName);
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATORHALCONTROLLER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
new file mode 100644
index 0000000..7b99bbb
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_VIBRATORHALWRAPPER_H
+#define ANDROID_OS_VIBRATORHALWRAPPER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+// Result of a call to the Vibrator HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+ static HalResult<T> ok(T value) { return HalResult(value); }
+ static HalResult<T> failed(std::string msg) {
+ return HalResult(std::move(msg), /* unsupported= */ false);
+ }
+ static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
+
+ static HalResult<T> fromStatus(binder::Status status, T data);
+ static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret,
+ hardware::vibrator::V1_0::Status status, T data);
+
+ // This will throw std::bad_optional_access if this result is not ok.
+ const T& value() const { return mValue.value(); }
+ bool isOk() const { return !mUnsupported && mValue.has_value(); }
+ bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::optional<T> mValue;
+ std::string mErrorMessage;
+ bool mUnsupported;
+
+ explicit HalResult(T value)
+ : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {}
+ explicit HalResult(std::string errorMessage, bool unsupported)
+ : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
+};
+
+// Empty result of a call to the Vibrator HAL wrapper.
+template <>
+class HalResult<void> {
+public:
+ static HalResult<void> ok() { return HalResult(); }
+ static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
+ static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
+
+ static HalResult<void> fromStatus(status_t status);
+ static HalResult<void> fromStatus(binder::Status status);
+ static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status);
+
+ template <typename R>
+ static HalResult<void> fromReturn(hardware::Return<R>& ret);
+
+ bool isOk() const { return !mUnsupported && !mFailed; }
+ bool isFailed() const { return !mUnsupported && mFailed; }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::string mErrorMessage;
+ bool mFailed;
+ bool mUnsupported;
+
+ explicit HalResult(bool unsupported = false)
+ : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+ explicit HalResult(std::string errorMessage)
+ : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Vibrator HAL capabilities.
+enum class Capabilities : int32_t {
+ NONE = 0,
+ ON_CALLBACK = hardware::vibrator::IVibrator::CAP_ON_CALLBACK,
+ PERFORM_CALLBACK = hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK,
+ AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL,
+ EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL,
+ EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL,
+ COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS,
+ ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL
+};
+
+inline Capabilities operator|(Capabilities lhs, Capabilities rhs) {
+ using underlying = typename std::underlying_type<Capabilities>::type;
+ return static_cast<Capabilities>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs));
+}
+
+inline Capabilities& operator|=(Capabilities& lhs, Capabilities rhs) {
+ return lhs = lhs | rhs;
+}
+
+inline Capabilities operator&(Capabilities lhs, Capabilities rhs) {
+ using underlying = typename std::underlying_type<Capabilities>::type;
+ return static_cast<Capabilities>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs));
+}
+
+inline Capabilities& operator&=(Capabilities& lhs, Capabilities rhs) {
+ return lhs = lhs & rhs;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// Wrapper for Vibrator HAL handlers.
+class HalWrapper {
+public:
+ explicit HalWrapper(std::shared_ptr<CallbackScheduler> scheduler)
+ : mCallbackScheduler(std::move(scheduler)) {}
+ virtual ~HalWrapper() = default;
+
+ virtual HalResult<void> ping() = 0;
+ virtual void tryReconnect() = 0;
+
+ virtual HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) = 0;
+ virtual HalResult<void> off() = 0;
+
+ virtual HalResult<void> setAmplitude(int32_t amplitude) = 0;
+ virtual HalResult<void> setExternalControl(bool enabled) = 0;
+
+ virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) = 0;
+ virtual HalResult<void> alwaysOnDisable(int32_t id) = 0;
+
+ virtual HalResult<Capabilities> getCapabilities() = 0;
+ virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() = 0;
+ virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
+ getSupportedPrimitives() = 0;
+
+ virtual HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) = 0;
+
+ virtual HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) = 0;
+
+protected:
+ // Shared pointer to allow CallbackScheduler to outlive this wrapper.
+ const std::shared_ptr<CallbackScheduler> mCallbackScheduler;
+};
+
+// Wrapper for the AIDL Vibrator HAL.
+class AidlHalWrapper : public HalWrapper {
+public:
+ AidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::IVibrator> handle)
+ : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+ virtual ~AidlHalWrapper() = default;
+
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
+
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) override final;
+ HalResult<void> off() override final;
+
+ HalResult<void> setAmplitude(int32_t amplitude) override final;
+ HalResult<void> setExternalControl(bool enabled) override final;
+
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) override final;
+ HalResult<void> alwaysOnDisable(int32_t id) override final;
+
+ HalResult<Capabilities> getCapabilities() override final;
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+ override final;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+
+ HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) override final;
+
+private:
+ std::mutex mHandleMutex;
+ std::mutex mCapabilitiesMutex;
+ std::mutex mSupportedEffectsMutex;
+ std::mutex mSupportedPrimitivesMutex;
+ sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
+ std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
+ std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
+ GUARDED_BY(mSupportedEffectsMutex);
+ std::optional<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives
+ GUARDED_BY(mSupportedPrimitivesMutex);
+
+ // Loads directly from IVibrator handle, skipping caches.
+ HalResult<Capabilities> getCapabilitiesInternal();
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
+ sp<hardware::vibrator::IVibrator> getHal();
+};
+
+// Wrapper for the HDIL Vibrator HALs.
+template <typename I>
+class HidlHalWrapper : public HalWrapper {
+public:
+ HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle)
+ : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+ virtual ~HidlHalWrapper() = default;
+
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
+
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) override final;
+ HalResult<void> off() override final;
+
+ HalResult<void> setAmplitude(int32_t amplitude) override final;
+ virtual HalResult<void> setExternalControl(bool enabled) override;
+
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) override final;
+ HalResult<void> alwaysOnDisable(int32_t id) override final;
+
+ HalResult<Capabilities> getCapabilities() override final;
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+ override final;
+
+ HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) override final;
+
+protected:
+ std::mutex mHandleMutex;
+ std::mutex mCapabilitiesMutex;
+ sp<I> mHandle GUARDED_BY(mHandleMutex);
+ std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
+
+ // Loads directly from IVibrator handle, skipping the mCapabilities cache.
+ virtual HalResult<Capabilities> getCapabilitiesInternal();
+
+ template <class T>
+ using perform_fn =
+ hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength,
+ hardware::vibrator::V1_0::IVibrator::perform_cb);
+
+ template <class T>
+ HalResult<std::chrono::milliseconds> performInternal(
+ perform_fn<T> performFn, sp<I> handle, T effect,
+ hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback);
+
+ sp<I> getHal();
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.0.
+class HidlHalWrapperV1_0 : public HidlHalWrapper<hardware::vibrator::V1_0::IVibrator> {
+public:
+ HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_0::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_0::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_0() = default;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.1.
+class HidlHalWrapperV1_1 : public HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> {
+public:
+ HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_1::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_1::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_1() = default;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.2.
+class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> {
+public:
+ HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_2::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_2::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_2() = default;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.3.
+class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> {
+public:
+ HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_3::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_3::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_3() = default;
+
+ HalResult<void> setExternalControl(bool enabled) override final;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+
+protected:
+ HalResult<Capabilities> getCapabilitiesInternal() override final;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATORHALWRAPPER_H
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
new file mode 100644
index 0000000..9033124
--- /dev/null
+++ b/services/vibratorservice/test/Android.bp
@@ -0,0 +1,48 @@
+// 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: "libvibratorservice_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "VibratorCallbackSchedulerTest.cpp",
+ "VibratorHalControllerTest.cpp",
+ "VibratorHalWrapperAidlTest.cpp",
+ "VibratorHalWrapperHidlV1_0Test.cpp",
+ "VibratorHalWrapperHidlV1_1Test.cpp",
+ "VibratorHalWrapperHidlV1_2Test.cpp",
+ "VibratorHalWrapperHidlV1_3Test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libvibratorservice",
+ "libutils",
+ "android.hardware.vibrator-cpp",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
new file mode 100644
index 0000000..aaeb8f9
--- /dev/null
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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 "VibratorHalWrapperAidlTest"
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <condition_variable>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+using std::chrono::milliseconds;
+using std::chrono::steady_clock;
+using std::chrono::time_point;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorCallbackSchedulerTest : public Test {
+public:
+ void SetUp() override {
+ mScheduler = std::make_unique<vibrator::CallbackScheduler>();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mExpiredCallbacks.clear();
+ }
+
+protected:
+ std::mutex mMutex;
+ std::condition_variable_any mCondition;
+ std::unique_ptr<vibrator::CallbackScheduler> mScheduler = nullptr;
+ std::vector<int32_t> mExpiredCallbacks GUARDED_BY(mMutex);
+
+ std::function<void()> createCallback(int32_t id) {
+ return [=]() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mExpiredCallbacks.push_back(id);
+ }
+ mCondition.notify_all();
+ };
+ }
+
+ std::vector<int32_t> getExpiredCallbacks() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return std::vector<int32_t>(mExpiredCallbacks);
+ }
+
+ bool waitForCallbacks(uint32_t callbackCount, milliseconds timeout) {
+ time_point<steady_clock> expiration = steady_clock::now() + timeout;
+ while (steady_clock::now() < expiration) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (callbackCount <= mExpiredCallbacks.size()) {
+ return true;
+ }
+ mCondition.wait_until(mMutex, expiration);
+ }
+ return false;
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) {
+ mScheduler->schedule(createCallback(1), 15ms);
+
+ // Not triggered before delay.
+ ASSERT_FALSE(waitForCallbacks(1, 10ms));
+ ASSERT_TRUE(getExpiredCallbacks().empty());
+
+ ASSERT_TRUE(waitForCallbacks(1, 10ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) {
+ mScheduler->schedule(createCallback(1), 10ms);
+ mScheduler->schedule(createCallback(2), 5ms);
+ mScheduler->schedule(createCallback(3), 1ms);
+
+ ASSERT_TRUE(waitForCallbacks(3, 15ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) {
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 5; i++) {
+ threads.push_back(std::thread(
+ [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ ASSERT_TRUE(waitForCallbacks(5, 25ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
+ mScheduler->schedule(createCallback(1), 5ms);
+ mScheduler.reset(nullptr);
+
+ // Should time out waiting for callback to run.
+ ASSERT_FALSE(waitForCallbacks(1, 10ms));
+ ASSERT_TRUE(getExpiredCallbacks().empty());
+}
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
new file mode 100644
index 0000000..f04e016
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -0,0 +1,392 @@
+/*
+ * 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 "VibratorHalControllerTest"
+
+#include <android/hardware/vibrator/IVibrator.h>
+#include <cutils/atomic.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+static constexpr int MAX_ATTEMPTS = 2;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockHalWrapper : public vibrator::HalWrapper {
+public:
+ MockHalWrapper(std::shared_ptr<vibrator::CallbackScheduler> scheduler)
+ : HalWrapper(scheduler) {}
+ virtual ~MockHalWrapper() = default;
+
+ MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+ MOCK_METHOD(void, tryReconnect, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, on,
+ (milliseconds timeout, const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, off, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (int32_t amplitude), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable,
+ (int32_t id, Effect effect, EffectStrength strength), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilities, (), (override));
+ MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
+ MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
+ (Effect effect, EffectStrength strength,
+ const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, performComposedEffect,
+ (const std::vector<CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback),
+ (override));
+
+ vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); }
+};
+
+class TestHalConnector : public vibrator::HalConnector {
+public:
+ TestHalConnector(int32_t* connectCounter, std::shared_ptr<MockHalWrapper> mockHal)
+ : mConnectCounter(connectCounter), mMockHal(std::move(mockHal)) {}
+ ~TestHalConnector() = default;
+
+ std::shared_ptr<vibrator::HalWrapper> connect(
+ std::shared_ptr<vibrator::CallbackScheduler>) override final {
+ android_atomic_inc(mConnectCounter);
+ return mMockHal;
+ }
+
+private:
+ int32_t* mConnectCounter;
+ std::shared_ptr<MockHalWrapper> mMockHal;
+};
+
+class FailingHalConnector : public vibrator::HalConnector {
+public:
+ FailingHalConnector(int32_t* connectCounter) : mConnectCounter(connectCounter) {}
+ ~FailingHalConnector() = default;
+
+ std::shared_ptr<vibrator::HalWrapper> connect(
+ std::shared_ptr<vibrator::CallbackScheduler>) override final {
+ android_atomic_inc(mConnectCounter);
+ return nullptr;
+ }
+
+private:
+ int32_t* mConnectCounter;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalControllerTest : public Test {
+public:
+ void SetUp() override {
+ mConnectCounter = 0;
+ auto callbackScheduler = std::make_shared<vibrator::CallbackScheduler>();
+ mMockHal = std::make_shared<StrictMock<MockHalWrapper>>(callbackScheduler);
+ auto halConnector = std::make_unique<TestHalConnector>(&mConnectCounter, mMockHal);
+ mController = std::make_unique<vibrator::HalController>(std::move(halConnector),
+ std::move(callbackScheduler));
+ ASSERT_NE(mController, nullptr);
+ }
+
+protected:
+ int32_t mConnectCounter;
+ std::shared_ptr<MockHalWrapper> mMockHal;
+ std::unique_ptr<vibrator::HalController> mController;
+
+ void setHalExpectations(int32_t cardinality, std::vector<CompositeEffect> compositeEffects,
+ vibrator::HalResult<void> voidResult,
+ vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
+ vibrator::HalResult<std::vector<Effect>> effectsResult,
+ vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult,
+ vibrator::HalResult<milliseconds> durationResult) {
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(255)))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1)))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), getCapabilities())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(capabilitiesResult));
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(effectsResult));
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(primitivesResult));
+ EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(durationResult));
+ EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+
+ if (cardinality > 1) {
+ // One reconnection call after each failure.
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(12 * cardinality));
+ }
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalControllerTest, TestInit) {
+ mController->init();
+ ASSERT_EQ(1, mConnectCounter);
+
+ // Noop when wrapper was already initialized.
+ mController->init();
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
+ std::vector<Effect> effects;
+ effects.push_back(Effect::CLICK);
+ effects.push_back(Effect::TICK);
+ std::vector<CompositePrimitive> primitives;
+ primitives.push_back(CompositePrimitive::CLICK);
+ primitives.push_back(CompositePrimitive::THUD);
+ std::vector<CompositeEffect> compositeEffects;
+ compositeEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ compositeEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
+ vibrator::HalResult<vibrator::Capabilities>::ok(
+ vibrator::Capabilities::ON_CALLBACK),
+ vibrator::HalResult<std::vector<Effect>>::ok(effects),
+ vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives),
+ vibrator::HalResult<milliseconds>::ok(100ms));
+
+ ASSERT_TRUE(mController->ping().isOk());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isOk());
+ ASSERT_TRUE(mController->off().isOk());
+ ASSERT_TRUE(mController->setAmplitude(255).isOk());
+ ASSERT_TRUE(mController->setExternalControl(true).isOk());
+ ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isOk());
+
+ auto getCapabilitiesResult = mController->getCapabilities();
+ ASSERT_TRUE(getCapabilitiesResult.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, getCapabilitiesResult.value());
+
+ auto getSupportedEffectsResult = mController->getSupportedEffects();
+ ASSERT_TRUE(getSupportedEffectsResult.isOk());
+ ASSERT_EQ(effects, getSupportedEffectsResult.value());
+
+ auto getSupportedPrimitivesResult = mController->getSupportedPrimitives();
+ ASSERT_TRUE(getSupportedPrimitivesResult.isOk());
+ ASSERT_EQ(primitives, getSupportedPrimitivesResult.value());
+
+ auto performEffectResult =
+ mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
+ ASSERT_TRUE(performEffectResult.isOk());
+ ASSERT_EQ(100ms, performEffectResult.value());
+
+ ASSERT_TRUE(mController->performComposedEffect(compositeEffects, []() {}).isOk());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
+ setHalExpectations(/* cardinality= */ 1, std::vector<CompositeEffect>(),
+ vibrator::HalResult<void>::unsupported(),
+ vibrator::HalResult<vibrator::Capabilities>::unsupported(),
+ vibrator::HalResult<std::vector<Effect>>::unsupported(),
+ vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(),
+ vibrator::HalResult<milliseconds>::unsupported());
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isUnsupported());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
+ ASSERT_TRUE(mController->off().isUnsupported());
+ ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
+ ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(
+ mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
+ ASSERT_TRUE(mController->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+ ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
+ .isUnsupported());
+ ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
+ .isUnsupported());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) {
+ setHalExpectations(MAX_ATTEMPTS, std::vector<CompositeEffect>(),
+ vibrator::HalResult<void>::failed("message"),
+ vibrator::HalResult<vibrator::Capabilities>::failed("message"),
+ vibrator::HalResult<std::vector<Effect>>::failed("message"),
+ vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"),
+ vibrator::HalResult<milliseconds>::failed("message"));
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isFailed());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isFailed());
+ ASSERT_TRUE(mController->off().isFailed());
+ ASSERT_TRUE(mController->setAmplitude(255).isFailed());
+ ASSERT_TRUE(mController->setExternalControl(true).isFailed());
+ ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isFailed());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isFailed());
+ ASSERT_TRUE(mController->getCapabilities().isFailed());
+ ASSERT_TRUE(mController->getSupportedEffects().isFailed());
+ ASSERT_TRUE(mController->getSupportedPrimitives().isFailed());
+ ASSERT_TRUE(
+ mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
+ ASSERT_TRUE(
+ mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}).isFailed());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+ }
+
+ ASSERT_EQ(0, mConnectCounter);
+ ASSERT_TRUE(mController->ping().isOk());
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+ ASSERT_EQ(0, mConnectCounter);
+
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(10))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() { ASSERT_TRUE(mController->ping().isOk()); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // Connector was called only by the first thread to use the api.
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestNoVibratorReturnsUnsupportedAndAttemptsToReconnect) {
+ auto failingHalConnector = std::make_unique<FailingHalConnector>(&mConnectCounter);
+ mController =
+ std::make_unique<vibrator::HalController>(std::move(failingHalConnector), nullptr);
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isUnsupported());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
+ ASSERT_TRUE(mController->off().isUnsupported());
+ ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
+ ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(
+ mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
+ ASSERT_TRUE(mController->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+ ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
+ .isUnsupported());
+ ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
+ .isUnsupported());
+
+ // One connection attempt per api call.
+ ASSERT_EQ(12, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([&](milliseconds timeout, std::function<void()> callback) {
+ mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout);
+ return vibrator::HalResult<void>::ok();
+ });
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mController->on(10ms, callback).isOk());
+ ASSERT_TRUE(mController->ping().isFailed());
+ mMockHal.reset();
+ ASSERT_EQ(0, *callbackCounter.get());
+
+ // Callback triggered even after HalWrapper was reconnected.
+ std::this_thread::sleep_for(15ms);
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
new file mode 100644
index 0000000..96b76ba
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -0,0 +1,532 @@
+/*
+ * 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 "VibratorHalWrapperAidlTest"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::binder::Status;
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::IVibratorCallback;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockBinder : public BBinder {
+public:
+ MOCK_METHOD(status_t, linkToDeath,
+ (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override));
+ MOCK_METHOD(status_t, unlinkToDeath,
+ (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
+ wp<DeathRecipient>* outRecipient),
+ (override));
+ MOCK_METHOD(status_t, pingBinder, (), (override));
+};
+
+class MockIVibrator : public IVibrator {
+public:
+ MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override));
+ MOCK_METHOD(Status, off, (), (override));
+ MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override));
+ MOCK_METHOD(Status, perform,
+ (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret),
+ (override));
+ MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(Status, setAmplitude, (float amplitude), (override));
+ MOCK_METHOD(Status, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret),
+ (override));
+ MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override));
+ MOCK_METHOD(Status, compose,
+ (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
+ (override));
+ MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
+ MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperAidlTest : public Test {
+public:
+ void SetUp() override {
+ mMockBinder = new StrictMock<MockBinder>();
+ mMockHal = new StrictMock<MockIVibrator>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::AidlHalWrapper>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibrator>> mMockHal = nullptr;
+ sp<StrictMock<MockBinder>> mMockBinder = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+ACTION(TriggerCallbackInArg1) {
+ if (arg1 != nullptr) {
+ arg1->onComplete();
+ }
+}
+
+ACTION(TriggerCallbackInArg2) {
+ if (arg2 != nullptr) {
+ arg2->onComplete();
+ }
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPing) {
+ EXPECT_CALL(*mMockHal.get(), onAsBinder())
+ .Times(Exactly(2))
+ .WillRepeatedly(Return(mMockBinder.get()));
+ EXPECT_CALL(*mMockBinder.get(), pingBinder())
+ .Times(Exactly(2))
+ .WillOnce(Return(android::OK))
+ .WillRepeatedly(Return(android::DEAD_OBJECT));
+
+ ASSERT_TRUE(mWrapper->ping().isOk());
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOnWithCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(100), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(1000), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(100ms, callback).isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(1000ms, callback).isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOnWithoutCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status()));
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), on(Eq(11), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(12), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(11ms, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOff) {
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(3))
+ .WillOnce(Return(Status()))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+
+ ASSERT_TRUE(mWrapper->off().isOk());
+ ASSERT_TRUE(mWrapper->off().isUnsupported());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.1, 1e-2))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.2, 1e-2)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.5, 1e-2)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 10).isOk());
+ ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 5).isUnsupported());
+ ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 2).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestSetExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
+ .Times(Exactly(2))
+ .WillOnce(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnEnable) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ auto result = mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->alwaysOnEnable(3, Effect::POP, EffectStrength::STRONG);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnDisable) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(2)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(3)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isOk());
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(2).isUnsupported());
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesCachesResult) {
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsDoesNotCacheFailedResult) {
+ std::vector<Effect> supportedEffects;
+ supportedEffects.push_back(Effect::CLICK);
+ supportedEffects.push_back(Effect::TICK);
+
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed());
+
+ auto result = mWrapper->getSupportedEffects();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedEffects, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsCachesResult) {
+ std::vector<Effect> supportedEffects;
+ supportedEffects.push_back(Effect::CLICK);
+ supportedEffects.push_back(Effect::TICK);
+
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getSupportedEffects();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedEffects, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getSupportedEffects();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedEffects, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesDoesNotCacheFailedResult) {
+ std::vector<CompositePrimitive> supportedPrimitives;
+ supportedPrimitives.push_back(CompositePrimitive::CLICK);
+ supportedPrimitives.push_back(CompositePrimitive::THUD);
+
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
+ ASSERT_TRUE(mWrapper->getSupportedPrimitives().isFailed());
+
+ auto result = mWrapper->getSupportedPrimitives();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedPrimitives, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesCachesResult) {
+ std::vector<CompositePrimitive> supportedPrimitives;
+ supportedPrimitives.push_back(CompositePrimitive::CLICK);
+ supportedPrimitives.push_back(CompositePrimitive::THUD);
+
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getSupportedPrimitives();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedPrimitives, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getSupportedPrimitives();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedPrimitives, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1000ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithoutCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(10), Return(Status())));
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) {
+ std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
+ singleEffect.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performComposedEffect(emptyEffects, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performComposedEffect(singleEffect, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performComposedEffect(multipleEffects, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
new file mode 100644
index 0000000..06aa36f
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -0,0 +1,326 @@
+/*
+ * 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 "VibratorHalWrapperHidlV1_0Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_0 : public V1_0::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<void>, ping, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_0Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_0>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) {
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<void>(); })
+ .WillRepeatedly([]() {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ ASSERT_TRUE(mWrapper->ping().isOk());
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1))))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
+ });
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(1ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed());
+ ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) {
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(4))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
+ .WillOnce([]() {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ })
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ ASSERT_TRUE(mWrapper->off().isOk());
+ ASSERT_TRUE(mWrapper->off().isUnsupported());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(static_cast<uint8_t>(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
+ });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->setAmplitude(1).isOk());
+ ASSERT_TRUE(mWrapper->setAmplitude(2).isUnsupported());
+ ASSERT_TRUE(mWrapper->setAmplitude(3).isFailed());
+ ASSERT_TRUE(mWrapper->setAmplitude(4).isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) {
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) {
+ ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) {
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(2))
+ .WillOnce([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ })
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesWithoutAmplitudeControl) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+ return hardware::Return<bool>(false);
+ });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+ return hardware::Return<bool>(true);
+ });
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffectsUnsupported) {
+ ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedPrimitivesUnsupported) {
+ ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 10);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using TICK that is only available in v1.1
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) {
+ std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
+ singleEffect.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).isUnsupported());
+
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
new file mode 100644
index 0000000..d887efc
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 "VibratorHalWrapperHidlV1_1Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_1 : public V1_1::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_1Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_1>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 0);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using THUD that is only available in v1.2
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
new file mode 100644
index 0000000..26d9350
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 "VibratorHalWrapperHidlV1_2Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_2 : public V1_2::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_2,
+ (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_2Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_2>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 10);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using TEXTURE_TICK that is only available in v1.3
+ auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
new file mode 100644
index 0000000..5de6257
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.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 "VibratorHalWrapperHidlV1_3Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_3 : public V1_3::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_2,
+ (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_3,
+ (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_3Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_3>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL,
+ result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+ }
+
+ // Call to supportsAmplitudeControl failed.
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isFailed());
+
+ // Call to supportsExternalControl failed.
+ result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isFailed());
+
+ // Returns successful result from third call.
+ result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+
+ // Returns cached successful result.
+ result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM),
+ _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG),
+ _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 0);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
new file mode 100644
index 0000000..8d0b22e
--- /dev/null
+++ b/services/vibratorservice/test/test_utils.h
@@ -0,0 +1,71 @@
+/*
+ * 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 VIBRATORSERVICE_UNITTEST_UTIL_H_
+#define VIBRATORSERVICE_UNITTEST_UTIL_H_
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+using ::android::hardware::vibrator::CompositeEffect;
+using ::android::hardware::vibrator::CompositePrimitive;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockCallbackScheduler : public vibrator::CallbackScheduler {
+public:
+ MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay),
+ (override));
+};
+
+ACTION(TriggerSchedulerCallback) {
+ arg0();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class TestFactory {
+public:
+ static CompositeEffect createCompositeEffect(CompositePrimitive primitive,
+ std::chrono::milliseconds delay, float scale) {
+ CompositeEffect effect;
+ effect.primitive = primitive;
+ effect.delayMs = delay.count();
+ effect.scale = scale;
+ return effect;
+ }
+
+ static std::function<void()> createCountingCallback(int32_t* counter) {
+ return [counter]() { *counter += 1; };
+ }
+
+private:
+ TestFactory() = delete;
+ ~TestFactory() = delete;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace vibrator
+
+} // namespace android
+
+#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_
\ No newline at end of file
diff --git a/vulkan/include/hardware/hwvulkan.h b/vulkan/include/hardware/hwvulkan.h
index 9e9a14d..98bc8e3 100644
--- a/vulkan/include/hardware/hwvulkan.h
+++ b/vulkan/include/hardware/hwvulkan.h
@@ -54,8 +54,9 @@
/* A hwvulkan_device_t corresponds to an ICD on other systems. Currently there
* can only be one on a system (HWVULKAN_DEVICE_0). It is opened once per
* process when the Vulkan API is first used; the hw_device_t::close() function
- * is never called. Any non-trivial resource allocation should be done when
- * the VkInstance is created rather than when the hwvulkan_device_t is opened.
+ * is called upon driver unloading. Any non-trivial resource allocation should
+ * be done when the VkInstance is created rather than when the hwvulkan_device_t
+ * is opened.
*/
typedef struct hwvulkan_device_t {
struct hw_device_t common;
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 9ffe83b..ba98696 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -27,17 +27,19 @@
#define VK_ANDROID_native_buffer 1
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER 11
-/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
*
* This version of the extension transitions from gralloc0 to gralloc1 usage
* flags (int -> 2x uint64_t). The WSI implementation will temporarily continue
* to fill out deprecated fields in VkNativeBufferANDROID, and will call the
* deprecated vkGetSwapchainGrallocUsageANDROID if the new
* vkGetSwapchainGrallocUsage2ANDROID is not supported. This transitionary
- * backwards-compatibility support is temporary, and will likely be removed in
+ * backwards-compatibility support is temporary, and will likely be removed
* (along with all gralloc0 support) in a future release.
*/
-/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
*
* This version of the extension doesn't introduce new types or structs, but is
* to accommodate the new struct VkBindImageMemorySwapchainInfoKHR added in
@@ -47,97 +49,155 @@
* in VkBindImageMemorySwapchainInfoKHR will be additionally chained to the
* pNext chain of VkBindImageMemoryInfo and passed down to the driver.
*/
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
-#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
+#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
-#define VK_ANDROID_NATIVE_BUFFER_ENUM(type,id) ((type)(1000000000 + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
-#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
-#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
-#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
+#define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
+ ((type)(1000000000 + \
+ (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
+#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID \
+ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
+#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID \
+ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
+#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID \
+ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
+/* clang-format off */
typedef enum VkSwapchainImageUsageFlagBitsANDROID {
VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID = 0x00000001,
VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkSwapchainImageUsageFlagBitsANDROID;
typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
+/*
+ * struct VkNativeBufferUsage2ANDROID
+ *
+ * consumer: gralloc1 consumer usage flag
+ * producer: gralloc1 producer usage flag
+ */
typedef struct {
- uint64_t consumer;
- uint64_t producer;
+ uint64_t consumer;
+ uint64_t producer;
} VkNativeBufferUsage2ANDROID;
+/*
+ * struct VkNativeBufferANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * handle: buffer handle returned from gralloc alloc()
+ * stride: stride returned from gralloc alloc()
+ * format: gralloc format requested when the buffer was allocated
+ * usage: gralloc usage requested when the buffer was allocated
+ * usage2: gralloc usage requested when the buffer was allocated
+ */
typedef struct {
- VkStructureType sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
- const void* pNext;
-
- // Buffer handle and stride returned from gralloc alloc()
- buffer_handle_t handle;
- int stride;
-
- // Gralloc format and usage requested when the buffer was allocated.
- int format;
- int usage; // DEPRECATED in SPEC_VERSION 6
- // -- Added in SPEC_VERSION 6 --
- VkNativeBufferUsage2ANDROID usage2;
+ VkStructureType sType;
+ const void* pNext;
+ buffer_handle_t handle;
+ int stride;
+ int format;
+ int usage; /* DEPRECATED in SPEC_VERSION 6 */
+ VkNativeBufferUsage2ANDROID usage2; /* ADDED in SPEC_VERSION 6 */
} VkNativeBufferANDROID;
+/*
+ * struct VkSwapchainImageCreateInfoANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * usage: is a bitmask of VkSwapchainImageUsageFlagsANDROID
+ */
typedef struct {
- VkStructureType sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
- const void* pNext;
-
- VkSwapchainImageUsageFlagsANDROID usage;
+ VkStructureType sType;
+ const void* pNext;
+ VkSwapchainImageUsageFlagsANDROID usage;
} VkSwapchainImageCreateInfoANDROID;
+/*
+ * struct VkPhysicalDevicePresentationPropertiesANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * sharedImage: specifies if the image can be shared with the display system
+ */
typedef struct {
- VkStructureType sType; // must be VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID
- const void* pNext;
-
- VkBool32 sharedImage;
+ VkStructureType sType;
+ const void* pNext;
+ VkBool32 sharedImage;
} VkPhysicalDevicePresentationPropertiesANDROID;
-// -- DEPRECATED in SPEC_VERSION 6 --
-typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
-// -- ADDED in SPEC_VERSION 6 --
-typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
-typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
-typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
+/* DEPRECATED in SPEC_VERSION 6 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ int* grallocUsage);
+
+/* ADDED in SPEC_VERSION 6 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+ uint64_t* grallocConsumerUsage,
+ uint64_t* grallocProducerUsage);
+
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(
+ VkDevice device,
+ VkImage image,
+ int nativeFenceFd,
+ VkSemaphore semaphore,
+ VkFence fence);
+
+typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(
+ VkQueue queue,
+ uint32_t waitSemaphoreCount,
+ const VkSemaphore* pWaitSemaphores,
+ VkImage image,
+ int* pNativeFenceFd);
#ifndef VK_NO_PROTOTYPES
-// -- DEPRECATED in SPEC_VERSION 6 --
+/* DEPRECATED in SPEC_VERSION 6 */
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsageANDROID(
- VkDevice device,
- VkFormat format,
- VkImageUsageFlags imageUsage,
- int* grallocUsage
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
+ int* grallocUsage
);
-// -- ADDED in SPEC_VERSION 6 --
+
+/* ADDED in SPEC_VERSION 6 */
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
- VkDevice device,
- VkFormat format,
- VkImageUsageFlags imageUsage,
+ VkDevice device,
+ VkFormat format,
+ VkImageUsageFlags imageUsage,
VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
- uint64_t* grallocConsumerUsage,
- uint64_t* grallocProducerUsage
+ uint64_t* grallocConsumerUsage,
+ uint64_t* grallocProducerUsage
);
+
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
- VkDevice device,
- VkImage image,
- int nativeFenceFd,
- VkSemaphore semaphore,
- VkFence fence
+ VkDevice device,
+ VkImage image,
+ int nativeFenceFd,
+ VkSemaphore semaphore,
+ VkFence fence
);
+
VKAPI_ATTR VkResult VKAPI_CALL vkQueueSignalReleaseImageANDROID(
- VkQueue queue,
- uint32_t waitSemaphoreCount,
- const VkSemaphore* pWaitSemaphores,
- VkImage image,
- int* pNativeFenceFd
+ VkQueue queue,
+ uint32_t waitSemaphoreCount,
+ const VkSemaphore* pWaitSemaphores,
+ VkImage image,
+ int* pNativeFenceFd
);
+
#endif
+/* clang-format on */
#ifdef __cplusplus
}
#endif
-#endif // __VK_ANDROID_NATIVE_BUFFER_H__
+#endif /* __VK_ANDROID_NATIVE_BUFFER_H__ */
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 5b9affd..2d4690a 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -1174,23 +1174,18 @@
// ----------------------------------------------------------------------------
bool EnsureInitialized() {
- static std::once_flag once_flag;
- static bool initialized;
+ static bool initialized = false;
+ static pid_t init_attempted_for_pid = 0;
+ static std::mutex init_lock;
- std::call_once(once_flag, []() {
- if (driver::OpenHAL()) {
- initialized = true;
- }
- });
+ std::lock_guard<std::mutex> lock(init_lock);
+ if (init_attempted_for_pid == getpid())
+ return initialized;
- {
- static pid_t pid = getpid() + 1;
- static std::mutex layer_lock;
- std::lock_guard<std::mutex> lock(layer_lock);
- if (pid != getpid()) {
- pid = getpid();
- DiscoverLayers();
- }
+ init_attempted_for_pid = getpid();
+ if (driver::OpenHAL()) {
+ DiscoverLayers();
+ initialized = true;
}
return initialized;
@@ -1256,7 +1251,7 @@
ATRACE_CALL();
if (!EnsureInitialized())
- return VK_ERROR_INITIALIZATION_FAILED;
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
uint32_t count = GetLayerCount();
@@ -1280,7 +1275,7 @@
ATRACE_CALL();
if (!EnsureInitialized())
- return VK_ERROR_INITIALIZATION_FAILED;
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
if (pLayerName) {
const Layer* layer = FindLayer(pLayerName);
@@ -1456,6 +1451,11 @@
VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {
ATRACE_CALL();
+ // Load the driver here if not done yet. This api will be used in Zygote
+ // for Vulkan driver pre-loading because of the minimum overhead.
+ if (!EnsureInitialized())
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+
*pApiVersion = VK_API_VERSION_1_1;
return VK_SUCCESS;
}
diff --git a/vulkan/libvulkan/api.h b/vulkan/libvulkan/api.h
index 416cba0..2a215d7 100644
--- a/vulkan/libvulkan/api.h
+++ b/vulkan/libvulkan/api.h
@@ -24,17 +24,34 @@
namespace vulkan {
namespace api {
-// clang-format off
-VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
-VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
-VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkInstance* pInstance);
+VKAPI_ATTR void DestroyInstance(VkInstance instance,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice,
+ const VkDeviceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkDevice* pDevice);
+VKAPI_ATTR void DestroyDevice(VkDevice device,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult
+EnumerateInstanceLayerProperties(uint32_t* pPropertyCount,
+ VkLayerProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateInstanceExtensionProperties(const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
+ uint32_t* pPropertyCount,
+ VkLayerProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+ const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
VKAPI_ATTR VkResult EnumerateInstanceVersion(uint32_t* pApiVersion);
-// clang-format on
inline InstanceData& GetData(VkInstance instance) {
return driver::GetData(instance).opaque_api_data;
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 01cbf39..4068a16 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -84,6 +84,8 @@
Hal(const Hal&) = delete;
Hal& operator=(const Hal&) = delete;
+ bool ShouldUnloadBuiltinDriver();
+ void UnloadBuiltinDriver();
bool InitDebugReportIndex();
static Hal hal_;
@@ -95,15 +97,15 @@
class CreateInfoWrapper {
public:
CreateInfoWrapper(const VkInstanceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator);
CreateInfoWrapper(VkPhysicalDevice physical_dev,
const VkDeviceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator);
~CreateInfoWrapper();
VkResult Validate();
- void DowngradeApiVersion();
- void UpgradeDeviceCoreApiVersion(uint32_t api_version);
const std::bitset<ProcHook::EXTENSION_COUNT>& GetHookExtensions() const;
const std::bitset<ProcHook::EXTENSION_COUNT>& GetHalExtensions() const;
@@ -118,10 +120,12 @@
const char** names;
uint32_t name_count;
+ ExtensionFilter()
+ : exts(nullptr), ext_count(0), names(nullptr), name_count(0) {}
};
+ VkResult SanitizeApiVersion();
VkResult SanitizePNext();
-
VkResult SanitizeLayers();
VkResult SanitizeExtensions();
@@ -133,6 +137,8 @@
const bool is_instance_;
const VkAllocationCallbacks& allocator_;
+ const uint32_t loader_api_version_;
+ const uint32_t icd_api_version_;
VkPhysicalDevice physical_dev_;
@@ -241,7 +247,12 @@
const nsecs_t openTime = systemTime();
- ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
+ if (hal_.ShouldUnloadBuiltinDriver()) {
+ hal_.UnloadBuiltinDriver();
+ }
+
+ if (hal_.dev_)
+ return true;
// Use a stub device unless we successfully open a real HAL device.
hal_.dev_ = &stubhal::kDevice;
@@ -286,6 +297,38 @@
return true;
}
+bool Hal::ShouldUnloadBuiltinDriver() {
+ // Should not unload since the driver was not loaded
+ if (!hal_.dev_)
+ return false;
+
+ // Should not unload if stubhal is used on the device
+ if (hal_.dev_ == &stubhal::kDevice)
+ return false;
+
+ // Unload the driver if updated driver is chosen
+ if (android::GraphicsEnv::getInstance().getDriverNamespace())
+ return true;
+
+ return false;
+}
+
+void Hal::UnloadBuiltinDriver() {
+ ATRACE_CALL();
+
+ ALOGD("Unload builtin Vulkan driver.");
+
+ // Close the opened device
+ ALOG_ASSERT(!hal_.dev_->common.close(hal_.dev_->common),
+ "hw_device_t::close() failed.");
+
+ // Close the opened shared library in the hw_module_t
+ dlclose(hal_.dev_->common.module->dso);
+
+ hal_.dev_ = nullptr;
+ hal_.debug_report_index_ = -1;
+}
+
bool Hal::InitDebugReportIndex() {
ATRACE_CALL();
@@ -324,32 +367,27 @@
}
CreateInfoWrapper::CreateInfoWrapper(const VkInstanceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator)
: is_instance_(true),
allocator_(allocator),
+ loader_api_version_(VK_API_VERSION_1_1),
+ icd_api_version_(icd_api_version),
physical_dev_(VK_NULL_HANDLE),
instance_info_(create_info),
- extension_filter_() {
- // instance core versions need to match the loader api version
- for (uint32_t i = ProcHook::EXTENSION_CORE_1_0;
- i != ProcHook::EXTENSION_COUNT; ++i) {
- hook_extensions_.set(i);
- hal_extensions_.set(i);
- }
-}
+ extension_filter_() {}
CreateInfoWrapper::CreateInfoWrapper(VkPhysicalDevice physical_dev,
const VkDeviceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator)
: is_instance_(false),
allocator_(allocator),
+ loader_api_version_(VK_API_VERSION_1_1),
+ icd_api_version_(icd_api_version),
physical_dev_(physical_dev),
dev_info_(create_info),
- extension_filter_() {
- // initialize with baseline core API version
- hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
- hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
-}
+ extension_filter_() {}
CreateInfoWrapper::~CreateInfoWrapper() {
allocator_.pfnFree(allocator_.pUserData, extension_filter_.exts);
@@ -357,7 +395,9 @@
}
VkResult CreateInfoWrapper::Validate() {
- VkResult result = SanitizePNext();
+ VkResult result = SanitizeApiVersion();
+ if (result == VK_SUCCESS)
+ result = SanitizePNext();
if (result == VK_SUCCESS)
result = SanitizeLayers();
if (result == VK_SUCCESS)
@@ -384,6 +424,22 @@
return &dev_info_;
}
+VkResult CreateInfoWrapper::SanitizeApiVersion() {
+ if (!is_instance_ || !instance_info_.pApplicationInfo)
+ return VK_SUCCESS;
+
+ if (icd_api_version_ > VK_API_VERSION_1_0 ||
+ instance_info_.pApplicationInfo->apiVersion < VK_API_VERSION_1_1)
+ return VK_SUCCESS;
+
+ // override apiVersion to avoid error return from 1.0 icd
+ application_info_ = *instance_info_.pApplicationInfo;
+ application_info_.apiVersion = VK_API_VERSION_1_0;
+ instance_info_.pApplicationInfo = &application_info_;
+
+ return VK_SUCCESS;
+}
+
VkResult CreateInfoWrapper::SanitizePNext() {
const struct StructHeader {
VkStructureType type;
@@ -431,15 +487,33 @@
: dev_info_.ppEnabledExtensionNames;
auto& ext_count = (is_instance_) ? instance_info_.enabledExtensionCount
: dev_info_.enabledExtensionCount;
- if (!ext_count)
- return VK_SUCCESS;
VkResult result = InitExtensionFilter();
if (result != VK_SUCCESS)
return result;
- for (uint32_t i = 0; i < ext_count; i++)
- FilterExtension(ext_names[i]);
+ if (is_instance_ && icd_api_version_ < loader_api_version_) {
+ for (uint32_t i = 0; i < ext_count; i++) {
+ // Upon api downgrade, skip the promoted instance extensions in the
+ // first pass to avoid duplicate extensions.
+ const std::optional<uint32_t> version =
+ GetInstanceExtensionPromotedVersion(ext_names[i]);
+ if (version && *version > icd_api_version_ &&
+ *version <= loader_api_version_)
+ continue;
+
+ FilterExtension(ext_names[i]);
+ }
+
+ // Enable the required extensions to support core functionalities.
+ const auto promoted_extensions = GetPromotedInstanceExtensions(
+ icd_api_version_, loader_api_version_);
+ for (const auto& promoted_extension : promoted_extensions)
+ FilterExtension(promoted_extension);
+ } else {
+ for (uint32_t i = 0; i < ext_count; i++)
+ FilterExtension(ext_names[i]);
+ }
// Enable device extensions that contain physical-device commands, so that
// vkGetInstanceProcAddr will return those physical-device commands.
@@ -447,6 +521,23 @@
hook_extensions_.set(ProcHook::KHR_swapchain);
}
+ const uint32_t api_version =
+ is_instance_ ? loader_api_version_
+ : std::min(icd_api_version_, loader_api_version_);
+ switch (api_version) {
+ case VK_API_VERSION_1_1:
+ hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
+ hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
+ [[clang::fallthrough]];
+ case VK_API_VERSION_1_0:
+ hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
+ hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
+ break;
+ default:
+ ALOGE("Unknown API version[%u]", api_version);
+ break;
+ }
+
ext_names = extension_filter_.names;
ext_count = extension_filter_.name_count;
@@ -504,10 +595,24 @@
filter.ext_count = count;
// allocate name array
- uint32_t enabled_ext_count = (is_instance_)
- ? instance_info_.enabledExtensionCount
- : dev_info_.enabledExtensionCount;
- count = std::min(filter.ext_count, enabled_ext_count);
+ if (is_instance_) {
+ uint32_t enabled_ext_count = instance_info_.enabledExtensionCount;
+
+ // It requires enabling additional promoted extensions to downgrade api,
+ // so we reserve enough space here.
+ if (icd_api_version_ < loader_api_version_) {
+ enabled_ext_count += CountPromotedInstanceExtensions(
+ icd_api_version_, loader_api_version_);
+ }
+
+ count = std::min(filter.ext_count, enabled_ext_count);
+ } else {
+ count = std::min(filter.ext_count, dev_info_.enabledExtensionCount);
+ }
+
+ if (!count)
+ return VK_SUCCESS;
+
filter.names = reinterpret_cast<const char**>(allocator_.pfnAllocation(
allocator_.pUserData, sizeof(const char*) * count, alignof(const char*),
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
@@ -535,6 +640,10 @@
hook_extensions_.set(ext_bit);
break;
case ProcHook::KHR_get_physical_device_properties2:
+ case ProcHook::KHR_device_group_creation:
+ case ProcHook::KHR_external_memory_capabilities:
+ case ProcHook::KHR_external_semaphore_capabilities:
+ case ProcHook::KHR_external_fence_capabilities:
case ProcHook::EXTENSION_UNKNOWN:
// Extensions we don't need to do anything about at this level
break;
@@ -591,6 +700,10 @@
case ProcHook::KHR_android_surface:
case ProcHook::KHR_get_physical_device_properties2:
+ case ProcHook::KHR_device_group_creation:
+ case ProcHook::KHR_external_memory_capabilities:
+ case ProcHook::KHR_external_semaphore_capabilities:
+ case ProcHook::KHR_external_fence_capabilities:
case ProcHook::KHR_get_surface_capabilities2:
case ProcHook::KHR_surface:
case ProcHook::EXT_debug_report:
@@ -636,40 +749,6 @@
}
}
-void CreateInfoWrapper::DowngradeApiVersion() {
- // If pApplicationInfo is NULL, apiVersion is assumed to be 1.0:
- if (instance_info_.pApplicationInfo) {
- application_info_ = *instance_info_.pApplicationInfo;
- instance_info_.pApplicationInfo = &application_info_;
- application_info_.apiVersion = VK_API_VERSION_1_0;
- }
-}
-
-void CreateInfoWrapper::UpgradeDeviceCoreApiVersion(uint32_t api_version) {
- ALOG_ASSERT(!is_instance_, "Device only API called by instance wrapper.");
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
- api_version ^= VK_VERSION_PATCH(api_version);
-#pragma clang diagnostic pop
-
- // cap the API version to the loader supported highest version
- if (api_version > VK_API_VERSION_1_1)
- api_version = VK_API_VERSION_1_1;
-
- switch (api_version) {
- case VK_API_VERSION_1_1:
- hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
- hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
- [[clang::fallthrough]];
- case VK_API_VERSION_1_0:
- break;
- default:
- ALOGD("Unknown upgrade API version[%u]", api_version);
- break;
- }
-}
-
VKAPI_ATTR void* DefaultAllocate(void*,
size_t size,
size_t alignment,
@@ -901,21 +980,14 @@
return result;
}
-bool QueryPresentationProperties(
+void QueryPresentationProperties(
VkPhysicalDevice physicalDevice,
- VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties) {
- const InstanceData& data = GetData(physicalDevice);
-
- // GPDP2 must be present and enabled on the instance.
- if (!data.driver.GetPhysicalDeviceProperties2KHR &&
- !data.driver.GetPhysicalDeviceProperties2)
- return false;
-
+ VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) {
// Request the android-specific presentation properties via GPDP2
- VkPhysicalDeviceProperties2KHR properties = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
+ VkPhysicalDeviceProperties2 properties = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
presentation_properties,
- {}
+ {},
};
#pragma clang diagnostic push
@@ -926,14 +998,7 @@
presentation_properties->pNext = nullptr;
presentation_properties->sharedImage = VK_FALSE;
- if (data.driver.GetPhysicalDeviceProperties2KHR) {
- data.driver.GetPhysicalDeviceProperties2KHR(physicalDevice,
- &properties);
- } else {
- data.driver.GetPhysicalDeviceProperties2(physicalDevice, &properties);
- }
-
- return true;
+ GetPhysicalDeviceProperties2(physicalDevice, &properties);
}
VkResult EnumerateDeviceExtensionProperties(
@@ -955,8 +1020,8 @@
}
VkPhysicalDevicePresentationPropertiesANDROID presentation_properties;
- if (QueryPresentationProperties(physicalDevice, &presentation_properties) &&
- presentation_properties.sharedImage) {
+ QueryPresentationProperties(physicalDevice, &presentation_properties);
+ if (presentation_properties.sharedImage) {
loader_extensions.push_back({
VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME,
VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION});
@@ -1025,49 +1090,32 @@
const VkAllocationCallbacks& data_allocator =
(pAllocator) ? *pAllocator : GetDefaultAllocator();
- CreateInfoWrapper wrapper(*pCreateInfo, data_allocator);
- VkResult result = wrapper.Validate();
- if (result != VK_SUCCESS)
- return result;
-
- ATRACE_BEGIN("AllocateInstanceData");
- InstanceData* data = AllocateInstanceData(data_allocator);
- ATRACE_END();
- if (!data)
- return VK_ERROR_OUT_OF_HOST_MEMORY;
-
- data->hook_extensions |= wrapper.GetHookExtensions();
-
- ATRACE_BEGIN("autoDowngradeApiVersion");
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
- uint32_t api_version = ((pCreateInfo->pApplicationInfo)
- ? pCreateInfo->pApplicationInfo->apiVersion
- : VK_API_VERSION_1_0);
- uint32_t api_major_version = VK_VERSION_MAJOR(api_version);
- uint32_t api_minor_version = VK_VERSION_MINOR(api_version);
- uint32_t icd_api_version;
+ VkResult result = VK_SUCCESS;
+ uint32_t icd_api_version = VK_API_VERSION_1_0;
PFN_vkEnumerateInstanceVersion pfn_enumerate_instance_version =
reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
Hal::Device().GetInstanceProcAddr(nullptr,
"vkEnumerateInstanceVersion"));
- if (!pfn_enumerate_instance_version) {
- icd_api_version = VK_API_VERSION_1_0;
- } else {
+ if (pfn_enumerate_instance_version) {
ATRACE_BEGIN("pfn_enumerate_instance_version");
result = (*pfn_enumerate_instance_version)(&icd_api_version);
ATRACE_END();
- }
- uint32_t icd_api_major_version = VK_VERSION_MAJOR(icd_api_version);
- uint32_t icd_api_minor_version = VK_VERSION_MINOR(icd_api_version);
+ if (result != VK_SUCCESS)
+ return result;
- if ((icd_api_major_version == 1) && (icd_api_minor_version == 0) &&
- ((api_major_version > 1) || (api_minor_version > 0))) {
- api_version = VK_API_VERSION_1_0;
- wrapper.DowngradeApiVersion();
+ icd_api_version ^= VK_VERSION_PATCH(icd_api_version);
}
-#pragma clang diagnostic pop
- ATRACE_END();
+
+ CreateInfoWrapper wrapper(*pCreateInfo, icd_api_version, data_allocator);
+ result = wrapper.Validate();
+ if (result != VK_SUCCESS)
+ return result;
+
+ InstanceData* data = AllocateInstanceData(data_allocator);
+ if (!data)
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+ data->hook_extensions |= wrapper.GetHookExtensions();
// call into the driver
VkInstance instance;
@@ -1131,7 +1179,16 @@
const VkAllocationCallbacks& data_allocator =
(pAllocator) ? *pAllocator : instance_data.allocator;
- CreateInfoWrapper wrapper(physicalDevice, *pCreateInfo, data_allocator);
+ VkPhysicalDeviceProperties properties;
+ ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
+ instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
+ &properties);
+ ATRACE_END();
+
+ CreateInfoWrapper wrapper(
+ physicalDevice, *pCreateInfo,
+ properties.apiVersion ^ VK_VERSION_PATCH(properties.apiVersion),
+ data_allocator);
VkResult result = wrapper.Validate();
if (result != VK_SUCCESS)
return result;
@@ -1143,13 +1200,6 @@
if (!data)
return VK_ERROR_OUT_OF_HOST_MEMORY;
- VkPhysicalDeviceProperties properties;
- ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
- instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
- &properties);
- ATRACE_END();
-
- wrapper.UpgradeDeviceCoreApiVersion(properties.apiVersion);
data->hook_extensions |= wrapper.GetHookExtensions();
// call into the driver
@@ -1246,7 +1296,8 @@
VkResult result = VK_SUCCESS;
const auto& data = GetData(instance);
- if (!data.driver.EnumeratePhysicalDeviceGroups) {
+ if (!data.driver.EnumeratePhysicalDeviceGroups &&
+ !data.driver.EnumeratePhysicalDeviceGroupsKHR) {
uint32_t device_count = 0;
result = EnumeratePhysicalDevices(instance, &device_count, nullptr);
if (result < 0)
@@ -1278,9 +1329,15 @@
pPhysicalDeviceGroupProperties[i].subsetAllocation = 0;
}
} else {
- result = data.driver.EnumeratePhysicalDeviceGroups(
- instance, pPhysicalDeviceGroupCount,
- pPhysicalDeviceGroupProperties);
+ if (data.driver.EnumeratePhysicalDeviceGroups) {
+ result = data.driver.EnumeratePhysicalDeviceGroups(
+ instance, pPhysicalDeviceGroupCount,
+ pPhysicalDeviceGroupProperties);
+ } else {
+ result = data.driver.EnumeratePhysicalDeviceGroupsKHR(
+ instance, pPhysicalDeviceGroupCount,
+ pPhysicalDeviceGroupProperties);
+ }
if ((result == VK_SUCCESS || result == VK_INCOMPLETE) &&
*pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) {
for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) {
@@ -1321,10 +1378,10 @@
if (*pQueue != VK_NULL_HANDLE) SetData(*pQueue, data);
}
-VKAPI_ATTR VkResult
-AllocateCommandBuffers(VkDevice device,
- const VkCommandBufferAllocateInfo* pAllocateInfo,
- VkCommandBuffer* pCommandBuffers) {
+VkResult AllocateCommandBuffers(
+ VkDevice device,
+ const VkCommandBufferAllocateInfo* pAllocateInfo,
+ VkCommandBuffer* pCommandBuffers) {
ATRACE_CALL();
const auto& data = GetData(device);
@@ -1339,10 +1396,10 @@
return result;
}
-VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
- uint32_t submitCount,
- const VkSubmitInfo* pSubmits,
- VkFence fence) {
+VkResult QueueSubmit(VkQueue queue,
+ uint32_t submitCount,
+ const VkSubmitInfo* pSubmits,
+ VkFence fence) {
ATRACE_CALL();
const auto& data = GetData(queue);
@@ -1350,5 +1407,198 @@
return data.driver.QueueSubmit(queue, submitCount, pSubmits, fence);
}
+void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceFeatures2* pFeatures) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceFeatures2) {
+ driver.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
+ return;
+ }
+
+ driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
+}
+
+void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceProperties2* pProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceProperties2) {
+ driver.GetPhysicalDeviceProperties2(physicalDevice, pProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceProperties2KHR(physicalDevice, pProperties);
+}
+
+void GetPhysicalDeviceFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkFormatProperties2* pFormatProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceFormatProperties2) {
+ driver.GetPhysicalDeviceFormatProperties2(physicalDevice, format,
+ pFormatProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceFormatProperties2KHR(physicalDevice, format,
+ pFormatProperties);
+}
+
+VkResult GetPhysicalDeviceImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
+ VkImageFormatProperties2* pImageFormatProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceImageFormatProperties2) {
+ return driver.GetPhysicalDeviceImageFormatProperties2(
+ physicalDevice, pImageFormatInfo, pImageFormatProperties);
+ }
+
+ return driver.GetPhysicalDeviceImageFormatProperties2KHR(
+ physicalDevice, pImageFormatInfo, pImageFormatProperties);
+}
+
+void GetPhysicalDeviceQueueFamilyProperties2(
+ VkPhysicalDevice physicalDevice,
+ uint32_t* pQueueFamilyPropertyCount,
+ VkQueueFamilyProperties2* pQueueFamilyProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceQueueFamilyProperties2) {
+ driver.GetPhysicalDeviceQueueFamilyProperties2(
+ physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceQueueFamilyProperties2KHR(
+ physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+}
+
+void GetPhysicalDeviceMemoryProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceMemoryProperties2) {
+ driver.GetPhysicalDeviceMemoryProperties2(physicalDevice,
+ pMemoryProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceMemoryProperties2KHR(physicalDevice,
+ pMemoryProperties);
+}
+
+void GetPhysicalDeviceSparseImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo,
+ uint32_t* pPropertyCount,
+ VkSparseImageFormatProperties2* pProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceSparseImageFormatProperties2) {
+ driver.GetPhysicalDeviceSparseImageFormatProperties2(
+ physicalDevice, pFormatInfo, pPropertyCount, pProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceSparseImageFormatProperties2KHR(
+ physicalDevice, pFormatInfo, pPropertyCount, pProperties);
+}
+
+void GetPhysicalDeviceExternalBufferProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
+ VkExternalBufferProperties* pExternalBufferProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceExternalBufferProperties) {
+ driver.GetPhysicalDeviceExternalBufferProperties(
+ physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
+ return;
+ }
+
+ if (driver.GetPhysicalDeviceExternalBufferPropertiesKHR) {
+ driver.GetPhysicalDeviceExternalBufferPropertiesKHR(
+ physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
+ return;
+ }
+
+ memset(&pExternalBufferProperties->externalMemoryProperties, 0,
+ sizeof(VkExternalMemoryProperties));
+}
+
+void GetPhysicalDeviceExternalSemaphoreProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
+ VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceExternalSemaphoreProperties) {
+ driver.GetPhysicalDeviceExternalSemaphoreProperties(
+ physicalDevice, pExternalSemaphoreInfo,
+ pExternalSemaphoreProperties);
+ return;
+ }
+
+ if (driver.GetPhysicalDeviceExternalSemaphorePropertiesKHR) {
+ driver.GetPhysicalDeviceExternalSemaphorePropertiesKHR(
+ physicalDevice, pExternalSemaphoreInfo,
+ pExternalSemaphoreProperties);
+ return;
+ }
+
+ pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
+ pExternalSemaphoreProperties->compatibleHandleTypes = 0;
+ pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
+}
+
+void GetPhysicalDeviceExternalFenceProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
+ VkExternalFenceProperties* pExternalFenceProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceExternalFenceProperties) {
+ driver.GetPhysicalDeviceExternalFenceProperties(
+ physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
+ return;
+ }
+
+ if (driver.GetPhysicalDeviceExternalFencePropertiesKHR) {
+ driver.GetPhysicalDeviceExternalFencePropertiesKHR(
+ physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
+ return;
+ }
+
+ pExternalFenceProperties->exportFromImportedHandleTypes = 0;
+ pExternalFenceProperties->compatibleHandleTypes = 0;
+ pExternalFenceProperties->externalFenceFeatures = 0;
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 23c717c..14c516b 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -103,30 +103,95 @@
bool OpenHAL();
const VkAllocationCallbacks& GetDefaultAllocator();
-bool QueryPresentationProperties(
+void QueryPresentationProperties(
VkPhysicalDevice physicalDevice,
- VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties);
+ VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties);
-// clang-format off
-VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName);
-VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName);
-VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-
-VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-
-VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
-VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
-VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
-
-VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
-VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
-
-VKAPI_ATTR void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);
-VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
-VKAPI_ATTR VkResult AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
-VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
-// clang-format on
+VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance,
+ const char* pName);
+VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device,
+ const char* pName);
+VKAPI_ATTR VkResult
+EnumerateInstanceExtensionProperties(const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+ const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkInstance* pInstance);
+VKAPI_ATTR void DestroyInstance(VkInstance instance,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice,
+ const VkDeviceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkDevice* pDevice);
+VKAPI_ATTR void DestroyDevice(VkDevice device,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult
+EnumeratePhysicalDevices(VkInstance instance,
+ uint32_t* pPhysicalDeviceCount,
+ VkPhysicalDevice* pPhysicalDevices);
+VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(
+ VkInstance instance,
+ uint32_t* pPhysicalDeviceGroupCount,
+ VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
+VKAPI_ATTR void GetDeviceQueue(VkDevice device,
+ uint32_t queueFamilyIndex,
+ uint32_t queueIndex,
+ VkQueue* pQueue);
+VKAPI_ATTR void GetDeviceQueue2(VkDevice device,
+ const VkDeviceQueueInfo2* pQueueInfo,
+ VkQueue* pQueue);
+VKAPI_ATTR VkResult
+AllocateCommandBuffers(VkDevice device,
+ const VkCommandBufferAllocateInfo* pAllocateInfo,
+ VkCommandBuffer* pCommandBuffers);
+VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
+ uint32_t submitCount,
+ const VkSubmitInfo* pSubmits,
+ VkFence fence);
+VKAPI_ATTR void GetPhysicalDeviceFeatures2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceFeatures2* pFeatures);
+VKAPI_ATTR void GetPhysicalDeviceProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkFormatProperties2* pFormatProperties);
+VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
+ VkImageFormatProperties2* pImageFormatProperties);
+VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(
+ VkPhysicalDevice physicalDevice,
+ uint32_t* pQueueFamilyPropertyCount,
+ VkQueueFamilyProperties2* pQueueFamilyProperties);
+VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
+VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo,
+ uint32_t* pPropertyCount,
+ VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
+ VkExternalBufferProperties* pExternalBufferProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
+ VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
+ VkExternalFenceProperties* pExternalFenceProperties);
template <typename DispatchableType>
void StaticAssertDispatchable(DispatchableType) {
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 52205e9..5f37a50 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -363,6 +363,55 @@
reinterpret_cast<PFN_vkVoidFunction>(checkedGetPastPresentationTimingGOOGLE),
},
{
+ "vkGetPhysicalDeviceExternalBufferProperties",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalBufferProperties),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceExternalFenceProperties",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalFenceProperties),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceExternalSemaphoreProperties",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalSemaphoreProperties),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceFeatures2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceFeatures2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceFormatProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceFormatProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceImageFormatProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceImageFormatProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceMemoryProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceMemoryProperties2),
+ nullptr,
+ },
+ {
"vkGetPhysicalDevicePresentRectanglesKHR",
ProcHook::INSTANCE,
ProcHook::KHR_swapchain,
@@ -370,6 +419,27 @@
nullptr,
},
{
+ "vkGetPhysicalDeviceProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceQueueFamilyProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceQueueFamilyProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceSparseImageFormatProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSparseImageFormatProperties2),
+ nullptr,
+ },
+ {
"vkGetPhysicalDeviceSurfaceCapabilities2KHR",
ProcHook::INSTANCE,
ProcHook::KHR_get_surface_capabilities2,
@@ -480,10 +550,9 @@
} // namespace
const ProcHook* GetProcHook(const char* name) {
- const auto& begin = g_proc_hooks;
- const auto& end =
- g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
- const auto hook = std::lower_bound(
+ auto begin = std::cbegin(g_proc_hooks);
+ auto end = std::cend(g_proc_hooks);
+ auto hook = std::lower_bound(
begin, end, name,
[](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
@@ -505,6 +574,10 @@
if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer;
if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2;
if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2;
+ if (strcmp(name, "VK_KHR_device_group_creation") == 0) return ProcHook::KHR_device_group_creation;
+ if (strcmp(name, "VK_KHR_external_memory_capabilities") == 0) return ProcHook::KHR_external_memory_capabilities;
+ if (strcmp(name, "VK_KHR_external_semaphore_capabilities") == 0) return ProcHook::KHR_external_semaphore_capabilities;
+ if (strcmp(name, "VK_KHR_external_fence_capabilities") == 0) return ProcHook::KHR_external_fence_capabilities;
// clang-format on
return ProcHook::EXTENSION_UNKNOWN;
}
@@ -543,9 +616,28 @@
INIT_PROC_EXT(EXT_debug_report, true, instance, CreateDebugReportCallbackEXT);
INIT_PROC_EXT(EXT_debug_report, true, instance, DestroyDebugReportCallbackEXT);
INIT_PROC_EXT(EXT_debug_report, true, instance, DebugReportMessageEXT);
+ INIT_PROC(false, instance, GetPhysicalDeviceFeatures2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceFeatures2KHR);
INIT_PROC(false, instance, GetPhysicalDeviceProperties2);
INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceFormatProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceFormatProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceImageFormatProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceImageFormatProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceQueueFamilyProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceQueueFamilyProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceMemoryProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceMemoryProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceSparseImageFormatProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceSparseImageFormatProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceExternalBufferProperties);
+ INIT_PROC_EXT(KHR_external_memory_capabilities, true, instance, GetPhysicalDeviceExternalBufferPropertiesKHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceExternalSemaphoreProperties);
+ INIT_PROC_EXT(KHR_external_semaphore_capabilities, true, instance, GetPhysicalDeviceExternalSemaphorePropertiesKHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties);
+ INIT_PROC_EXT(KHR_external_fence_capabilities, true, instance, GetPhysicalDeviceExternalFencePropertiesKHR);
INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
+ INIT_PROC_EXT(KHR_device_group_creation, true, instance, EnumeratePhysicalDeviceGroupsKHR);
// clang-format on
return success;
@@ -577,5 +669,53 @@
return success;
}
+const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = {
+ // clang-format off
+ std::make_pair("VK_KHR_device_group_creation", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_external_fence_capabilities", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_external_memory_capabilities", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_external_semaphore_capabilities", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_get_physical_device_properties2", VK_API_VERSION_1_1),
+ // clang-format on
+};
+
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ auto iter =
+ std::lower_bound(begin, end, name,
+ [](const std::pair<const char*, uint32_t>& e,
+ const char* n) { return strcmp(e.first, n) < 0; });
+ return (iter < end && strcmp(iter->first, name) == 0)
+ ? std::optional<uint32_t>(iter->second)
+ : std::nullopt;
+}
+
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ uint32_t count = 0;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ count++;
+
+ return count;
+}
+
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ std::vector<const char*> extensions;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ extensions.emplace_back(iter->first);
+
+ return extensions;
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 43c4d14..1aba674 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -23,6 +23,8 @@
#include <vulkan/vulkan.h>
#include <bitset>
+#include <optional>
+#include <vector>
namespace vulkan {
namespace driver {
@@ -48,6 +50,10 @@
ANDROID_external_memory_android_hardware_buffer,
KHR_bind_memory2,
KHR_get_physical_device_properties2,
+ KHR_device_group_creation,
+ KHR_external_memory_capabilities,
+ KHR_external_semaphore_capabilities,
+ KHR_external_fence_capabilities,
EXTENSION_CORE_1_0,
EXTENSION_CORE_1_1,
@@ -74,9 +80,28 @@
PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
+ PFN_vkGetPhysicalDeviceFeatures2 GetPhysicalDeviceFeatures2;
+ PFN_vkGetPhysicalDeviceFeatures2KHR GetPhysicalDeviceFeatures2KHR;
PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2;
PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR;
+ PFN_vkGetPhysicalDeviceFormatProperties2 GetPhysicalDeviceFormatProperties2;
+ PFN_vkGetPhysicalDeviceFormatProperties2KHR GetPhysicalDeviceFormatProperties2KHR;
+ PFN_vkGetPhysicalDeviceImageFormatProperties2 GetPhysicalDeviceImageFormatProperties2;
+ PFN_vkGetPhysicalDeviceImageFormatProperties2KHR GetPhysicalDeviceImageFormatProperties2KHR;
+ PFN_vkGetPhysicalDeviceQueueFamilyProperties2 GetPhysicalDeviceQueueFamilyProperties2;
+ PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR GetPhysicalDeviceQueueFamilyProperties2KHR;
+ PFN_vkGetPhysicalDeviceMemoryProperties2 GetPhysicalDeviceMemoryProperties2;
+ PFN_vkGetPhysicalDeviceMemoryProperties2KHR GetPhysicalDeviceMemoryProperties2KHR;
+ PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 GetPhysicalDeviceSparseImageFormatProperties2;
+ PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR GetPhysicalDeviceSparseImageFormatProperties2KHR;
+ PFN_vkGetPhysicalDeviceExternalBufferProperties GetPhysicalDeviceExternalBufferProperties;
+ PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR GetPhysicalDeviceExternalBufferPropertiesKHR;
+ PFN_vkGetPhysicalDeviceExternalSemaphoreProperties GetPhysicalDeviceExternalSemaphoreProperties;
+ PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR GetPhysicalDeviceExternalSemaphorePropertiesKHR;
+ PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties;
+ PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR GetPhysicalDeviceExternalFencePropertiesKHR;
PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
+ PFN_vkEnumeratePhysicalDeviceGroupsKHR EnumeratePhysicalDeviceGroupsKHR;
// clang-format on
};
@@ -109,6 +134,12 @@
PFN_vkGetDeviceProcAddr get_proc,
const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name);
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index d3ed88d..0f791c9 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -881,11 +881,10 @@
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
VkPhysicalDevicePresentationPropertiesANDROID present_properties;
- if (QueryPresentationProperties(pdev, &present_properties)) {
- if (present_properties.sharedImage) {
- present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR);
- present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
- }
+ QueryPresentationProperties(pdev, &present_properties);
+ if (present_properties.sharedImage) {
+ present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR);
+ present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
}
uint32_t num_modes = uint32_t(present_modes.size());
@@ -970,7 +969,6 @@
strerror(-err), err);
}
- // TODO(b/143294545): Return something better than "whole window"
pRects[0].offset.x = 0;
pRects[0].offset.y = 0;
pRects[0].extent = VkExtent2D{static_cast<uint32_t>(width),
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index a64a702..6a73023 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -40,6 +40,10 @@
'VK_ANDROID_external_memory_android_hardware_buffer',
'VK_KHR_bind_memory2',
'VK_KHR_get_physical_device_properties2',
+ 'VK_KHR_device_group_creation',
+ 'VK_KHR_external_memory_capabilities',
+ 'VK_KHR_external_semaphore_capabilities',
+ 'VK_KHR_external_fence_capabilities',
]
# Functions needed at vulkan::driver level.
@@ -71,12 +75,41 @@
'vkDestroyImage',
'vkGetPhysicalDeviceProperties',
- 'vkGetPhysicalDeviceProperties2',
- 'vkGetPhysicalDeviceProperties2KHR',
# VK_KHR_swapchain v69 requirement
'vkBindImageMemory2',
'vkBindImageMemory2KHR',
+
+ # For promoted VK_KHR_device_group_creation
+ 'vkEnumeratePhysicalDeviceGroupsKHR',
+
+ # For promoted VK_KHR_get_physical_device_properties2
+ 'vkGetPhysicalDeviceFeatures2',
+ 'vkGetPhysicalDeviceFeatures2KHR',
+ 'vkGetPhysicalDeviceProperties2',
+ 'vkGetPhysicalDeviceProperties2KHR',
+ 'vkGetPhysicalDeviceFormatProperties2',
+ 'vkGetPhysicalDeviceFormatProperties2KHR',
+ 'vkGetPhysicalDeviceImageFormatProperties2',
+ 'vkGetPhysicalDeviceImageFormatProperties2KHR',
+ 'vkGetPhysicalDeviceQueueFamilyProperties2',
+ 'vkGetPhysicalDeviceQueueFamilyProperties2KHR',
+ 'vkGetPhysicalDeviceMemoryProperties2',
+ 'vkGetPhysicalDeviceMemoryProperties2KHR',
+ 'vkGetPhysicalDeviceSparseImageFormatProperties2',
+ 'vkGetPhysicalDeviceSparseImageFormatProperties2KHR',
+
+ # For promoted VK_KHR_external_memory_capabilities
+ 'vkGetPhysicalDeviceExternalBufferProperties',
+ 'vkGetPhysicalDeviceExternalBufferPropertiesKHR',
+
+ # For promoted VK_KHR_external_semaphore_capabilities
+ 'vkGetPhysicalDeviceExternalSemaphoreProperties',
+ 'vkGetPhysicalDeviceExternalSemaphorePropertiesKHR',
+
+ # For promoted VK_KHR_external_fence_capabilities
+ 'vkGetPhysicalDeviceExternalFenceProperties',
+ 'vkGetPhysicalDeviceExternalFencePropertiesKHR',
]
# Functions intercepted at vulkan::driver level.
@@ -106,6 +139,24 @@
# VK_KHR_swapchain v69 requirement
'vkBindImageMemory2',
'vkBindImageMemory2KHR',
+
+ # For promoted VK_KHR_get_physical_device_properties2
+ 'vkGetPhysicalDeviceFeatures2',
+ 'vkGetPhysicalDeviceProperties2',
+ 'vkGetPhysicalDeviceFormatProperties2',
+ 'vkGetPhysicalDeviceImageFormatProperties2',
+ 'vkGetPhysicalDeviceQueueFamilyProperties2',
+ 'vkGetPhysicalDeviceMemoryProperties2',
+ 'vkGetPhysicalDeviceSparseImageFormatProperties2',
+
+ # For promoted VK_KHR_external_memory_capabilities
+ 'vkGetPhysicalDeviceExternalBufferProperties',
+
+ # For promoted VK_KHR_external_semaphore_capabilities
+ 'vkGetPhysicalDeviceExternalSemaphoreProperties',
+
+ # For promoted VK_KHR_external_fence_capabilities
+ 'vkGetPhysicalDeviceExternalFenceProperties',
]
@@ -162,6 +213,8 @@
#include <vulkan/vulkan.h>
#include <bitset>
+#include <optional>
+#include <vector>
namespace vulkan {
namespace driver {
@@ -229,6 +282,12 @@
PFN_vkGetDeviceProcAddr get_proc,
const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name);
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+
} // namespace driver
} // namespace vulkan
@@ -464,10 +523,9 @@
} // namespace
const ProcHook* GetProcHook(const char* name) {
- const auto& begin = g_proc_hooks;
- const auto& end =
- g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
- const auto hook = std::lower_bound(
+ auto begin = std::cbegin(g_proc_hooks);
+ auto end = std::cend(g_proc_hooks);
+ auto hook = std::lower_bound(
begin, end, name,
[](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
@@ -539,6 +597,54 @@
return success;
}
+const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = {
+ // clang-format off\n""")
+
+ for key, value in sorted(gencom.promoted_inst_ext_dict.items()):
+ f.write(gencom.indent(1) + 'std::make_pair("' + key + '", ' + value + '),\n')
+
+ f.write("""\
+ // clang-format on
+};
+
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ auto iter =
+ std::lower_bound(begin, end, name,
+ [](const std::pair<const char*, uint32_t>& e,
+ const char* n) { return strcmp(e.first, n) < 0; });
+ return (iter < end && strcmp(iter->first, name) == 0)
+ ? std::optional<uint32_t>(iter->second)
+ : std::nullopt;
+}
+
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ uint32_t count = 0;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ count++;
+
+ return count;
+}
+
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ std::vector<const char*> extensions;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ extensions.emplace_back(iter->first);
+
+ return extensions;
+}
+
} // namespace driver
} // namespace vulkan\n""")
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index ef0719d..ef021f2 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -97,6 +97,9 @@
# Dict for mapping a function to the core Vulkan API version.
version_dict = {}
+# Dict for mapping a promoted instance extension to the core Vulkan API version.
+promoted_inst_ext_dict = {}
+
def indent(num):
"""Returns the requested indents.
@@ -183,6 +186,15 @@
return version[11:]
+def version_2_api_version(version):
+ """Returns the api version from a version string.
+
+ Args:
+ version: Vulkan version string.
+ """
+ return 'VK_API' + version[2:]
+
+
def is_function_supported(cmd):
"""Returns true if a function is core or from a supportable extension.
@@ -327,6 +339,7 @@
return_type_dict
version_code_list
version_dict
+ promoted_inst_ext_dict
"""
registry = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..',
'external', 'vulkan-headers', 'registry', 'vk.xml')
@@ -379,6 +392,10 @@
apiversion = ''
if extension.tag == 'extension':
extname = extension.get('name')
+ if (extension.get('type') == 'instance' and
+ extension.get('promotedto') is not None):
+ promoted_inst_ext_dict[extname] = \
+ version_2_api_version(extension.get('promotedto'))
for req in extension:
if req.get('feature') is not None:
apiversion = req.get('feature')
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index a283b83..52e7bee 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -158,10 +158,7 @@
VkJsonInstance* instance,
std::string* errors);
-VkJsonDevice VkJsonGetDevice(VkInstance instance,
- VkPhysicalDevice device,
- uint32_t instanceExtensionCount,
- const char* const* instanceExtensions);
+VkJsonDevice VkJsonGetDevice(VkPhysicalDevice device);
std::string VkJsonDeviceToJson(const VkJsonDevice& device);
bool VkJsonDeviceFromJson(const std::string& json,
VkJsonDevice* device,
@@ -177,7 +174,7 @@
typedef VkJsonDevice VkJsonAllProperties;
inline VkJsonAllProperties VkJsonGetAllProperties(
VkPhysicalDevice physicalDevice) {
- return VkJsonGetDevice(VK_NULL_HANDLE, physicalDevice, 0, nullptr);
+ return VkJsonGetDevice(physicalDevice);
}
inline std::string VkJsonAllPropertiesToJson(
const VkJsonAllProperties& properties) {
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 84cfe5e..ace713b 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -28,8 +28,6 @@
#include <utility>
namespace {
-const char* kSupportedInstanceExtensions[] = {
- "VK_KHR_get_physical_device_properties2"};
bool EnumerateExtensions(const char* layer_name,
std::vector<VkExtensionProperties>* extensions) {
@@ -47,15 +45,6 @@
}
bool HasExtension(const char* extension_name,
- uint32_t count,
- const char* const* extensions) {
- return std::find_if(extensions, extensions + count,
- [extension_name](const char* extension) {
- return strcmp(extension, extension_name) == 0;
- }) != extensions + count;
-}
-
-bool HasExtension(const char* extension_name,
const std::vector<VkExtensionProperties>& extensions) {
return std::find_if(extensions.cbegin(), extensions.cend(),
[extension_name](const VkExtensionProperties& extension) {
@@ -65,27 +54,9 @@
}
} // anonymous namespace
-VkJsonDevice VkJsonGetDevice(VkInstance instance,
- VkPhysicalDevice physical_device,
- uint32_t instance_extension_count,
- const char* const* instance_extensions) {
+VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) {
VkJsonDevice device;
- PFN_vkGetPhysicalDeviceProperties2KHR vkpGetPhysicalDeviceProperties2KHR =
- nullptr;
- PFN_vkGetPhysicalDeviceFeatures2KHR vkpGetPhysicalDeviceFeatures2KHR =
- nullptr;
- if (instance != VK_NULL_HANDLE &&
- HasExtension("VK_KHR_get_physical_device_properties2",
- instance_extension_count, instance_extensions)) {
- vkpGetPhysicalDeviceProperties2KHR =
- reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2KHR>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2KHR"));
- vkpGetPhysicalDeviceFeatures2KHR =
- reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2KHR"));
- }
-
uint32_t extension_count = 0;
vkEnumerateDeviceExtensionProperties(physical_device, nullptr,
&extension_count, nullptr);
@@ -103,55 +74,48 @@
device.layers.data());
}
- if (HasExtension("VK_KHR_get_physical_device_properties2",
- instance_extension_count, instance_extensions)) {
- VkPhysicalDeviceProperties2KHR properties = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
- nullptr,
- {} // properties
- };
- if (HasExtension("VK_KHR_driver_properties", device.extensions)) {
- device.ext_driver_properties.reported = true;
- device.ext_driver_properties.driver_properties_khr.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
- device.ext_driver_properties.driver_properties_khr.pNext =
- properties.pNext;
- properties.pNext =
- &device.ext_driver_properties.driver_properties_khr;
- }
- vkpGetPhysicalDeviceProperties2KHR(physical_device, &properties);
- device.properties = properties.properties;
-
- VkPhysicalDeviceFeatures2KHR features = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR,
- nullptr,
- {} // features
- };
- if (HasExtension("VK_KHR_variable_pointers", device.extensions)) {
- device.ext_variable_pointer_features.reported = true;
- device.ext_variable_pointer_features.variable_pointer_features_khr.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
- device.ext_variable_pointer_features.variable_pointer_features_khr.pNext =
- features.pNext;
- features.pNext =
- &device.ext_variable_pointer_features.variable_pointer_features_khr;
- }
- if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
- device.ext_shader_float16_int8_features.reported = true;
- device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
- .sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
- device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
- .pNext = features.pNext;
- features.pNext = &device.ext_shader_float16_int8_features
- .shader_float16_int8_features_khr;
- }
- vkpGetPhysicalDeviceFeatures2KHR(physical_device, &features);
- device.features = features.features;
- } else {
- vkGetPhysicalDeviceProperties(physical_device, &device.properties);
- vkGetPhysicalDeviceFeatures(physical_device, &device.features);
+ VkPhysicalDeviceProperties2 properties = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+ nullptr,
+ {},
+ };
+ if (HasExtension("VK_KHR_driver_properties", device.extensions)) {
+ device.ext_driver_properties.reported = true;
+ device.ext_driver_properties.driver_properties_khr.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
+ device.ext_driver_properties.driver_properties_khr.pNext = properties.pNext;
+ properties.pNext = &device.ext_driver_properties.driver_properties_khr;
}
+ vkGetPhysicalDeviceProperties2(physical_device, &properties);
+ device.properties = properties.properties;
+
+ VkPhysicalDeviceFeatures2 features = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+ nullptr,
+ {},
+ };
+ if (HasExtension("VK_KHR_variable_pointers", device.extensions)) {
+ device.ext_variable_pointer_features.reported = true;
+ device.ext_variable_pointer_features.variable_pointer_features_khr.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
+ device.ext_variable_pointer_features.variable_pointer_features_khr.pNext =
+ features.pNext;
+ features.pNext =
+ &device.ext_variable_pointer_features.variable_pointer_features_khr;
+ }
+ if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
+ device.ext_shader_float16_int8_features.reported = true;
+ device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+ .sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
+ device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+ .pNext = features.pNext;
+ features.pNext = &device.ext_shader_float16_int8_features
+ .shader_float16_int8_features_khr;
+ }
+ vkGetPhysicalDeviceFeatures2(physical_device, &features);
+ device.features = features.features;
+
vkGetPhysicalDeviceMemoryProperties(physical_device, &device.memory);
uint32_t queue_family_count = 0;
@@ -189,135 +153,117 @@
}
}
- PFN_vkGetPhysicalDeviceProperties2 vkpGetPhysicalDeviceProperties2 =
- reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2"));
- if (vkpGetPhysicalDeviceProperties2) {
- VkPhysicalDeviceProperties2 properties2 = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, nullptr, {}};
+ VkPhysicalDeviceProperties2 properties2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+ nullptr,
+ {},
+ };
- device.subgroup_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
- device.subgroup_properties.pNext = properties2.pNext;
- properties2.pNext = &device.subgroup_properties;
+ device.subgroup_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
+ device.subgroup_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.subgroup_properties;
- device.point_clipping_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES;
- device.point_clipping_properties.pNext = properties2.pNext;
- properties2.pNext = &device.point_clipping_properties;
+ device.point_clipping_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES;
+ device.point_clipping_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.point_clipping_properties;
- device.multiview_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
- device.multiview_properties.pNext = properties2.pNext;
- properties2.pNext = &device.multiview_properties;
+ device.multiview_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
+ device.multiview_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.multiview_properties;
- device.id_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
- device.id_properties.pNext = properties2.pNext;
- properties2.pNext = &device.id_properties;
+ device.id_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
+ device.id_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.id_properties;
- device.maintenance3_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
- device.maintenance3_properties.pNext = properties2.pNext;
- properties2.pNext = &device.maintenance3_properties;
+ device.maintenance3_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
+ device.maintenance3_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.maintenance3_properties;
- (*vkpGetPhysicalDeviceProperties2)(physical_device, &properties2);
- }
+ vkGetPhysicalDeviceProperties2(physical_device, &properties2);
- PFN_vkGetPhysicalDeviceFeatures2 vkpGetPhysicalDeviceFeatures2 =
- reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2"));
- if (vkpGetPhysicalDeviceFeatures2) {
- VkPhysicalDeviceFeatures2 features2 = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, nullptr, {}};
+ VkPhysicalDeviceFeatures2 features2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+ nullptr,
+ {},
+ };
- device.bit16_storage_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
- device.bit16_storage_features.pNext = features2.pNext;
- features2.pNext = &device.bit16_storage_features;
+ device.bit16_storage_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
+ device.bit16_storage_features.pNext = features2.pNext;
+ features2.pNext = &device.bit16_storage_features;
- device.multiview_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
- device.multiview_features.pNext = features2.pNext;
- features2.pNext = &device.multiview_features;
+ device.multiview_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
+ device.multiview_features.pNext = features2.pNext;
+ features2.pNext = &device.multiview_features;
- device.variable_pointer_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
- device.variable_pointer_features.pNext = features2.pNext;
- features2.pNext = &device.variable_pointer_features;
+ device.variable_pointer_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
+ device.variable_pointer_features.pNext = features2.pNext;
+ features2.pNext = &device.variable_pointer_features;
- device.protected_memory_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
- device.protected_memory_features.pNext = features2.pNext;
- features2.pNext = &device.protected_memory_features;
+ device.protected_memory_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
+ device.protected_memory_features.pNext = features2.pNext;
+ features2.pNext = &device.protected_memory_features;
- device.sampler_ycbcr_conversion_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
- device.sampler_ycbcr_conversion_features.pNext = features2.pNext;
- features2.pNext = &device.sampler_ycbcr_conversion_features;
+ device.sampler_ycbcr_conversion_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
+ device.sampler_ycbcr_conversion_features.pNext = features2.pNext;
+ features2.pNext = &device.sampler_ycbcr_conversion_features;
- device.shader_draw_parameter_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
- device.shader_draw_parameter_features.pNext = features2.pNext;
- features2.pNext = &device.shader_draw_parameter_features;
+ device.shader_draw_parameter_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
+ device.shader_draw_parameter_features.pNext = features2.pNext;
+ features2.pNext = &device.shader_draw_parameter_features;
- (*vkpGetPhysicalDeviceFeatures2)(physical_device, &features2);
- }
+ vkGetPhysicalDeviceFeatures2(physical_device, &features2);
- PFN_vkGetPhysicalDeviceExternalFenceProperties
- vkpGetPhysicalDeviceExternalFenceProperties =
- reinterpret_cast<PFN_vkGetPhysicalDeviceExternalFenceProperties>(
- vkGetInstanceProcAddr(
- instance, "vkGetPhysicalDeviceExternalFenceProperties"));
- if (vkpGetPhysicalDeviceExternalFenceProperties) {
- VkPhysicalDeviceExternalFenceInfo external_fence_info = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr,
- VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT};
- VkExternalFenceProperties external_fence_properties = {};
+ VkPhysicalDeviceExternalFenceInfo external_fence_info = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr,
+ VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT};
+ VkExternalFenceProperties external_fence_properties = {};
- for (VkExternalFenceHandleTypeFlagBits handle_type =
- VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
- handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
- handle_type = static_cast<VkExternalFenceHandleTypeFlagBits>(
- handle_type << 1)) {
- external_fence_info.handleType = handle_type;
- (*vkpGetPhysicalDeviceExternalFenceProperties)(
- physical_device, &external_fence_info, &external_fence_properties);
- if (external_fence_properties.exportFromImportedHandleTypes ||
- external_fence_properties.compatibleHandleTypes ||
- external_fence_properties.externalFenceFeatures) {
- device.external_fence_properties.insert(
- std::make_pair(handle_type, external_fence_properties));
- }
+ for (VkExternalFenceHandleTypeFlagBits handle_type =
+ VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
+ handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
+ handle_type =
+ static_cast<VkExternalFenceHandleTypeFlagBits>(handle_type << 1)) {
+ external_fence_info.handleType = handle_type;
+ vkGetPhysicalDeviceExternalFenceProperties(
+ physical_device, &external_fence_info, &external_fence_properties);
+ if (external_fence_properties.exportFromImportedHandleTypes ||
+ external_fence_properties.compatibleHandleTypes ||
+ external_fence_properties.externalFenceFeatures) {
+ device.external_fence_properties.insert(
+ std::make_pair(handle_type, external_fence_properties));
}
}
- PFN_vkGetPhysicalDeviceExternalSemaphoreProperties
- vkpGetPhysicalDeviceExternalSemaphoreProperties = reinterpret_cast<
- PFN_vkGetPhysicalDeviceExternalSemaphoreProperties>(
- vkGetInstanceProcAddr(
- instance, "vkGetPhysicalDeviceExternalSemaphoreProperties"));
- if (vkpGetPhysicalDeviceExternalSemaphoreProperties) {
- VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr,
- VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
- VkExternalSemaphoreProperties external_semaphore_properties = {};
+ VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr,
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
+ VkExternalSemaphoreProperties external_semaphore_properties = {};
- for (VkExternalSemaphoreHandleTypeFlagBits handle_type =
- VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
- handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
- handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>(
- handle_type << 1)) {
- external_semaphore_info.handleType = handle_type;
- (*vkpGetPhysicalDeviceExternalSemaphoreProperties)(
- physical_device, &external_semaphore_info,
- &external_semaphore_properties);
- if (external_semaphore_properties.exportFromImportedHandleTypes ||
- external_semaphore_properties.compatibleHandleTypes ||
- external_semaphore_properties.externalSemaphoreFeatures) {
- device.external_semaphore_properties.insert(
- std::make_pair(handle_type, external_semaphore_properties));
- }
+ for (VkExternalSemaphoreHandleTypeFlagBits handle_type =
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
+ handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+ handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>(
+ handle_type << 1)) {
+ external_semaphore_info.handleType = handle_type;
+ vkGetPhysicalDeviceExternalSemaphoreProperties(
+ physical_device, &external_semaphore_info,
+ &external_semaphore_properties);
+ if (external_semaphore_properties.exportFromImportedHandleTypes ||
+ external_semaphore_properties.compatibleHandleTypes ||
+ external_semaphore_properties.externalSemaphoreFeatures) {
+ device.external_semaphore_properties.insert(
+ std::make_pair(handle_type, external_semaphore_properties));
}
}
}
@@ -351,19 +297,15 @@
if (!EnumerateExtensions(nullptr, &instance.extensions))
return VkJsonInstance();
- std::vector<const char*> instance_extensions;
- for (const auto extension : kSupportedInstanceExtensions) {
- if (HasExtension(extension, instance.extensions))
- instance_extensions.push_back(extension);
- }
-
- const VkApplicationInfo app_info = {VK_STRUCTURE_TYPE_APPLICATION_INFO,
- nullptr,
- "vkjson_info",
- 1,
- "",
- 0,
- VK_API_VERSION_1_1};
+ const VkApplicationInfo app_info = {
+ VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ nullptr,
+ "vkjson_info",
+ 1,
+ "",
+ 0,
+ VK_API_VERSION_1_1,
+ };
VkInstanceCreateInfo instance_info = {
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
nullptr,
@@ -371,8 +313,9 @@
&app_info,
0,
nullptr,
- static_cast<uint32_t>(instance_extensions.size()),
- instance_extensions.data()};
+ 0,
+ nullptr,
+ };
VkInstance vkinstance;
result = vkCreateInstance(&instance_info, nullptr, &vkinstance);
if (result != VK_SUCCESS)
@@ -397,52 +340,38 @@
instance.devices.reserve(sz);
for (uint32_t i = 0; i < sz; ++i) {
device_map.insert(std::make_pair(devices[i], i));
- instance.devices.emplace_back(VkJsonGetDevice(vkinstance, devices[i],
- instance_extensions.size(),
- instance_extensions.data()));
+ instance.devices.emplace_back(VkJsonGetDevice(devices[i]));
}
- PFN_vkEnumerateInstanceVersion vkpEnumerateInstanceVersion =
- reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
- vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"));
- if (!vkpEnumerateInstanceVersion) {
- instance.api_version = VK_API_VERSION_1_0;
- } else {
- result = (*vkpEnumerateInstanceVersion)(&instance.api_version);
- if (result != VK_SUCCESS) {
- vkDestroyInstance(vkinstance, nullptr);
- return VkJsonInstance();
- }
+ result = vkEnumerateInstanceVersion(&instance.api_version);
+ if (result != VK_SUCCESS) {
+ vkDestroyInstance(vkinstance, nullptr);
+ return VkJsonInstance();
}
- PFN_vkEnumeratePhysicalDeviceGroups vkpEnumeratePhysicalDeviceGroups =
- reinterpret_cast<PFN_vkEnumeratePhysicalDeviceGroups>(
- vkGetInstanceProcAddr(vkinstance, "vkEnumeratePhysicalDeviceGroups"));
- if (vkpEnumeratePhysicalDeviceGroups) {
- count = 0;
- result = (*vkpEnumeratePhysicalDeviceGroups)(vkinstance, &count, nullptr);
- if (result != VK_SUCCESS) {
- vkDestroyInstance(vkinstance, nullptr);
- return VkJsonInstance();
- }
+ count = 0;
+ result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count, nullptr);
+ if (result != VK_SUCCESS) {
+ vkDestroyInstance(vkinstance, nullptr);
+ return VkJsonInstance();
+ }
- VkJsonDeviceGroup device_group;
- std::vector<VkPhysicalDeviceGroupProperties> group_properties;
- group_properties.resize(count);
- result = (*vkpEnumeratePhysicalDeviceGroups)(vkinstance, &count,
- group_properties.data());
- if (result != VK_SUCCESS) {
- vkDestroyInstance(vkinstance, nullptr);
- return VkJsonInstance();
+ VkJsonDeviceGroup device_group;
+ std::vector<VkPhysicalDeviceGroupProperties> group_properties;
+ group_properties.resize(count);
+ result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count,
+ group_properties.data());
+ if (result != VK_SUCCESS) {
+ vkDestroyInstance(vkinstance, nullptr);
+ return VkJsonInstance();
+ }
+ for (auto properties : group_properties) {
+ device_group.properties = properties;
+ for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) {
+ device_group.device_inds.push_back(
+ device_map[properties.physicalDevices[i]]);
}
- for (auto properties : group_properties) {
- device_group.properties = properties;
- for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) {
- device_group.device_inds.push_back(
- device_map[properties.physicalDevices[i]]);
- }
- instance.device_groups.push_back(device_group);
- }
+ instance.device_groups.push_back(device_group);
}
vkDestroyInstance(vkinstance, nullptr);