Merge "DO NOT MERGE Correct physical orientation"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 0473bb8..9cab9b4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,6 +8,7 @@
include/input/
libs/binder/fuzzer/
libs/binder/ndk/
+ libs/binder/tests/fuzzers/
libs/binderthreadstate/
libs/graphicsenv/
libs/gui/
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index ead491e..80d14ac 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -105,6 +105,8 @@
name: "dumpstate",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"main.cpp",
],
@@ -132,6 +134,8 @@
name: "dumpstate_test",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"tests/dumpstate_test.cpp",
],
@@ -148,10 +152,14 @@
name: "dumpstate_smoke_test",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
+ "TaskQueue.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..e15ac3f
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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::deleteTempFiles() {
+ deleteTempFiles(tmp_root_);
+}
+
+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..0c3c2cc
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.h
@@ -0,0 +1,206 @@
+/*
+ * 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);
+
+ /*
+ * Deletes temporary files created by DumpPool.
+ */
+ void deleteTempFiles();
+
+ 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/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index bbc724c..3091f6b 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -69,7 +69,7 @@
static const std::vector<std::string> group_names{
"log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats",
- "readproc", "bluetooth", "wakelock"};
+ "readproc", "bluetooth", "wakelock", "nfc"};
std::vector<gid_t> groups(group_names.size(), 0);
for (size_t i = 0; i < group_names.size(); ++i) {
grp = getgrnam(group_names[i].c_str());
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/TaskQueue.cpp b/cmds/dumpstate/TaskQueue.cpp
new file mode 100644
index 0000000..8550aec
--- /dev/null
+++ b/cmds/dumpstate/TaskQueue.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TaskQueue.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+TaskQueue::~TaskQueue() {
+ run(/* do_cancel = */true);
+}
+
+void TaskQueue::run(bool do_cancel) {
+ std::unique_lock lock(lock_);
+ while (!tasks_.empty()) {
+ auto task = tasks_.front();
+ tasks_.pop();
+ lock.unlock();
+ std::invoke(task, do_cancel);
+ lock.lock();
+ }
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/TaskQueue.h b/cmds/dumpstate/TaskQueue.h
new file mode 100644
index 0000000..b7e72f1
--- /dev/null
+++ b/cmds/dumpstate/TaskQueue.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
+#define FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
+
+#include <mutex>
+#include <queue>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+/*
+ * A task queue for dumpstate to collect tasks such as adding file to the zip
+ * which are needed to run in a single thread. The task is a callable function
+ * included a cancel task boolean parameter. The TaskQueue could
+ * cancel the task in the destructor if the task has never been called.
+ */
+class TaskQueue {
+ public:
+ TaskQueue() = default;
+ ~TaskQueue();
+
+ /*
+ * Adds a task into the queue.
+ *
+ * |f| Callable function to execute the task. The function must include a
+ * boolean parameter for TaskQueue to notify whether the task is
+ * cancelled or not.
+ * |args| A list of arguments.
+ */
+ template<class F, class... Args> void add(F&& f, Args&&... args) {
+ auto func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
+ std::unique_lock lock(lock_);
+ tasks_.emplace([=](bool cancelled) {
+ std::invoke(func, cancelled);
+ });
+ }
+
+ /*
+ * Invokes all tasks in the task queue.
+ *
+ * |do_cancel| true to cancel all tasks in the queue.
+ */
+ void run(bool do_cancel);
+
+ private:
+ using Task = std::function<void(bool)>;
+
+ std::mutex lock_;
+ std::queue<Task> tasks_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueue);
+};
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+#endif //FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index edad3fd..eefc84f 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,7 +114,9 @@
using android::os::IDumpstateListener;
using android::os::dumpstate::CommandOptions;
using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::DumpPool;
using android::os::dumpstate::PropertiesHelper;
+using android::os::dumpstate::TaskQueue;
// Keep in sync with
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -126,8 +129,8 @@
static Dumpstate& ds = Dumpstate::GetInstance();
static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options = CommandOptions::DEFAULT,
- bool verbose_duration = false) {
- return ds.RunCommand(title, full_command, options, verbose_duration);
+ bool verbose_duration = false, int out_fd = STDOUT_FILENO) {
+ return ds.RunCommand(title, full_command, options, verbose_duration, out_fd);
}
// Reasonable value for max stats.
@@ -196,8 +199,36 @@
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();
+
+#define WAIT_TASK_WITH_CONSENT_CHECK(task_name, pool_ptr) \
+ RETURN_IF_USER_DENIED_CONSENT(); \
+ pool_ptr->waitForTask(task_name); \
+ RETURN_IF_USER_DENIED_CONSENT();
+
static const char* WAKE_LOCK_NAME = "dumpstate_wakelock";
+// Names of parallel tasks, they are used for the DumpPool to identify the dump
+// task and the log title of the duration report.
+static const std::string DUMP_TRACES_TASK = "DUMP TRACES";
+static const std::string DUMP_INCIDENT_REPORT_TASK = "INCIDENT REPORT";
+static const std::string DUMP_HALS_TASK = "DUMP HALS";
+static const std::string DUMP_BOARD_TASK = "dumpstate_board()";
+static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
+static const std::string DUMP_APP_INFOS_TASK = "DUMP APP INFOS";
+
namespace android {
namespace os {
namespace {
@@ -306,8 +337,12 @@
static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,
- long dumpsysTimeoutMs = 0) {
- return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs);
+ long dumpsysTimeoutMs = 0, int out_fd = STDOUT_FILENO) {
+ return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs, out_fd);
+}
+static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+ int out_fd) {
+ return ds.RunDumpsys(title, dumpsysArgs, Dumpstate::DEFAULT_DUMPSYS, 0, out_fd);
}
static int DumpFile(const std::string& title, const std::string& path) {
return ds.DumpFile(title, path);
@@ -762,8 +797,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");
}
@@ -993,7 +1029,6 @@
MYLOGD("Not dumping incident report because it's not a zipped bugreport\n");
return;
}
- DurationReporter duration_reporter("INCIDENT REPORT");
const std::string path = ds.bugreport_internal_dir_ + "/tmp_incident_report";
auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(),
O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
@@ -1008,9 +1043,11 @@
// Use a different name from "incident.proto"
// /proto/incident.proto is reserved for incident service dump
// i.e. metadata for debugging.
- ds.AddZipEntry(kProtoPath + "incident_report" + kProtoExt, path);
+ ds.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "incident_report" + kProtoExt,
+ path);
+ } else {
+ unlink(path.c_str());
}
- unlink(path.c_str());
}
static void DumpVisibleWindowViews() {
@@ -1305,15 +1342,20 @@
/* timeout= */ 90s, /* service_timeout= */ 10s);
}
-static void DumpHals() {
+/*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpHals(int out_fd = STDOUT_FILENO) {
if (!ds.IsZipping()) {
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
CommandOptions::WithTimeout(60).AsRootIfAvailable().Build());
return;
}
- DurationReporter duration_reporter("DUMP HALS");
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
- CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+ CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(),
+ false, out_fd);
using android::hidl::manager::V1_0::IServiceManager;
using android::hardware::defaultServiceManager;
@@ -1335,6 +1377,7 @@
}, '_');
const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName;
+ bool empty = false;
{
auto fd = android::base::unique_fd(
TEMP_FAILURE_RETRY(open(path.c_str(),
@@ -1349,13 +1392,14 @@
{"lshal", "debug", "-E", interface},
CommandOptions::WithTimeout(2).AsRootIfAvailable().Build());
- bool empty = 0 == lseek(fd, 0, SEEK_END);
- if (!empty) {
- ds.AddZipEntry("lshal-debug/" + cleanName + ".txt", path);
- }
+ empty = 0 == lseek(fd, 0, SEEK_END);
}
-
- unlink(path.c_str());
+ if (!empty) {
+ ds.EnqueueAddZipEntryAndCleanupIfNeeded("lshal-debug/" + cleanName + ".txt",
+ path);
+ } else {
+ unlink(path.c_str());
+ }
}
});
@@ -1443,6 +1487,73 @@
printf("========================================================\n");
}
+/*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpCheckins(int out_fd = STDOUT_FILENO) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Checkins\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}, out_fd);
+ RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}, out_fd);
+ RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"}, out_fd);
+}
+
+/*
+ * Runs dumpsys on activity service to dump all application activities, services
+ * and providers in the device.
+ *
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpAppInfos(int out_fd = STDOUT_FILENO) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Activities\n");
+ dprintf(out_fd, "========================================================\n");
+
+ // The following dumpsys internally collects output from running apps, so it can take a long
+ // time. So let's extend the timeout.
+
+ const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
+
+ RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Services (platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Services (non-platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Providers (platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Providers (non-platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+}
+
// Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent
// via the consent they are shown. Ignores other errors that occur while running various
// commands. The consent checking is currently done around long running tasks, which happen to
@@ -1450,6 +1561,19 @@
static Dumpstate::RunStatus dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
+ // Enqueue slow functions into the thread pool, if the parallel run is enabled.
+ if (ds.dump_pool_) {
+ // Pool was shutdown in DumpstateDefaultAfterCritical method in order to
+ // drop root user. Restarts it with two threads for the parallel run.
+ ds.dump_pool_->start(/* thread_counts = */2);
+
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+ ds.dump_pool_->enqueueTask(DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_APP_INFOS_TASK, &DumpAppInfos, _1);
+ }
+
// Dump various things. Note that anything that takes "long" (i.e. several seconds) should
// check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped
// in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK).
@@ -1481,7 +1605,11 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"},
CommandOptions::AS_ROOT);
- DumpHals();
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_HALS_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_HALS_TASK, DumpHals);
+ }
RunCommand("PRINTENV", {"printenv"});
RunCommand("NETSTAT", {"netstat", "-nW"});
@@ -1507,6 +1635,8 @@
/* Dump Bluetooth HCI logs */
ds.AddDir("/data/misc/bluetooth/logs", true);
+ /* Dump Nfc NCI logs */
+ ds.AddDir("/data/misc/nfc/logs", true);
if (ds.options_->do_screenshot && !ds.do_early_screenshot_) {
MYLOGI("taking late screenshot\n");
@@ -1562,7 +1692,11 @@
ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard);
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_BOARD_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
+ }
/* Migrate the ril_dumpstate to a device specific dumpstate? */
int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
@@ -1584,57 +1718,17 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal);
- printf("========================================================\n");
- printf("== Checkins\n");
- printf("========================================================\n");
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins);
+ }
- RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
-
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"});
-
- RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
- RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
- RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
- RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
-
- printf("========================================================\n");
- printf("== Running Application Activities\n");
- printf("========================================================\n");
-
- // The following dumpsys internally collects output from running apps, so it can take a long
- // time. So let's extend the timeout.
-
- const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
-
- RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Services (platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Services (non-platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Providers (platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Providers (non-platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_APP_INFOS_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_APP_INFOS_TASK, DumpAppInfos);
+ }
printf("========================================================\n");
printf("== Dropbox crashes\n");
@@ -1659,7 +1753,12 @@
// Add linker configuration directory
ds.AddDir(LINKERCONFIG_DIR, true);
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport);
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_INCIDENT_REPORT_TASK,
+ DumpIncidentReport);
+ }
return Dumpstate::RunStatus::OK;
}
@@ -1680,7 +1779,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 +1833,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;
}
@@ -1734,31 +1853,39 @@
return status;
}
+// Common states for telephony and wifi which are needed to be collected before
+// dumpstate drop the root user.
+static void DumpstateRadioAsRoot() {
+ DumpIpTablesAsRoot();
+ ds.AddDir(LOGPERSIST_DATA_DIR, false);
+}
+
// This method collects common dumpsys for telephony and wifi. Typically, wifi
// reports are fine to include all information, but telephony reports on user
// builds need to strip some content (see DumpstateTelephonyOnly).
static void DumpstateRadioCommon(bool include_sensitive_info = true) {
- DumpIpTablesAsRoot();
-
- ds.AddDir(LOGPERSIST_DATA_DIR, false);
-
- if (!DropRootUser()) {
- return;
- }
-
// We need to be picky about some stuff for telephony reports on user builds.
if (!include_sensitive_info) {
// Only dump the radio log buffer (other buffers and dumps contain too much unrelated info).
DoRadioLogcat();
} else {
+ // DumpHals takes long time, post it to the another thread in the pool,
+ // if pool is available.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+ }
// Contains various system properties and process startup info.
do_dmesg();
// Logs other than the radio buffer may contain package/component names and potential PII.
DoLogcat();
// Too broad for connectivity problems.
DoKmsg();
- // Contains unrelated hardware info (camera, NFC, biometrics, ...).
- DumpHals();
+ // DumpHals contains unrelated hardware info (camera, NFC, biometrics, ...).
+ if (ds.dump_pool_) {
+ ds.dump_pool_->waitForTask(DUMP_HALS_TASK);
+ } else {
+ RUN_SLOW_FUNCTION_AND_LOG(DUMP_HALS_TASK, DumpHals);
+ }
}
DumpPacketStats();
@@ -1782,6 +1909,21 @@
const bool include_sensitive_info = !PropertiesHelper::IsUserBuild();
+ DumpstateRadioAsRoot();
+ if (!DropRootUser()) {
+ return;
+ }
+
+ // Starts thread pool after the root user is dropped, and two additional threads
+ // are created for DumpHals in the DumpstateRadioCommon and DumpstateBoard.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->start(/*thread_counts =*/2);
+
+ // DumpstateBoard takes long time, post it to the another thread in the pool,
+ // if pool is available.
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+ }
+
DumpstateRadioCommon(include_sensitive_info);
if (include_sensitive_info) {
@@ -1858,12 +2000,29 @@
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
+
+ if (ds.dump_pool_) {
+ ds.dump_pool_->waitForTask(DUMP_BOARD_TASK);
+ } else {
+ RUN_SLOW_FUNCTION_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
+ }
}
// This method collects dumpsys for wifi debugging only
static void DumpstateWifiOnly() {
DurationReporter duration_reporter("DUMPSTATE");
+ DumpstateRadioAsRoot();
+ if (!DropRootUser()) {
+ return;
+ }
+
+ // Starts thread pool after the root user is dropped. Only one additional
+ // thread is needed for DumpHals in the DumpstateRadioCommon.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->start(/*thread_counts =*/1);
+ }
+
DumpstateRadioCommon();
printf("========================================================\n");
@@ -1881,8 +2040,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]);
@@ -1990,11 +2147,10 @@
return RunStatus::OK;
}
-void Dumpstate::DumpstateBoard() {
- DurationReporter duration_reporter("dumpstate_board()");
- printf("========================================================\n");
- printf("== Board\n");
- printf("========================================================\n");
+void Dumpstate::DumpstateBoard(int out_fd) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Board\n");
+ dprintf(out_fd, "========================================================\n");
if (!IsZipping()) {
MYLOGD("Not dumping board info because it's not a zipped bugreport\n");
@@ -2120,8 +2276,9 @@
MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str());
continue;
}
- AddZipEntry(kDumpstateBoardFiles[i], paths[i]);
- printf("*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
+ remover[i].Disable();
+ EnqueueAddZipEntryAndCleanupIfNeeded(kDumpstateBoardFiles[i], paths[i]);
+ dprintf(out_fd, "*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
}
}
@@ -2151,6 +2308,11 @@
}
bool Dumpstate::FinishZipFile() {
+ // Runs all enqueued adding zip entry and cleanup tasks before finishing the zip file.
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/* do_cancel = */false);
+ }
+
std::string entry_name = base_name_ + "-" + name_ + ".txt";
MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
tmp_path_.c_str());
@@ -2354,7 +2516,6 @@
break;
case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
// Currently, the dumpstate binder is only used by Shell to update progress.
- options->do_start_service = true;
options->do_progress_updates = true;
options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
@@ -2366,7 +2527,6 @@
options->dumpstate_hal_mode = DumpstateMode::REMOTE;
break;
case Dumpstate::BugreportMode::BUGREPORT_WEAR:
- options->do_start_service = true;
options->do_progress_updates = true;
options->do_zip_file = true;
options->do_screenshot = is_screenshot_requested;
@@ -2393,12 +2553,12 @@
static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
MYLOGI(
"do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
- "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
+ "is_remote_mode: %d show_header_only: %d telephony_only: %d "
"wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
"limited_only: %d args: %s\n",
options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
options.do_screenshot, options.is_remote_mode, options.show_header_only,
- options.do_start_service, options.telephony_only, options.wifi_only,
+ options.telephony_only, options.wifi_only,
options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(),
toString(options.dumpstate_hal_mode).c_str(), options.limited_only, options.args.c_str());
}
@@ -2529,6 +2689,15 @@
}
tombstone_data_.clear();
anr_data_.clear();
+
+ // Instead of shutdown the pool, we delete temporary files directly since
+ // shutdown blocking the call.
+ if (dump_pool_) {
+ dump_pool_->deleteTempFiles();
+ }
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/*do_cancel =*/ true);
+ }
}
/*
@@ -2554,6 +2723,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");
@@ -2613,15 +2783,6 @@
register_sig_handler();
- // TODO(b/111441001): maybe skip if already started?
- if (options_->do_start_service) {
- MYLOGI("Starting 'dumpstate' service\n");
- android::status_t ret;
- if ((ret = android::os::DumpstateService::Start()) != android::OK) {
- MYLOGE("Unable to start DumpstateService: %d\n", ret);
- }
- }
-
if (PropertiesHelper::IsDryRun()) {
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
@@ -2715,6 +2876,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.
@@ -2726,7 +2894,6 @@
onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateTelephonyOnly(calling_package);
- DumpstateBoard();
} else if (options_->wifi_only) {
MaybeTakeEarlyScreenshot();
onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
@@ -2881,6 +3048,45 @@
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_);
+ zip_entry_tasks_ = std::make_unique<TaskQueue>();
+}
+
+void Dumpstate::ShutdownDumpPool() {
+ if (dump_pool_) {
+ dump_pool_->shutdown();
+ dump_pool_ = nullptr;
+ }
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/* do_cancel = */true);
+ zip_entry_tasks_ = nullptr;
+ }
+}
+
+void Dumpstate::EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name,
+ const std::string& entry_path) {
+ auto func_add_zip_entry_and_cleanup = [=](bool task_cancelled) {
+ if (!task_cancelled) {
+ AddZipEntry(entry_name, entry_path);
+ }
+ android::os::UnlinkAndLogOnError(entry_path);
+ };
+ if (zip_entry_tasks_) {
+ // Enqueues AddZipEntryAndCleanup function if the parallel run is enabled.
+ zip_entry_tasks_->add(func_add_zip_entry_and_cleanup, _1);
+ } else {
+ // Invokes AddZipEntryAndCleanup immediately
+ std::invoke(func_add_zip_entry_and_cleanup, /* task_cancelled = */false);
+ }
+}
+
Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
MYLOGD("User denied consent; deleting files and returning\n");
CleanupTmpFiles();
@@ -2999,8 +3205,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 +3221,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());
}
}
}
@@ -3600,10 +3808,11 @@
}
int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
- const CommandOptions& options, bool verbose_duration) {
- DurationReporter duration_reporter(title, false /* logcat_only */, verbose_duration);
+ const CommandOptions& options, bool verbose_duration, int out_fd) {
+ DurationReporter duration_reporter(title, false /* logcat_only */,
+ verbose_duration, out_fd);
- int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options);
+ int status = RunCommandToFd(out_fd, title, full_command, options);
/* TODO: for now we're simplifying the progress calculation by using the
* timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
@@ -3615,11 +3824,11 @@
}
void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
- const CommandOptions& options, long dumpsysTimeoutMs) {
+ const CommandOptions& options, long dumpsysTimeoutMs, int out_fd) {
long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs();
std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)};
dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());
- RunCommand(title, dumpsys, options);
+ RunCommand(title, dumpsys, options, false, out_fd);
}
int open_socket(const char *service) {
@@ -3742,12 +3951,16 @@
fclose(fp);
}
-// TODO: make this function thread safe if sections are generated in parallel.
void Dumpstate::UpdateProgress(int32_t delta_sec) {
if (progress_ == nullptr) {
MYLOGE("UpdateProgress: progress_ not set\n");
return;
}
+ // This function updates progress related members of the dumpstate and reports
+ // progress percentage to the bugreport client. Since it could be called by
+ // different dump tasks at the same time if the parallel run is enabled, a
+ // mutex lock is necessary here to synchronize the call.
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
// Always update progess so stats can be tuned...
progress_->Inc(delta_sec);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0d25d30..9582c9d 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -35,6 +35,8 @@
#include <ziparchive/zip_writer.h>
#include "DumpstateUtil.h"
+#include "DumpPool.h"
+#include "TaskQueue.h"
// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
// std::vector<std::string>
@@ -75,7 +77,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 +86,7 @@
bool logcat_only_;
bool verbose_;
uint64_t started_;
+ int duration_fd_;
DISALLOW_COPY_AND_ASSIGN(DurationReporter);
};
@@ -193,7 +196,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 };
@@ -227,11 +230,13 @@
* |full_command| array containing the command (first entry) and its arguments.
* Must contain at least one element.
* |options| optional argument defining the command's behavior.
+ * |out_fd| A fd to support the DumpPool to output results to a temporary
+ * file. Using STDOUT_FILENO if it's not running in the parallel task.
*/
int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
const android::os::dumpstate::CommandOptions& options =
android::os::dumpstate::CommandOptions::DEFAULT,
- bool verbose_duration = false);
+ bool verbose_duration = false, int out_fd = STDOUT_FILENO);
/*
* Runs `dumpsys` with the given arguments, automatically setting its timeout
@@ -244,10 +249,12 @@
* |options| optional argument defining the command's behavior.
* |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -T` (otherwise it uses the
* timeout from `options`)
+ * |out_fd| A fd to support the DumpPool to output results to a temporary
+ * file. Using STDOUT_FILENO if it's not running in the parallel task.
*/
void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS,
- long dumpsys_timeout_ms = 0);
+ long dumpsys_timeout_ms = 0, int out_fd = STDOUT_FILENO);
/*
* Prints the contents of a file.
@@ -304,7 +311,12 @@
// Returns OK in all other cases.
RunStatus DumpTraces(const char** path);
- void DumpstateBoard();
+ /*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+ void DumpstateBoard(int out_fd = STDOUT_FILENO);
/*
* Updates the overall progress of the bugreport generation by the given weight increment.
@@ -361,6 +373,18 @@
bool CalledByApi() const;
/*
+ * Enqueues a task to the dumpstate's TaskQueue if the parallel run is enabled,
+ * otherwise invokes it immediately. The task adds file at path entry_path
+ * as a zip file entry with name entry_name. Unlinks entry_path when done.
+ *
+ * All enqueued tasks will be executed in the dumpstate's FinishZipFile method
+ * before the zip file is finished. Tasks will be cancelled in dumpstate's
+ * ShutdownDumpPool method if they have never been called.
+ */
+ void EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name,
+ const std::string& entry_path);
+
+ /*
* Structure to hold options that determine the behavior of dumpstate.
*/
struct DumpOptions {
@@ -374,7 +398,6 @@
bool is_screenshot_copied = false;
bool is_remote_mode = false;
bool show_header_only = false;
- bool do_start_service = false;
bool telephony_only = false;
bool wifi_only = false;
// Trimmed-down version of dumpstate to only include whitelisted logs.
@@ -490,6 +513,13 @@
// 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 task queue to collect adding zip entry tasks inside dump tasks if the
+ // parallel run is enabled.
+ std::unique_ptr<android::os::dumpstate::TaskQueue> zip_entry_tasks_;
+
// A callback to IncidentCompanion service, which checks user consent for sharing the
// bugreport with the calling app. If the user has not responded yet to the dialog it will
// be neither confirmed nor denied.
@@ -528,6 +558,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
@@ -539,6 +573,8 @@
android::sp<ConsentCallback> consent_callback_;
+ std::recursive_mutex mutex_;
+
DISALLOW_COPY_AND_ASSIGN(Dumpstate);
};
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..1c6583e 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, Is1MBMBinSize) {
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 */);
+ EXPECT_GE(st.st_size, 1000000 /* 1MB */);
}
-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) {
@@ -400,7 +401,7 @@
SectionExists("batterystats", /* bytes= */ 1000);
}
-TEST_F(BugreportSectionTest, WifiSectionGenerated) {
+TEST_F(BugreportSectionTest, DISABLED_WifiSectionGenerated) {
SectionExists("wifi", /* bytes= */ 100000);
}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index c7df1bb..6b93692 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;
}
@@ -252,7 +258,6 @@
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
- EXPECT_FALSE(options_.do_start_service);
EXPECT_FALSE(options_.limited_only);
}
@@ -261,7 +266,6 @@
EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
- EXPECT_TRUE(options_.do_start_service);
EXPECT_TRUE(options_.do_screenshot);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
@@ -297,7 +301,6 @@
EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
- EXPECT_TRUE(options_.do_start_service);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR);
// Other options retain default values
@@ -542,6 +545,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 +576,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 +1018,30 @@
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.zip_entry_tasks_);
+ 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.zip_entry_tasks_);
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) {
+ SetParallelRun(false);
+ EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.zip_entry_tasks_);
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
class DumpstateServiceTest : public DumpstateBaseTest {
public:
DumpstateService dss;
@@ -1618,6 +1653,154 @@
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));
+}
+
+class TaskQueueTest : public DumpstateBaseTest {
+public:
+ void SetUp() {
+ DumpstateBaseTest::SetUp();
+ }
+
+ TaskQueue task_queue_;
+};
+
+TEST_F(TaskQueueTest, runTask) {
+ bool is_task1_run = false;
+ bool is_task2_run = false;
+ auto task_1 = [&](bool task_cancelled) {
+ if (task_cancelled) {
+ return;
+ }
+ is_task1_run = true;
+ };
+ auto task_2 = [&](bool task_cancelled) {
+ if (task_cancelled) {
+ return;
+ }
+ is_task2_run = true;
+ };
+ task_queue_.add(task_1, std::placeholders::_1);
+ task_queue_.add(task_2, std::placeholders::_1);
+
+ task_queue_.run(/* do_cancel = */false);
+
+ EXPECT_TRUE(is_task1_run);
+ EXPECT_TRUE(is_task2_run);
+}
+
+TEST_F(TaskQueueTest, runTask_withCancelled) {
+ bool is_task1_cancelled = false;
+ bool is_task2_cancelled = false;
+ auto task_1 = [&](bool task_cancelled) {
+ is_task1_cancelled = task_cancelled;
+ };
+ auto task_2 = [&](bool task_cancelled) {
+ is_task2_cancelled = task_cancelled;
+ };
+ task_queue_.add(task_1, std::placeholders::_1);
+ task_queue_.add(task_2, std::placeholders::_1);
+
+ task_queue_.run(/* do_cancel = */true);
+
+ EXPECT_TRUE(is_task1_cancelled);
+ EXPECT_TRUE(is_task2_cancelled);
+}
+
+
} // namespace dumpstate
} // namespace os
} // namespace android
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index bd45005..7c9e3b2 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -41,6 +41,21 @@
"liblogwrap",
],
test_config: "installd_cache_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
@@ -66,6 +81,21 @@
"liblogwrap",
],
test_config: "installd_service_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
@@ -93,6 +123,21 @@
"libz",
],
test_config: "installd_dexopt_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index 9964888..ba6cdf1 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -479,8 +479,7 @@
}
TEST_F(ListTest, DumpVintf) {
- const std::string expected = "<manifest version=\"2.0\" type=\"device\">\n"
- " <hal format=\"hidl\">\n"
+ const std::string expected = " <hal format=\"hidl\">\n"
" <name>a.h.foo1</name>\n"
" <transport>hwbinder</transport>\n"
" <fqname>@1.0::IFoo/1</fqname>\n"
@@ -499,8 +498,7 @@
" <name>a.h.foo4</name>\n"
" <transport arch=\"32\">passthrough</transport>\n"
" <fqname>@4.0::IFoo/4</fqname>\n"
- " </hal>\n"
- "</manifest>";
+ " </hal>\n";
optind = 1; // mimic Lshal::parseArg()
EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"})));
diff --git a/include/input/Input.h b/include/input/Input.h
index 54b4e5a..af9c41d 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -86,6 +86,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. */
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
index 09a5f39..cdd7764 100644
--- a/libs/android_runtime_lazy/Android.bp
+++ b/libs/android_runtime_lazy/Android.bp
@@ -35,6 +35,11 @@
vendor_available: true,
double_loadable: true,
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
cflags: [
"-Wall",
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index d363ee9..e6071a0 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -31,6 +31,11 @@
"libutils_headers",
],
min_sdk_version: "29",
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
// These interfaces are android-specific implementation unrelated to binder
@@ -122,6 +127,9 @@
vendor: {
exclude_srcs: libbinder_device_interface_sources,
},
+ darwin: {
+ enabled: false,
+ },
},
aidl: {
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index e4ea60f..e1cbc19 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -49,7 +49,7 @@
int fd = ashmem_create_region(name == nullptr ? "MemoryHeapBase" : name, size);
ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
if (fd >= 0) {
- if (mapfd(fd, size) == NO_ERROR) {
+ if (mapfd(fd, true, size) == NO_ERROR) {
if (flags & READ_ONLY) {
ashmem_set_prot_region(fd, PROT_READ);
}
@@ -70,7 +70,7 @@
if (fd >= 0) {
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
- if (mapfd(fd, size) == NO_ERROR) {
+ if (mapfd(fd, false, size) == NO_ERROR) {
mDevice = device;
}
}
@@ -82,7 +82,7 @@
{
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
- mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
+ mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), false, size, offset);
}
status_t MemoryHeapBase::init(int fd, void *base, size_t size, int flags, const char* device)
@@ -98,7 +98,7 @@
return NO_ERROR;
}
-status_t MemoryHeapBase::mapfd(int fd, size_t size, off_t offset)
+status_t MemoryHeapBase::mapfd(int fd, bool writeableByCaller, size_t size, off_t offset)
{
if (size == 0) {
// try to figure out the size automatically
@@ -116,8 +116,12 @@
}
if ((mFlags & DONT_MAP_LOCALLY) == 0) {
+ int prot = PROT_READ;
+ if (writeableByCaller || (mFlags & READ_ONLY) == 0) {
+ prot |= PROT_WRITE;
+ }
void* base = (uint8_t*)mmap(nullptr, size,
- PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
+ prot, MAP_SHARED, fd, offset);
if (base == MAP_FAILED) {
ALOGE("mmap(fd=%d, size=%zu) failed (%s)",
fd, size, strerror(errno));
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a9c19b3..27cf97c 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -77,9 +77,8 @@
// many things compile this into prebuilts on the stack
static_assert(sizeof(Parcel) == 60 || sizeof(Parcel) == 120);
-static pthread_mutex_t gParcelGlobalAllocSizeLock = PTHREAD_MUTEX_INITIALIZER;
-static size_t gParcelGlobalAllocSize = 0;
-static size_t gParcelGlobalAllocCount = 0;
+static std::atomic<size_t> gParcelGlobalAllocCount;
+static std::atomic<size_t> gParcelGlobalAllocSize;
static size_t gMaxFds = 0;
@@ -275,17 +274,11 @@
}
size_t Parcel::getGlobalAllocSize() {
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- size_t size = gParcelGlobalAllocSize;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
- return size;
+ return gParcelGlobalAllocSize.load();
}
size_t Parcel::getGlobalAllocCount() {
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- size_t count = gParcelGlobalAllocCount;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
- return count;
+ return gParcelGlobalAllocCount.load();
}
const uint8_t* Parcel::data() const
@@ -1075,6 +1068,7 @@
{
if (str == nullptr) return writeInt32(-1);
+ // NOTE: Keep this logic in sync with android_os_Parcel.cpp
status_t err = writeInt32(len);
if (err == NO_ERROR) {
uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char));
@@ -1115,6 +1109,7 @@
{
if (str == nullptr) return writeInt32(-1);
+ // NOTE: Keep this logic in sync with android_os_Parcel.cpp
status_t err = writeInt32(len);
if (err == NO_ERROR) {
len *= sizeof(char16_t);
@@ -2630,16 +2625,8 @@
releaseObjects();
if (mData) {
LOG_ALLOC("Parcel %p: freeing with %zu capacity", this, mDataCapacity);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- if (mDataCapacity <= gParcelGlobalAllocSize) {
- gParcelGlobalAllocSize = gParcelGlobalAllocSize - mDataCapacity;
- } else {
- gParcelGlobalAllocSize = 0;
- }
- if (gParcelGlobalAllocCount > 0) {
- gParcelGlobalAllocCount--;
- }
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
+ gParcelGlobalAllocSize -= mDataCapacity;
+ gParcelGlobalAllocCount--;
free(mData);
}
if (mObjects) free(mObjects);
@@ -2685,13 +2672,15 @@
if (data) {
LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- gParcelGlobalAllocSize += desired;
- gParcelGlobalAllocSize -= mDataCapacity;
+ if (mDataCapacity > desired) {
+ gParcelGlobalAllocSize -= (mDataCapacity - desired);
+ } else {
+ gParcelGlobalAllocSize += (desired - mDataCapacity);
+ }
+
if (!mData) {
gParcelGlobalAllocCount++;
}
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataCapacity = desired;
}
@@ -2779,10 +2768,8 @@
mOwner = nullptr;
LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mObjects = objects;
@@ -2830,10 +2817,8 @@
if (data) {
LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocSize -= mDataCapacity;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataCapacity = desired;
} else {
@@ -2865,10 +2850,8 @@
}
LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataSize = mDataPos = 0;
diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp
index e9df279..b2b8671 100644
--- a/libs/binder/ParcelableHolder.cpp
+++ b/libs/binder/ParcelableHolder.cpp
@@ -27,7 +27,6 @@
namespace android {
namespace os {
status_t ParcelableHolder::writeToParcel(Parcel* p) const {
- std::lock_guard<std::mutex> l(mMutex);
RETURN_ON_FAILURE(p->writeInt32(static_cast<int32_t>(this->getStability())));
if (this->mParcelPtr) {
RETURN_ON_FAILURE(p->writeInt32(this->mParcelPtr->dataSize()));
@@ -53,7 +52,6 @@
}
status_t ParcelableHolder::readFromParcel(const Parcel* p) {
- std::lock_guard<std::mutex> l(mMutex);
this->mStability = static_cast<Stability>(p->readInt32());
this->mParcelable = nullptr;
this->mParcelableName = std::nullopt;
diff --git a/libs/binder/fuzzer/main.cpp b/libs/binder/fuzzer/main.cpp
deleted file mode 100644
index 6657edb..0000000
--- a/libs/binder/fuzzer/main.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define FUZZ_LOG_TAG "main"
-
-#include "binder.h"
-#include "binder_ndk.h"
-#include "hwbinder.h"
-#include "util.h"
-
-#include <android-base/logging.h>
-
-#include <cstdlib>
-#include <ctime>
-
-template <typename P>
-void doFuzz(
- const std::vector<ParcelRead<P>>& reads,
- const std::vector<uint8_t>& input,
- const std::vector<uint8_t>& instructions) {
-
- P p;
- p.setData(input.data(), input.size());
-
- // since we are only using a byte to index
- CHECK(reads.size() <= 255) << reads.size();
-
- for (size_t i = 0; i < instructions.size() - 1; i += 2) {
- uint8_t a = instructions[i];
- uint8_t readIdx = a % reads.size();
-
- uint8_t b = instructions[i + 1];
-
- FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
- << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
- << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
- << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
- << " cap: " << p.dataCapacity();
-
- reads[readIdx](p, b);
- }
-}
-
-void fuzz(uint8_t options, const std::vector<uint8_t>& input, const std::vector<uint8_t>& instructions) {
- uint8_t parcelType = options & 0x3;
-
- switch (parcelType) {
- case 0x0:
- doFuzz<::android::hardware::Parcel>(HWBINDER_PARCEL_READ_FUNCTIONS, input,
- instructions);
- break;
- case 0x1:
- doFuzz<::android::Parcel>(BINDER_PARCEL_READ_FUNCTIONS, input, instructions);
- break;
- case 0x2:
- doFuzz<NdkParcelAdapter>(BINDER_NDK_PARCEL_READ_FUNCTIONS, input, instructions);
- break;
- case 0x3:
- /*reserved for future use*/
- break;
- default:
- LOG_ALWAYS_FATAL("unknown parcel type %d", static_cast<int>(parcelType));
- }
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size <= 1) return 0; // no use
-
- // avoid timeouts, see b/142617274, b/142473153
- if (size > 50000) return 0;
-
- uint8_t options = *data;
- data++;
- size--;
-
- // TODO: generate 'objects' data
-
- // data to fill out parcel
- size_t inputLen = size / 2;
- std::vector<uint8_t> input(data, data + inputLen);
- data += inputLen;
- size -= inputLen;
-
- // data to use to determine what to do
- size_t instructionLen = size;
- std::vector<uint8_t> instructions(data, data + instructionLen);
- data += instructionLen;
- size -= instructionLen;
-
- CHECK(size == 0) << "size: " << size;
-
- FUZZ_LOG() << "options: " << (int)options << " inputLen: " << inputLen << " instructionLen: " << instructionLen;
- FUZZ_LOG() << "input: " << hexString(input);
- FUZZ_LOG() << "instructions: " << hexString(instructions);
-
- fuzz(options, input, instructions);
- return 0;
-}
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 52bd5de..0ece121 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -51,6 +51,8 @@
/*
* maps memory from ashmem, with the given name for debugging
+ * if the READ_ONLY flag is set, the memory will be writeable by the calling process,
+ * but not by others. this is NOT the case with the other ctors.
*/
explicit MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = nullptr);
@@ -78,7 +80,7 @@
int flags = 0, const char* device = nullptr);
private:
- status_t mapfd(int fd, size_t size, off_t offset = 0);
+ status_t mapfd(int fd, bool writeableByCaller, size_t size, off_t offset = 0);
int mFD;
size_t mSize;
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index b6814aa..4ea3dd3 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -59,7 +59,6 @@
template <typename T>
bool setParcelable(std::shared_ptr<T> p) {
- std::lock_guard<std::mutex> l(mMutex);
static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
if (p && this->getStability() > p->getStability()) {
return false;
@@ -73,7 +72,6 @@
template <typename T>
std::shared_ptr<T> getParcelable() const {
static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
- std::lock_guard<std::mutex> l(mMutex);
const std::string& parcelableDesc = T::getParcelableDescriptor();
if (!this->mParcelPtr) {
if (!this->mParcelable || !this->mParcelableName) {
@@ -103,7 +101,7 @@
return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
}
- Stability getStability() const override { return mStability; };
+ Stability getStability() const override { return mStability; }
inline bool operator!=(const ParcelableHolder& rhs) const {
return std::tie(mParcelable, mParcelPtr, mStability) !=
@@ -135,7 +133,6 @@
mutable std::optional<std::string> mParcelableName;
mutable std::unique_ptr<Parcel> mParcelPtr;
Stability mStability;
- mutable std::mutex mMutex;
};
} // namespace os
} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 4fd0657..cecc759 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -26,6 +26,9 @@
"-D__ANDROID_API__=10000",
],
},
+ darwin: {
+ enabled: false,
+ },
},
}
@@ -35,6 +38,8 @@
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
+ llndk_stubs: "libbinder_ndk.llndk",
+
export_include_dirs: [
"include_cpp",
"include_ndk",
@@ -82,6 +87,9 @@
linux: {
version_script: "libbinder_ndk.map.txt",
},
+ darwin: {
+ enabled: false,
+ },
},
stubs: {
symbol_file: "libbinder_ndk.map.txt",
@@ -120,7 +128,7 @@
}
llndk_library {
- name: "libbinder_ndk",
+ name: "libbinder_ndk.llndk",
symbol_file: "libbinder_ndk.map.txt",
export_include_dirs: [
"include_cpp",
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 2a2eed7..d35debc 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include <android/binder_context.h>
#include <android/binder_ibinder.h>
#include <android/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
#include "ibinder_internal.h"
#include <android/binder_stability.h>
@@ -181,7 +181,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 f601127..0fa47c6 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -115,13 +115,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/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 18877af..8d60226 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -313,7 +313,8 @@
/**
* Takes ownership of a.
*/
- explicit ScopedFileDescriptor(int a = -1) : ScopedAResource(a) {}
+ ScopedFileDescriptor() : ScopedFileDescriptor(-1) {}
+ explicit ScopedFileDescriptor(int a) : ScopedAResource(a) {}
~ScopedFileDescriptor() {}
ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 09949ea..054aebe 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -45,7 +45,7 @@
std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
if (static_cast<size_t>(length) > vec->max_size()) return false;
- vec->resize(length);
+ vec->resize(static_cast<size_t>(length));
*outBuffer = vec->data();
return true;
}
@@ -66,7 +66,7 @@
*vec = std::optional<std::vector<T>>(std::vector<T>{});
if (static_cast<size_t>(length) > (*vec)->max_size()) return false;
- (*vec)->resize(length);
+ (*vec)->resize(static_cast<size_t>(length));
*outBuffer = (*vec)->data();
return true;
@@ -90,7 +90,7 @@
std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
if (static_cast<size_t>(length) > vec->max_size()) return false;
- vec->resize(length);
+ vec->resize(static_cast<size_t>(length));
return true;
}
@@ -117,7 +117,7 @@
*vec = std::optional<std::vector<T>>(std::vector<T>{});
if (static_cast<size_t>(length) > (*vec)->max_size()) return false;
- (*vec)->resize(length);
+ (*vec)->resize(static_cast<size_t>(length));
return true;
}
@@ -257,7 +257,7 @@
if (length <= 0) return false;
std::string* str = static_cast<std::string*>(stringData);
- str->resize(length - 1);
+ str->resize(static_cast<size_t>(length) - 1);
*buffer = &(*str)[0];
return true;
}
@@ -279,7 +279,7 @@
}
*str = std::optional<std::string>(std::string{});
- (*str)->resize(length - 1);
+ (*str)->resize(static_cast<size_t>(length) - 1);
*buffer = &(**str)[0];
return true;
}
@@ -303,7 +303,7 @@
const std::vector<std::string>* vec = static_cast<const std::vector<std::string>*>(vectorData);
const std::string& element = vec->at(index);
- *outLength = element.size();
+ *outLength = static_cast<int32_t>(element.size());
return element.c_str();
}
@@ -337,7 +337,7 @@
return nullptr;
}
- *outLength = element->size();
+ *outLength = static_cast<int32_t>(element->size());
return element->c_str();
}
@@ -345,7 +345,7 @@
* Convenience API for writing a std::string.
*/
static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::string& str) {
- return AParcel_writeString(parcel, str.c_str(), str.size());
+ return AParcel_writeString(parcel, str.c_str(), static_cast<int32_t>(str.size()));
}
/**
@@ -365,7 +365,7 @@
return AParcel_writeString(parcel, nullptr, -1);
}
- return AParcel_writeString(parcel, str->c_str(), str->size());
+ return AParcel_writeString(parcel, str->c_str(), static_cast<int32_t>(str->size()));
}
/**
@@ -383,7 +383,7 @@
static inline binder_status_t AParcel_writeVector(AParcel* parcel,
const std::vector<std::string>& vec) {
const void* vectorData = static_cast<const void*>(&vec);
- return AParcel_writeStringArray(parcel, vectorData, vec.size(),
+ return AParcel_writeStringArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
AParcel_stdVectorStringElementGetter);
}
@@ -404,7 +404,8 @@
static inline binder_status_t AParcel_writeVector(
AParcel* parcel, const std::optional<std::vector<std::optional<std::string>>>& vec) {
const void* vectorData = static_cast<const void*>(&vec);
- return AParcel_writeStringArray(parcel, vectorData, (vec ? vec->size() : -1),
+ return AParcel_writeStringArray(parcel, vectorData,
+ (vec ? static_cast<int32_t>(vec->size()) : -1),
AParcel_nullableStdVectorStringElementGetter);
}
@@ -545,7 +546,7 @@
template <typename P>
static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<P>& vec) {
const void* vectorData = static_cast<const void*>(&vec);
- return AParcel_writeParcelableArray(parcel, vectorData, vec.size(),
+ return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
AParcel_writeStdVectorParcelableElement<P>);
}
@@ -564,7 +565,7 @@
* Writes a vector of int32_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int32_t>& vec) {
- return AParcel_writeInt32Array(parcel, vec.data(), vec.size());
+ return AParcel_writeInt32Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -597,7 +598,7 @@
* Writes a vector of uint32_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint32_t>& vec) {
- return AParcel_writeUint32Array(parcel, vec.data(), vec.size());
+ return AParcel_writeUint32Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -631,7 +632,7 @@
* Writes a vector of int64_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int64_t>& vec) {
- return AParcel_writeInt64Array(parcel, vec.data(), vec.size());
+ return AParcel_writeInt64Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -664,7 +665,7 @@
* Writes a vector of uint64_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint64_t>& vec) {
- return AParcel_writeUint64Array(parcel, vec.data(), vec.size());
+ return AParcel_writeUint64Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -698,7 +699,7 @@
* Writes a vector of float to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<float>& vec) {
- return AParcel_writeFloatArray(parcel, vec.data(), vec.size());
+ return AParcel_writeFloatArray(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -731,7 +732,7 @@
* Writes a vector of double to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<double>& vec) {
- return AParcel_writeDoubleArray(parcel, vec.data(), vec.size());
+ return AParcel_writeDoubleArray(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -764,8 +765,8 @@
* Writes a vector of bool to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<bool>& vec) {
- return AParcel_writeBoolArray(parcel, static_cast<const void*>(&vec), vec.size(),
- AParcel_stdVectorGetter<bool>);
+ return AParcel_writeBoolArray(parcel, static_cast<const void*>(&vec),
+ static_cast<int32_t>(vec.size()), AParcel_stdVectorGetter<bool>);
}
/**
@@ -801,7 +802,7 @@
* Writes a vector of char16_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<char16_t>& vec) {
- return AParcel_writeCharArray(parcel, vec.data(), vec.size());
+ return AParcel_writeCharArray(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -834,7 +835,8 @@
* Writes a vector of uint8_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint8_t>& vec) {
- return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()), vec.size());
+ return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()),
+ static_cast<int32_t>(vec.size()));
}
/**
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index 6701518..dfcf4dc 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -25,6 +25,8 @@
*/
#pragma once
+#include <android/binder_parcel_utils.h>
+#include <optional>
namespace ndk {
// Also see Parcelable.h in libbinder.
@@ -33,6 +35,89 @@
STABILITY_LOCAL,
STABILITY_VINTF, // corresponds to @VintfStability
};
+#define RETURN_ON_FAILURE(expr) \
+ do { \
+ binder_status_t _status = (expr); \
+ if (_status != STATUS_OK) return _status; \
+ } while (false)
+
+class AParcelableHolder {
+ public:
+ AParcelableHolder() = delete;
+ explicit AParcelableHolder(parcelable_stability_t stability)
+ : mParcel(AParcel_create()), mStability(stability) {}
+
+ virtual ~AParcelableHolder() = default;
+
+ binder_status_t writeToParcel(AParcel* parcel) const {
+ RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
+ RETURN_ON_FAILURE(AParcel_writeInt32(parcel, AParcel_getDataSize(this->mParcel.get())));
+ RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0,
+ AParcel_getDataSize(this->mParcel.get())));
+ return STATUS_OK;
+ }
+
+ binder_status_t readFromParcel(const AParcel* parcel) {
+ AParcel_reset(mParcel.get());
+
+ RETURN_ON_FAILURE(AParcel_readInt32(parcel, &this->mStability));
+ int32_t dataSize;
+ binder_status_t status = AParcel_readInt32(parcel, &dataSize);
+
+ if (status != STATUS_OK || dataSize < 0) {
+ return status != STATUS_OK ? status : STATUS_BAD_VALUE;
+ }
+
+ int32_t dataStartPos = AParcel_getDataPosition(parcel);
+
+ if (dataStartPos > INT32_MAX - dataSize) {
+ return STATUS_BAD_VALUE;
+ }
+
+ status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return AParcel_setDataPosition(parcel, dataStartPos + dataSize);
+ }
+
+ template <typename T>
+ bool setParcelable(T* p) {
+ if (p && this->mStability > T::_aidl_stability) {
+ return false;
+ }
+ AParcel_reset(mParcel.get());
+ AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
+ p->writeToParcel(mParcel.get());
+ return true;
+ }
+
+ template <typename T>
+ std::unique_ptr<T> getParcelable() const {
+ const std::string parcelableDesc(T::descriptor);
+ AParcel_setDataPosition(mParcel.get(), 0);
+ if (AParcel_getDataSize(mParcel.get()) == 0) {
+ return nullptr;
+ }
+ std::string parcelableDescInParcel;
+ binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
+ if (status != STATUS_OK || parcelableDesc != parcelableDescInParcel) {
+ return nullptr;
+ }
+ std::unique_ptr<T> ret = std::make_unique<T>();
+ status = ret->readFromParcel(this->mParcel.get());
+ if (status != STATUS_OK) {
+ return nullptr;
+ }
+ return std::move(ret);
+ }
+
+ private:
+ mutable ndk::ScopedAParcel mParcel;
+ parcelable_stability_t mStability;
+};
+
+#undef RETURN_ON_FAILURE
} // namespace ndk
/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index a031e29..93c3f32 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -1120,6 +1120,53 @@
// @END-PRIMITIVE-READ-WRITE
#endif //__ANDROID_API__ >= 29
+#if __ANDROID_API__ >= 31
+/**
+ * Reset the parcel to the initial status.
+ *
+ * Available since API level 31.
+ *
+ * \param parcel The parcel of which to be reset.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AParcel_reset(AParcel* parcel) __INTRODUCED_IN(31);
+
+/**
+ * Gets the size of the parcel.
+ *
+ * Available since API level 31.
+ *
+ * \param parcel The parcel of which to get the size.
+ *
+ * \return The size of the parcel.
+ */
+int32_t AParcel_getDataSize(const AParcel* parcel) __INTRODUCED_IN(31);
+
+/**
+ * Copy the data of a parcel to other parcel.
+ *
+ * Available since API level 31.
+ *
+ * \param from The source
+ * \param to The detination
+ * \param start The position where the copied data starts.
+ * \param size The amount of data which will be copied.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AParcel_appendFrom(const AParcel* from, AParcel* to, int32_t start, int32_t size)
+ __INTRODUCED_IN(31);
+
+/**
+ * Creates a parcel.
+ *
+ * Available since API level 31.
+ *
+ * \return A parcel which is not related to any IBinder objects.
+ */
+AParcel* AParcel_create() __INTRODUCED_IN(31);
+#endif //__ANDROID_API__ >= 31
__END_DECLS
/** @} */
diff --git a/libs/binder/ndk/include_platform/android/binder_context.h b/libs/binder/ndk/include_platform/android/binder_context.h
deleted file mode 100644
index a99d555..0000000
--- a/libs/binder/ndk/include_platform/android/binder_context.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <android/binder_ibinder.h>
-
-__BEGIN_DECLS
-
-/**
- * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
- * must be called on a local binder server before it is sent out to any othe
- * process. If this is a remote binder, it will abort. If the kernel doesn't
- * support this feature, you'll always get null from AIBinder_getCallingSid.
- *
- * \param binder local server binder to request security contexts on
- */
-__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
- __INTRODUCED_IN(31);
-
-/**
- * Returns the selinux context of the callee.
- *
- * In order for this to work, the following conditions must be met:
- * - The kernel must be new enough to support this feature.
- * - The server must have called AIBinder_setRequestingSid.
- * - The callee must be a remote process.
- *
- * \return security context or null if unavailable. The lifetime of this context
- * is the lifetime of the transaction.
- */
-__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
-
-__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index 2af65cf..a99d555 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -16,39 +16,32 @@
#pragma once
-// binder_context.h used to be part of this header and is included for backwards
-// compatibility.
-#include <android/binder_context.h>
-
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
-
#include <android/binder_ibinder.h>
-#include <binder/IBinder.h>
+
+__BEGIN_DECLS
/**
- * Get libbinder version of binder from AIBinder.
+ * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
+ * must be called on a local binder server before it is sent out to any othe
+ * process. If this is a remote binder, it will abort. If the kernel doesn't
+ * support this feature, you'll always get null from AIBinder_getCallingSid.
*
- * WARNING: function calls to a local object on the other side of this function
- * will parcel. When converting between binders, keep in mind it is not as
- * efficient as a direct function call.
- *
- * \param binder binder with ownership retained by the client
- * \return platform binder object
+ * \param binder local server binder to request security contexts on
*/
-android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder);
+__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
+ __INTRODUCED_IN(31);
/**
- * Get libbinder_ndk version of binder from platform binder.
+ * Returns the selinux context of the callee.
*
- * WARNING: function calls to a local object on the other side of this function
- * will parcel. When converting between binders, keep in mind it is not as
- * efficient as a direct function call.
+ * In order for this to work, the following conditions must be met:
+ * - The kernel must be new enough to support this feature.
+ * - The server must have called AIBinder_setRequestingSid.
+ * - The callee must be a remote process.
*
- * \param binder platform binder which may be from anywhere (doesn't have to be
- * created with libbinder_ndK)
- * \return binder with one reference count of ownership given to the client. See
- * AIBinder_decStrong
+ * \return security context or null if unavailable. The lifetime of this context
+ * is the lifetime of the transaction.
*/
-AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
+__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
-#endif
+__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h
new file mode 100644
index 0000000..f0c00e8
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_libbinder.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+
+#include <android/binder_ibinder.h>
+#include <binder/IBinder.h>
+
+/**
+ * Get libbinder version of binder from AIBinder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder binder with ownership retained by the client
+ * \return platform binder object
+ */
+android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder);
+
+/**
+ * Get libbinder_ndk version of binder from platform binder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder platform binder which may be from anywhere (doesn't have to be
+ * created with libbinder_ndK)
+ * \return binder with one reference count of ownership given to the client. See
+ * AIBinder_decStrong
+ */
+AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
+
+#endif
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 1701fb5..947cc98 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -120,6 +120,11 @@
AServiceManager_isDeclared; # apex llndk
AServiceManager_registerLazyService; # llndk
AServiceManager_waitForService; # apex llndk
+
+ AParcel_reset;
+ AParcel_getDataSize;
+ AParcel_appendFrom;
+ AParcel_create;
};
LIBBINDER_NDK_PLATFORM {
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 722ae23..2f95318 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -647,4 +647,22 @@
return parcel->get()->allowFds();
}
+binder_status_t AParcel_reset(AParcel* parcel) {
+ parcel->get()->freeData();
+ return STATUS_OK;
+}
+
+int32_t AParcel_getDataSize(const AParcel* parcel) {
+ return parcel->get()->dataSize();
+}
+
+binder_status_t AParcel_appendFrom(const AParcel* from, AParcel* to, int32_t start, int32_t size) {
+ status_t status = to->get()->appendFrom(from->get(), start, size);
+ return PruneStatusT(status);
+}
+
+AParcel* AParcel_create() {
+ return new AParcel(nullptr);
+}
+
// @END
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 44d8ebf..f84d9d3 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -18,9 +18,9 @@
#include <aidl/BnBinderNdkUnitTest.h>
#include <aidl/BnEmpty.h>
#include <android-base/logging.h>
-#include <android/binder_context.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>
@@ -185,6 +185,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/binder/fuzzer/Android.bp b/libs/binder/parcel_fuzzer/Android.bp
similarity index 67%
rename from libs/binder/fuzzer/Android.bp
rename to libs/binder/parcel_fuzzer/Android.bp
index d2b4d52..3e6fe99 100644
--- a/libs/binder/fuzzer/Android.bp
+++ b/libs/binder/parcel_fuzzer/Android.bp
@@ -12,10 +12,13 @@
"binder_ndk.cpp",
"hwbinder.cpp",
"main.cpp",
+ "random_fd.cpp",
+ "random_parcel.cpp",
"util.cpp",
],
static_libs: [
"libbase",
+ "libbinder_random_parcel",
"libcgrouprc",
"libcgrouprc_format",
"libcutils",
@@ -45,3 +48,25 @@
// produced, you may find uncommenting the below line very useful.
// cflags: ["-DENABLE_LOG_FUZZ"],
}
+
+cc_library_static {
+ name: "libbinder_random_parcel",
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
+ srcs: [
+ "random_fd.cpp",
+ "random_parcel.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+ local_include_dirs: ["include_random_parcel"],
+ export_include_dirs: ["include_random_parcel"],
+}
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/parcel_fuzzer/binder.cpp
similarity index 87%
rename from libs/binder/fuzzer/binder.cpp
rename to libs/binder/parcel_fuzzer/binder.cpp
index c2d4a3f..e5c6333 100644
--- a/libs/binder/fuzzer/binder.cpp
+++ b/libs/binder/parcel_fuzzer/binder.cpp
@@ -137,6 +137,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();
@@ -145,6 +146,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;
@@ -158,17 +160,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),
@@ -176,30 +183,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*/) {
@@ -236,6 +255,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/fuzzer/binder.h b/libs/binder/parcel_fuzzer/binder.h
similarity index 98%
rename from libs/binder/fuzzer/binder.h
rename to libs/binder/parcel_fuzzer/binder.h
index b224ef4..0c51d68 100644
--- a/libs/binder/fuzzer/binder.h
+++ b/libs/binder/parcel_fuzzer/binder.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <binder/Parcel.h>
#include <vector>
diff --git a/libs/binder/fuzzer/binder_ndk.cpp b/libs/binder/parcel_fuzzer/binder_ndk.cpp
similarity index 83%
rename from libs/binder/fuzzer/binder_ndk.cpp
rename to libs/binder/parcel_fuzzer/binder_ndk.cpp
index 29da8f7..008780c 100644
--- a/libs/binder/fuzzer/binder_ndk.cpp
+++ b/libs/binder/parcel_fuzzer/binder_ndk.cpp
@@ -18,6 +18,7 @@
#include "binder_ndk.h"
#include <android/binder_parcel_utils.h>
+#include <android/binder_parcelable_utils.h>
#include "util.h"
@@ -54,6 +55,25 @@
binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR());
FUZZ_LOG() << "read status header: " << status;
},
+ [](const NdkParcelAdapter& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to getDataSize the parcel";
+ AParcel_getDataSize(p.aParcel());
+ FUZZ_LOG() << "getDataSize done";
+ },
+ [](const NdkParcelAdapter& p, uint8_t data) {
+ FUZZ_LOG() << "about to read a ParcelableHolder";
+ ndk::AParcelableHolder ph {(data % 2 == 1) ? ndk::STABILITY_LOCAL : ndk::STABILITY_VINTF};
+ binder_status_t status = AParcel_readParcelable(p.aParcel(), &ph);
+ FUZZ_LOG() << "read the ParcelableHolder: " << status;
+ },
+ [](const NdkParcelAdapter& p, uint8_t data) {
+ FUZZ_LOG() << "about to appendFrom";
+ AParcel* parcel = AParcel_create();
+ binder_status_t status = AParcel_appendFrom(p.aParcel(), parcel, 0, data);
+ AParcel_delete(parcel);
+ FUZZ_LOG() << "appendFrom: " << status;
+ },
+
PARCEL_READ(int32_t, AParcel_readInt32),
PARCEL_READ(uint32_t, AParcel_readUint32),
PARCEL_READ(int64_t, AParcel_readInt64),
diff --git a/libs/binder/fuzzer/binder_ndk.h b/libs/binder/parcel_fuzzer/binder_ndk.h
similarity index 91%
rename from libs/binder/fuzzer/binder_ndk.h
rename to libs/binder/parcel_fuzzer/binder_ndk.h
index 622cafc..e69d9c1 100644
--- a/libs/binder/fuzzer/binder_ndk.h
+++ b/libs/binder/parcel_fuzzer/binder_ndk.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <android/binder_auto_utils.h>
#include <vector>
@@ -31,6 +32,9 @@
const AParcel* aParcel() const { return mParcel.get(); }
AParcel* aParcel() { return mParcel.get(); }
+ android::Parcel* parcel() { return aParcel()->get(); }
+
+ const uint8_t* data() const { return aParcel()->get()->data(); }
size_t dataSize() const { return aParcel()->get()->dataSize(); }
size_t dataAvail() const { return aParcel()->get()->dataAvail(); }
size_t dataPosition() const { return aParcel()->get()->dataPosition(); }
diff --git a/libs/binder/fuzzer/hwbinder.cpp b/libs/binder/parcel_fuzzer/hwbinder.cpp
similarity index 100%
rename from libs/binder/fuzzer/hwbinder.cpp
rename to libs/binder/parcel_fuzzer/hwbinder.cpp
diff --git a/libs/binder/fuzzer/hwbinder.h b/libs/binder/parcel_fuzzer/hwbinder.h
similarity index 98%
rename from libs/binder/fuzzer/hwbinder.h
rename to libs/binder/parcel_fuzzer/hwbinder.h
index a6c66be..1fa56d4 100644
--- a/libs/binder/fuzzer/hwbinder.h
+++ b/libs/binder/parcel_fuzzer/hwbinder.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <hwbinder/Parcel.h>
#include <vector>
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
similarity index 63%
copy from libs/binder/fuzzer/parcel_fuzzer.h
copy to libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
index 10cf17c..0a083d7 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,5 +14,14 @@
* limitations under the License.
*/
-template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+// ownership to callee, always valid or aborts
+// get a random FD for use in fuzzing, of a few different specific types
+int getRandomFd(FuzzedDataProvider* provider);
+
+} // namespace android
diff --git a/libs/binder/fuzzer/binder.h b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
similarity index 72%
copy from libs/binder/fuzzer/binder.h
copy to libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
index b224ef4..b92a6a9 100644
--- a/libs/binder/fuzzer/binder.h
+++ b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+#pragma once
+
#include <binder/Parcel.h>
-#include <vector>
+#include <fuzzer/FuzzedDataProvider.h>
-#include "parcel_fuzzer.h"
-
-extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
+namespace android {
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider);
+} // namespace android
diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/parcel_fuzzer/main.cpp
new file mode 100644
index 0000000..386c70b
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/main.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define FUZZ_LOG_TAG "main"
+
+#include "binder.h"
+#include "binder_ndk.h"
+#include "hwbinder.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <fuzzbinder/random_parcel.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <cstdlib>
+#include <ctime>
+
+using android::fillRandomParcel;
+
+void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
+ // TODO: functionality to create random parcels for libhwbinder parcels
+ std::vector<uint8_t> input = provider.ConsumeRemainingBytes<uint8_t>();
+ p->setData(input.data(), input.size());
+}
+static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider) {
+ // fill underlying parcel using functions to fill random libbinder parcel
+ fillRandomParcel(p->parcel(), std::move(provider));
+}
+
+template <typename P>
+void doFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
+ FuzzedDataProvider&& provider) {
+ // Allow some majority of the bytes to be dedicated to telling us what to
+ // do. The fixed value added here represents that we want to test doing a
+ // lot of 'instructions' even on really short parcels.
+ size_t maxInstructions = 20 + (provider.remaining_bytes() * 2 / 3);
+ // but don't always use that many instructions. We want to allow the fuzzer
+ // to explore large parcels with few instructions if it wants to.
+ std::vector<uint8_t> instructions = provider.ConsumeBytes<uint8_t>(
+ provider.ConsumeIntegralInRange<size_t>(0, maxInstructions));
+
+ P p;
+ fillRandomParcel(&p, std::move(provider));
+
+ // since we are only using a byte to index
+ CHECK(reads.size() <= 255) << reads.size();
+
+ FUZZ_LOG() << "backend: " << backend;
+ FUZZ_LOG() << "input: " << hexString(p.data(), p.dataSize());
+ FUZZ_LOG() << "instructions: " << hexString(instructions);
+
+ for (size_t i = 0; i + 1 < instructions.size(); i += 2) {
+ uint8_t a = instructions[i];
+ uint8_t readIdx = a % reads.size();
+
+ uint8_t b = instructions[i + 1];
+
+ FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
+ << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
+ << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
+ << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
+ << " cap: " << p.dataCapacity();
+
+ reads[readIdx](p, b);
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size <= 1) return 0; // no use
+
+ // avoid timeouts, see b/142617274, b/142473153
+ if (size > 50000) return 0;
+
+ FuzzedDataProvider provider = FuzzedDataProvider(data, size);
+
+ const std::function<void(FuzzedDataProvider &&)> fuzzBackend[3] = {
+ [](FuzzedDataProvider&& provider) {
+ doFuzz<::android::hardware::Parcel>("hwbinder", HWBINDER_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ };
+
+ provider.PickValueInArray(fuzzBackend)(std::move(provider));
+
+ return 0;
+}
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/binder/parcel_fuzzer/parcel_fuzzer.h
similarity index 97%
rename from libs/binder/fuzzer/parcel_fuzzer.h
rename to libs/binder/parcel_fuzzer/parcel_fuzzer.h
index 10cf17c..b68a8a9 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/libs/binder/parcel_fuzzer/parcel_fuzzer.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
template <typename P>
using ParcelRead = std::function<void(const P& p, uint8_t data)>;
diff --git a/libs/binder/parcel_fuzzer/random_fd.cpp b/libs/binder/parcel_fuzzer/random_fd.cpp
new file mode 100644
index 0000000..cef6adb
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/random_fd.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/random_fd.h>
+
+#include <fcntl.h>
+
+#include <android-base/logging.h>
+#include <cutils/ashmem.h>
+
+namespace android {
+
+int getRandomFd(FuzzedDataProvider* provider) {
+ int fd = provider->PickValueInArray<std::function<int()>>({
+ []() { return ashmem_create_region("binder test region", 1024); },
+ []() { return open("/dev/null", O_RDWR); },
+ })();
+ CHECK(fd >= 0);
+ return fd;
+}
+
+} // namespace android
diff --git a/libs/binder/parcel_fuzzer/random_parcel.cpp b/libs/binder/parcel_fuzzer/random_parcel.cpp
new file mode 100644
index 0000000..9ca4c8a
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/random_parcel.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 <fuzzbinder/random_parcel.h>
+
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+#include <fuzzbinder/random_fd.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class NamedBinder : public BBinder {
+public:
+ NamedBinder(const String16& descriptor) : mDescriptor(descriptor) {}
+ const String16& getInterfaceDescriptor() const override { return mDescriptor; }
+
+private:
+ String16 mDescriptor;
+};
+
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
+ while (provider.remaining_bytes() > 0) {
+ auto fillFunc = provider.PickValueInArray<const std::function<void()>>({
+ // write data
+ [&]() {
+ size_t toWrite =
+ provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
+ std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(toWrite);
+ CHECK(OK == p->write(data.data(), data.size()));
+ },
+ // write FD
+ [&]() {
+ int fd = getRandomFd(&provider);
+ CHECK(OK == p->writeFileDescriptor(fd, true /*takeOwnership*/));
+ },
+ // write binder
+ [&]() {
+ auto makeFunc = provider.PickValueInArray<const std::function<sp<IBinder>()>>({
+ [&]() {
+ // descriptor is the length of a class name, e.g.
+ // "some.package.Foo"
+ std::string str =
+ provider.ConsumeRandomLengthString(100 /*max length*/);
+ return new NamedBinder(String16(str.c_str()));
+ },
+ []() {
+ // this is the easiest remote binder to get ahold of, and it
+ // should be able to handle anything thrown at it, and
+ // essentially every process can talk to it, so it's a good
+ // candidate for checking usage of an actual BpBinder
+ return IInterface::asBinder(defaultServiceManager());
+ },
+ []() { return nullptr; },
+ });
+ sp<IBinder> binder = makeFunc();
+ CHECK(OK == p->writeStrongBinder(binder));
+ },
+ });
+
+ fillFunc();
+ }
+}
+
+} // namespace android
diff --git a/libs/binder/fuzzer/util.cpp b/libs/binder/parcel_fuzzer/util.cpp
similarity index 100%
rename from libs/binder/fuzzer/util.cpp
rename to libs/binder/parcel_fuzzer/util.cpp
diff --git a/libs/binder/fuzzer/util.h b/libs/binder/parcel_fuzzer/util.h
similarity index 98%
rename from libs/binder/fuzzer/util.h
rename to libs/binder/parcel_fuzzer/util.h
index aa504d2..45e8c57 100644
--- a/libs/binder/fuzzer/util.h
+++ b/libs/binder/parcel_fuzzer/util.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <iostream>
#include <sstream>
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index dc8270e..fd5f2f5 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -10,6 +10,11 @@
"libbinder_ndk_sys",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
}
rust_library {
@@ -23,6 +28,11 @@
"libbinder_ndk",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
}
rust_bindgen {
@@ -64,6 +74,9 @@
"-D__ANDROID_API__=10000",
],
},
+ darwin: {
+ enabled: false,
+ },
},
}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index 303f4a5..3f20a4f 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include <android/binder_context.h>
#include <android/binder_ibinder.h>
+#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
#include <android/binder_parcel.h>
#include <android/binder_process.h>
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..c465bed
--- /dev/null
+++ b/libs/binder/tests/fuzzers/Android.bp
@@ -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.
+//
+
+cc_defaults {
+ name: "binder_fuzz_defaults",
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "libbase",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
+}
+
+cc_fuzz {
+ name: "binder_binderFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["BinderFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_bpBinderFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ host_supported: false,
+ srcs: ["BpBinderFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_persistableBundleFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["PersistableBundleFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_stabilityFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["StabilityFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_statusFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["StatusFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_textOutputFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["TextOutputFuzz.cpp"],
+}
diff --git a/libs/binder/tests/fuzzers/BinderFuzz.cpp b/libs/binder/tests/fuzzers/BinderFuzz.cpp
new file mode 100644
index 0000000..1e5d80a
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BinderFuzz.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <BinderFuzzFunctions.h>
+#include <IBinderFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Binder.h>
+
+namespace android {
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ sp<BBinder> bbinder = new BBinder();
+
+ // To prevent memory from running out from calling too many add item operations.
+ const uint32_t MAX_RUNS = 2048;
+ uint32_t count = 0;
+
+ while (fdp.remaining_bytes() > 0 && count++ < MAX_RUNS) {
+ if (fdp.ConsumeBool()) {
+ callArbitraryFunction(&fdp, gBBinderOperations, bbinder);
+ } else {
+ callArbitraryFunction(&fdp, gIBinderOperations,
+ reinterpret_cast<IBinder *>(bbinder.get()));
+ }
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
new file mode 100644
index 0000000..9ac65bb
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <IBinderFuzzFunctions.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <stdint.h>
+#include <atomic>
+
+namespace android {
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BBinder>&)>>
+ gBBinderOperations = {[](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->isRequestingSid();
+ },
+ [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+ bool request_sid = fdp->ConsumeBool();
+ bbinder->setRequestingSid(request_sid);
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getExtension();
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ static IBinder* extension = nullptr;
+ bbinder->setExtension(extension);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+ int priority;
+ int policy = fdp->ConsumeIntegralInRange<int>(0, 2);
+ if (policy == 0) {
+ priority = fdp->ConsumeIntegralInRange<int>(-20, 19);
+ } else {
+ priority = fdp->ConsumeIntegralInRange<int>(1, 99);
+ }
+ bbinder->setMinSchedulerPolicy(policy, priority);
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getMinSchedulerPolicy();
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getMinSchedulerPriority();
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getDebugPid();
+ }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/fuzzers/BpBinderFuzz.cpp
new file mode 100644
index 0000000..c50279b
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BpBinderFuzz.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <BpBinderFuzzFunctions.h>
+#include <IBinderFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/BpBinder.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ // TODO: In the future it would be more effective to fork a new process and then pass a BBinder
+ // to your process. Right now this is not implemented because it would involved fuzzing IPC on a
+ // forked process, and libfuzzer will not be able to handle code coverage. This would lead to
+ // crashes that are not easy to diagnose.
+ int32_t handle = fdp.ConsumeIntegralInRange<int32_t>(0, 1024);
+ sp<BpBinder> bpbinder = BpBinder::create(handle);
+ if (bpbinder == nullptr) return 0;
+
+ // To prevent memory from running out from calling too many add item operations.
+ const uint32_t MAX_RUNS = 2048;
+ uint32_t count = 0;
+ sp<IBinder::DeathRecipient> s_recipient = new FuzzDeathRecipient();
+
+ while (fdp.remaining_bytes() > 0 && count++ < MAX_RUNS) {
+ if (fdp.ConsumeBool()) {
+ callArbitraryFunction(&fdp, gBPBinderOperations, bpbinder, s_recipient);
+ } else {
+ callArbitraryFunction(&fdp, gIBinderOperations, bpbinder.get());
+ }
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
new file mode 100644
index 0000000..c685b41
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <IBinderFuzzFunctions.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/BpBinder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+
+#include <cutils/compiler.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
+
+#include <stdio.h>
+
+namespace android {
+
+// Static variable to reference so we don't consume a bunch of memory to link and
+// unlink DeathRecipients.
+static int8_t kBpBinderCookie = 0;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BpBinder>&,
+ const sp<IBinder::DeathRecipient>&)>>
+ gBPBinderOperations =
+ {[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->handle(); },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>& s_recipient) -> void {
+ // Clean up possible leftover memory.
+ wp<IBinder::DeathRecipient> outRecipient(nullptr);
+ bpbinder->sendObituary();
+ bpbinder->unlinkToDeath(nullptr, reinterpret_cast<void*>(&kBpBinderCookie), 0,
+ &outRecipient);
+
+ uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+ kBpBinderCookie = fdp->ConsumeIntegral<int8_t>();
+ bpbinder->linkToDeath(s_recipient.get(),
+ reinterpret_cast<void*>(&kBpBinderCookie), flags);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ wp<IBinder::DeathRecipient> out_recipient(nullptr);
+ uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+ int8_t random_cookie = fdp->ConsumeIntegral<int8_t>();
+ bpbinder->unlinkToDeath(nullptr, reinterpret_cast<void*>(&random_cookie),
+ flags, &out_recipient);
+ },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->remoteBinder(); },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->sendObituary(); },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ uint32_t uid = fdp->ConsumeIntegral<uint32_t>();
+ bpbinder->getBinderProxyCount(uid);
+ },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->enableCountByUid(); },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->disableCountByUid(); },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ Vector<uint32_t> uids;
+ Vector<uint32_t> counts;
+ bpbinder->getCountByUid(uids, counts);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ bool enable = fdp->ConsumeBool();
+ bpbinder->setCountByUidEnabled(enable);
+ },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ binder_proxy_limit_callback cb = binder_proxy_limit_callback();
+ bpbinder->setLimitCallback(cb);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ int high = fdp->ConsumeIntegral<int>();
+ int low = fdp->ConsumeIntegral<int>();
+ bpbinder->setBinderProxyCountWatermarks(high, low);
+ }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
new file mode 100644
index 0000000..626b758
--- /dev/null
+++ b/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+#include <cutils/compiler.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class FuzzDeathRecipient : public IBinder::DeathRecipient {
+private:
+ virtual void binderDied(const wp<IBinder>& who) { (void)who; };
+};
+
+// Allow objects to be attached that aren't stack locals
+static uint32_t objectID = 0;
+static uint32_t object = 0;
+static uint32_t cleanup_cookie = 0;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, IBinder*)>> gIBinderOperations =
+ {[](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->getInterfaceDescriptor(); },
+ [](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->isBinderAlive(); },
+ [](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->pingBinder(); },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ int fd = STDOUT_FILENO;
+ std::string rand_str = fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ Vector<String16> args;
+ args.push(String16(rand_str.c_str()));
+ ibinder->dump(fd, args);
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ objectID = fdp->ConsumeIntegral<uint32_t>();
+ object = fdp->ConsumeIntegral<uint32_t>();
+ cleanup_cookie = fdp->ConsumeIntegral<uint32_t>();
+ IBinder::object_cleanup_func func = IBinder::object_cleanup_func();
+ ibinder->attachObject(fdp->ConsumeBool() ? reinterpret_cast<void*>(&objectID)
+ : nullptr,
+ fdp->ConsumeBool() ? reinterpret_cast<void*>(&object) : nullptr,
+ fdp->ConsumeBool() ? reinterpret_cast<void*>(&cleanup_cookie)
+ : nullptr,
+ func);
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ uint32_t id = fdp->ConsumeIntegral<uint32_t>();
+ ibinder->findObject(reinterpret_cast<void*>(&id));
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ uint32_t id = fdp->ConsumeIntegral<uint32_t>();
+ ibinder->detachObject(reinterpret_cast<void*>(&id));
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ uint32_t code = fdp->ConsumeIntegral<uint32_t>();
+ Parcel p_data;
+ Parcel reply;
+ uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+ ibinder->transact(code, p_data, &reply, flags);
+ }};
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp b/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
new file mode 100644
index 0000000..4843c46
--- /dev/null
+++ b/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <PersistableBundleFuzzFunctions.h>
+#include <binder/PersistableBundle.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String16.h>
+
+namespace android {
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ std::shared_ptr<os::PersistableBundle> p_bundle(new os::PersistableBundle());
+
+ while (fdp.remaining_bytes() > 0) {
+ String16 key(fdp.ConsumeRandomLengthString(fdp.remaining_bytes()).c_str());
+ callArbitraryFunction(&fdp, gPersistableBundleOperations, p_bundle, &key);
+ }
+
+ return 0;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h b/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
new file mode 100644
index 0000000..820e9e8
--- /dev/null
+++ b/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/PersistableBundle.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+#include <map>
+#include <set>
+#include <vector>
+
+namespace android {
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<
+ void(FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const&, String16*)>>
+ gPersistableBundleOperations =
+ {[](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void { p_bundle->empty(); },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void {
+ Parcel parcel;
+ p_bundle->writeToParcel(&parcel);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void {
+ Parcel parcel;
+ std::vector<uint8_t> buf = fdp->ConsumeBytes<uint8_t>(
+ fdp->ConsumeIntegralInRange<size_t>(0, fdp->remaining_bytes() - 1));
+ parcel.write(buf.data(), buf.size());
+ p_bundle->readFromParcel(&parcel);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void { p_bundle->size(); },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void { p_bundle->erase(*key); },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ bool value = fdp->ConsumeBool();
+ p_bundle->putBoolean(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int32_t value = fdp->ConsumeIntegral<int32_t>();
+ p_bundle->putInt(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ os::PersistableBundle value = os::PersistableBundle();
+ p_bundle->putPersistableBundle(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<String16> value;
+ p_bundle->putStringVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<double> value;
+ p_bundle->putDoubleVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int64_t> value;
+ p_bundle->putLongVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int32_t> value;
+ p_bundle->putIntVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<bool> value;
+ p_bundle->putBooleanVector(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ String16 value(fdp->ConsumeRandomLengthString(fdp->remaining_bytes()).c_str());
+ p_bundle->putString(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int64_t value = fdp->ConsumeIntegral<int64_t>();
+ p_bundle->putLong(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ double value = fdp->ConsumeFloatingPoint<double>();
+ p_bundle->putDouble(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ bool out;
+ p_bundle->getBoolean(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ os::PersistableBundle out;
+ p_bundle->getPersistableBundle(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<String16> out;
+ p_bundle->getStringVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<double> out;
+ p_bundle->getDoubleVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int64_t> out;
+ p_bundle->getLongVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int32_t> out;
+ p_bundle->getIntVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<bool> out;
+ p_bundle->getBooleanVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ String16 out;
+ p_bundle->getString(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ double out;
+ p_bundle->getDouble(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int64_t out;
+ p_bundle->getLong(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int32_t out;
+ p_bundle->getInt(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void {
+ p_bundle->getBooleanKeys();
+ p_bundle->getIntKeys();
+ p_bundle->getLongKeys();
+ p_bundle->getDoubleKeys();
+ p_bundle->getStringKeys();
+ p_bundle->getBooleanVectorKeys();
+ p_bundle->getIntVectorKeys();
+ p_bundle->getLongVectorKeys();
+ p_bundle->getDoubleVectorKeys();
+ p_bundle->getStringVectorKeys();
+ p_bundle->getPersistableBundleKeys();
+ }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/StabilityFuzz.cpp b/libs/binder/tests/fuzzers/StabilityFuzz.cpp
new file mode 100644
index 0000000..8ad9b44
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StabilityFuzz.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <StabilityFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ // Init our wrapper
+ FuzzedDataProvider dataProvider(data, size);
+ android::sp<android::IBinder> bbinder = new android::BBinder();
+
+ // Call some functions
+ while (dataProvider.remaining_bytes() > 0) {
+ callArbitraryFunction(&dataProvider, gStabilityOperations, bbinder);
+ }
+
+ return 0;
+}
diff --git a/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
new file mode 100644
index 0000000..8b4ed70
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Binder.h>
+#include <binder/Stability.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#define STABILITY_MAX_TAG_LENGTH 2048
+static bool marked = false;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<
+ std::function<void(FuzzedDataProvider*, android::sp<android::IBinder> const&)>>
+ gStabilityOperations = {
+ // markCompilationUnit(IBinder* binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ if (!marked) {
+ android::internal::Stability::markCompilationUnit(bbinder.get());
+ marked = true;
+ }
+ },
+
+ // markVintf(IBinder* binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ if (!marked) {
+ android::internal::Stability::markVintf(bbinder.get());
+ marked = true;
+ }
+ },
+
+ // debugLogStability(const std::string& tag, const sp<IBinder>& binder)
+ [](FuzzedDataProvider* fdp, android::sp<android::IBinder> const& bbinder) -> void {
+ std::string tag = fdp->ConsumeRandomLengthString(STABILITY_MAX_TAG_LENGTH);
+ android::internal::Stability::debugLogStability(tag, bbinder);
+ },
+
+ // markVndk(IBinder* binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ if (!marked) {
+ android::internal::Stability::markVndk(bbinder.get());
+ marked = true;
+ }
+ },
+
+ // requiresVintfDeclaration(const sp<IBinder>& binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ android::internal::Stability::requiresVintfDeclaration(bbinder);
+ }};
diff --git a/libs/binder/tests/fuzzers/StatusFuzz.cpp b/libs/binder/tests/fuzzers/StatusFuzz.cpp
new file mode 100644
index 0000000..4f6ad6f
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StatusFuzz.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <StatusFuzzFunctions.h>
+#include <binder/Parcel.h>
+#include <binder/Status.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String8.h>
+#include <cstdint>
+#include <sstream>
+#include <string>
+
+namespace android {
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ int32_t exceptionCode = fdp.ConsumeIntegral<int32_t>();
+ std::string message_str = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ String8 message(message_str.c_str());
+
+ Parcel parcel;
+ std::vector<uint8_t> buf = fdp.ConsumeBytes<uint8_t>(
+ fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes() - 1));
+ parcel.write(buf.data(), buf.size());
+ binder::Status status = binder::Status::fromExceptionCode(exceptionCode, message);
+
+ while (fdp.remaining_bytes() > 0) {
+ callArbitraryFunction(&fdp, gStatusOperations, &status, &parcel);
+ }
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/StatusFuzzFunctions.h b/libs/binder/tests/fuzzers/StatusFuzzFunctions.h
new file mode 100644
index 0000000..bc8d17a
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StatusFuzzFunctions.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Parcel.h>
+#include <binder/Status.h>
+#include <stdio.h>
+#include <utils/String8.h>
+#include <cstdint>
+#include <sstream>
+#include <string>
+
+namespace android {
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, binder::Status*, Parcel*)>>
+ gStatusOperations = {
+ [](FuzzedDataProvider*, binder::Status* status, Parcel* parcel) -> void {
+ parcel->setDataPosition(0);
+ status->readFromParcel(*parcel);
+ },
+ [](FuzzedDataProvider*, binder::Status* status, Parcel* parcel) -> void {
+ status->writeToParcel(parcel);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ status->setServiceSpecificError(fdp->ConsumeIntegral<int32_t>(), message);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ status->setException(fdp->ConsumeIntegral<int32_t>(), message);
+ },
+ [](FuzzedDataProvider*, binder::Status* status, Parcel*) -> void { status->ok(); },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ *status = binder::Status::fromExceptionCode(fdp->ConsumeIntegral<int32_t>(),
+ message);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ *status = binder::Status::fromServiceSpecificError(
+ fdp->ConsumeIntegral<int32_t>());
+ },
+ [](FuzzedDataProvider* fdp, binder::Status*, Parcel*) -> void {
+ binder::Status::exceptionToString(fdp->ConsumeIntegral<int32_t>());
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ *status = binder::Status::fromServiceSpecificError(fdp->ConsumeIntegral<
+ int32_t>(),
+ message);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ *status = binder::Status::fromStatusT(fdp->ConsumeIntegral<status_t>());
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ status->setFromStatusT(fdp->ConsumeIntegral<status_t>());
+ },
+ [](FuzzedDataProvider*, binder::Status* status, Parcel*) -> void {
+ std::stringstream ss;
+ ss << *status;
+ },
+};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
new file mode 100644
index 0000000..c950020
--- /dev/null
+++ b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Debug.h>
+#include <binder/Parcel.h>
+#include <binder/TextOutput.h>
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstddef>
+#include <limits>
+
+// Fuzzer for the TextOutput class. These were lifted from the existing
+// test suite.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ CapturedStderr cap;
+
+ while (fdp.remaining_bytes() > 1) {
+ switch (fdp.ConsumeIntegral<uint8_t>() % 3) {
+ case 0: {
+ std::string input = fdp.ConsumeBytesAsString(
+ fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()));
+ android::aerr << input << android::endl;
+ break;
+ }
+ case 1: {
+ std::string str = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ android::HexDump input(str.c_str(), sizeof(str.c_str()));
+ android::aerr << input << android::endl;
+ break;
+ }
+ case 2: {
+ android::TypeCode input(fdp.ConsumeIntegral<uint32_t>());
+ android::aerr << input << android::endl;
+ }
+ }
+ }
+ cap.Stop();
+
+ return 0;
+}
diff --git a/libs/binder/tests/fuzzers/commonFuzzHelpers.h b/libs/binder/tests/fuzzers/commonFuzzHelpers.h
new file mode 100644
index 0000000..d58d9b6
--- /dev/null
+++ b/libs/binder/tests/fuzzers/commonFuzzHelpers.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <vector>
+
+// Calls a function from the ops_vector
+template <class F, class T, class... Types>
+void callArbitraryFunction(F* fdp, T const& ops_vector, Types... args) {
+ // Choose which function we'll be calling
+ uint8_t function_id = fdp->template ConsumeIntegralInRange<uint8_t>(0, ops_vector.size() - 1);
+
+ // Call the function we've chosen
+ ops_vector[function_id](fdp, args...);
+}
+
+template <class T>
+T getArbitraryVectorElement(FuzzedDataProvider* fdp, std::vector<T> const& vect, bool allow_null) {
+ // If we're allowing null, give it a 50:50 shot at returning a nullptr
+ if (vect.empty() || (allow_null && fdp->ConsumeBool())) {
+ return nullptr;
+ }
+
+ // Otherwise, return an element from our vector
+ return vect.at(fdp->ConsumeIntegralInRange<size_t>(0, vect.size() - 1));
+}
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 88752ee..08c62df 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -19,6 +19,11 @@
double_loadable: true,
vendor_available: true,
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
shared_libs: [
"libbinder",
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 5e785b6..6058430 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -425,6 +425,7 @@
uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0;
do {
+ if (key.bucket > (gNCpus - 1) / CPUS_PER_ENTRY) return {};
if (lastUpdate) {
auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate);
if (!uidUpdated.has_value()) return {};
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index ea2a200..0d5f412 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -387,6 +387,28 @@
}
}
+TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) {
+ uint32_t uid = 0;
+ {
+ // Find an unused UID
+ auto map = getUidsConcurrentTimes();
+ ASSERT_TRUE(map.has_value());
+ ASSERT_FALSE(map->empty());
+ for (const auto &kv : *map) uid = std::max(uid, kv.first);
+ ++uid;
+ }
+ android::base::unique_fd fd{
+ bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
+ ASSERT_GE(fd, 0);
+ uint32_t nCpus = get_nprocs_conf();
+ uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY;
+ time_key_t key = {.uid = uid, .bucket = maxBucket + 1};
+ std::vector<concurrent_val_t> vals(nCpus);
+ ASSERT_FALSE(writeToMapEntry(fd, &key, vals.data(), BPF_NOEXIST));
+ EXPECT_FALSE(getUidsConcurrentTimes().has_value());
+ ASSERT_FALSE(deleteMapEntry(fd, &key));
+}
+
TEST(TimeInStateTest, AllUidTimesConsistent) {
auto tisMap = getUidsCpuFreqTimes();
ASSERT_TRUE(tisMap.has_value());
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 6909637..76518c1 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -9,6 +9,11 @@
"libbinder",
"libutils",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
cc_library {
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 66fb295..243d7f1 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -21,6 +21,11 @@
"-Wno-enum-compare",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
vendor_available: true,
vndk: {
@@ -38,14 +43,14 @@
],
shared_libs: [
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
"liblog",
],
export_shared_lib_headers: [
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
],
diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp
index 8444883..8933dc3 100644
--- a/libs/gralloc/types/fuzzer/Android.bp
+++ b/libs/gralloc/types/fuzzer/Android.bp
@@ -2,6 +2,11 @@
name: "libgralloctypes_fuzzer",
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
fuzz_config: {
cc: ["marissaw@google.com"],
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index c3323fe..d6f9e63 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -732,6 +732,8 @@
mSharedBufferHasBeenQueued = false;
}
+ mDequeuedSlots.insert(buf);
+
return OK;
}
@@ -760,6 +762,8 @@
mSharedBufferHasBeenQueued = true;
}
+ mDequeuedSlots.erase(i);
+
return OK;
}
@@ -895,6 +899,8 @@
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
+ mDequeuedSlots.erase(i);
+
if (mEnableFrameTimestamps) {
mFrameEventHistory->applyDelta(output.frameTimestamps);
// Update timestamps with the local acquire fence.
@@ -1660,6 +1666,7 @@
mRemovedBuffers.push_back(mSlots[attachedSlot].buffer);
}
mSlots[attachedSlot].buffer = graphicBuffer;
+ mDequeuedSlots.insert(attachedSlot);
return NO_ERROR;
}
@@ -1926,6 +1933,10 @@
}
void Surface::freeAllBuffers() {
+ if (!mDequeuedSlots.empty()) {
+ ALOGE("%s: %zu buffers were freed while being dequeued!",
+ __FUNCTION__, mDequeuedSlots.size());
+ }
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].buffer = nullptr;
}
@@ -1947,6 +1958,10 @@
ALOGW("%s: Discarded slot %d doesn't contain buffer!", __FUNCTION__, i);
continue;
}
+ // Don't flush currently dequeued buffers
+ if (mDequeuedSlots.count(i) > 0) {
+ continue;
+ }
outBuffers->push_back(mSlots[i].buffer);
mSlots[i].buffer = nullptr;
}
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 49c83da..55b4101 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -30,6 +30,7 @@
#include <utils/RefBase.h>
#include <shared_mutex>
+#include <unordered_set>
namespace android {
@@ -543,8 +544,15 @@
int mMaxBufferCount;
sp<IProducerListener> mListenerProxy;
+
+ // Get and flush the buffers of given slots, if the buffer in the slot
+ // is currently dequeued then it won't be flushed and won't be returned
+ // in outBuffers.
status_t getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots,
std::vector<sp<GraphicBuffer>>* outBuffers);
+
+ // Buffers that are successfully dequeued/attached and handed to clients
+ std::unordered_set<int> mDequeuedSlots;
};
} // namespace android
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index d64dfd5..d98ffc6 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -72,13 +72,9 @@
}
String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
- size_t len;
- const char16_t* str = parcel->readString16Inplace(&len);
- if (str != nullptr) {
- return String16(str, len);
- } else {
- return String16();
- }
+ std::optional<String16> str;
+ parcel->readString16(&str);
+ return str.value_or(String16());
}
} // namespace view
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 52d73e0..07e5d86 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -40,6 +40,7 @@
cc_library {
name: "libnativewindow",
+ llndk_stubs: "libnativewindow.llndk",
export_include_dirs: [
"include",
"include-private",
@@ -95,7 +96,7 @@
}
llndk_library {
- name: "libnativewindow",
+ name: "libnativewindow.llndk",
symbol_file: "libnativewindow.map.txt",
unversioned: true,
export_include_dirs: ["include"],
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp
index b9cea10..245c9e1 100644
--- a/libs/renderengine/Description.cpp
+++ b/libs/renderengine/Description.cpp
@@ -52,5 +52,10 @@
return colorMatrix != identity;
}
+bool Description::hasDisplayColorMatrix() const {
+ const mat4 identity;
+ return displayColorMatrix != identity;
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 2139acb..02021c9 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1048,6 +1048,7 @@
setOutputDataSpace(display.outputDataspace);
setDisplayMaxLuminance(display.maxLuminance);
+ setDisplayColorTransform(display.colorTransform);
const mat4 projectionMatrix =
ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
@@ -1114,7 +1115,7 @@
position[3] = vec2(bounds.right, bounds.top);
setupLayerCropping(*layer, mesh);
- setColorTransform(display.colorTransform * layer->colorTransform);
+ setColorTransform(layer->colorTransform);
bool usePremultipliedAlpha = true;
bool disableTexture = true;
@@ -1271,6 +1272,10 @@
mState.colorMatrix = colorTransform;
}
+void GLESRenderEngine::setDisplayColorTransform(const mat4& colorTransform) {
+ mState.displayColorMatrix = colorTransform;
+}
+
void GLESRenderEngine::disableTexturing() {
mState.textureEnabled = false;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 61986ff..0e003a0 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -159,6 +159,7 @@
void setupLayerTexturing(const Texture& texture);
void setupFillWithColor(float r, float g, float b, float a);
void setColorTransform(const mat4& colorTransform);
+ void setDisplayColorTransform(const mat4& colorTransform);
void disableTexturing();
void disableBlending();
void setupCornerRadiusCropSize(float width, float height);
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index f4fbf35..a172c56 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -66,6 +66,7 @@
mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
mSamplerLoc = glGetUniformLocation(programId, "sampler");
mColorLoc = glGetUniformLocation(programId, "color");
+ mDisplayColorMatrixLoc = glGetUniformLocation(programId, "displayColorMatrix");
mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
@@ -129,6 +130,9 @@
const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
glUniform4fv(mColorLoc, 1, color);
}
+ if (mDisplayColorMatrixLoc >= 0) {
+ glUniformMatrix4fv(mDisplayColorMatrixLoc, 1, GL_FALSE, desc.displayColorMatrix.asArray());
+ }
if (mInputTransformMatrixLoc >= 0) {
mat4 inputTransformMatrix = desc.inputTransformMatrix;
glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index fc3755e..4292645 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -104,6 +104,7 @@
/* location of transform matrix */
GLint mInputTransformMatrixLoc;
GLint mOutputTransformMatrixLoc;
+ GLint mDisplayColorMatrixLoc;
/* location of corner radius uniform */
GLint mCornerRadiusLoc;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 3ae35ec..dc8ce54 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -180,6 +180,10 @@
description.hasOutputTransformMatrix() || description.hasColorMatrix()
? Key::OUTPUT_TRANSFORM_MATRIX_ON
: Key::OUTPUT_TRANSFORM_MATRIX_OFF)
+ .set(Key::Key::DISPLAY_COLOR_TRANSFORM_MATRIX_MASK,
+ description.hasDisplayColorMatrix()
+ ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON
+ : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF)
.set(Key::ROUNDED_CORNERS_MASK,
description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
.set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
@@ -661,7 +665,9 @@
)__SHADER__";
}
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ if (needs.hasTransformMatrix() ||
+ (needs.getInputTF() != needs.getOutputTF()) ||
+ needs.hasDisplayColorMatrix()) {
if (needs.needsToneMapping()) {
fs << "uniform float displayMaxLuminance;";
fs << "uniform float maxMasteringLuminance;";
@@ -700,6 +706,21 @@
)__SHADER__";
}
+ if (needs.hasDisplayColorMatrix()) {
+ fs << "uniform mat4 displayColorMatrix;";
+ fs << R"__SHADER__(
+ highp vec3 DisplayColorMatrix(const highp vec3 color) {
+ return clamp(vec3(displayColorMatrix * vec4(color, 1.0)), 0.0, 1.0);
+ }
+ )__SHADER__";
+ } else {
+ fs << R"__SHADER__(
+ highp vec3 DisplayColorMatrix(const highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ }
+
generateEOTF(fs, needs);
generateOOTF(fs, needs);
generateOETF(fs, needs);
@@ -732,14 +753,17 @@
}
}
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ if (needs.hasTransformMatrix() ||
+ (needs.getInputTF() != needs.getOutputTF()) ||
+ needs.hasDisplayColorMatrix()) {
if (!needs.isOpaque() && needs.isPremultiplied()) {
// un-premultiply if needed before linearization
// avoid divide by 0 by adding 0.5/256 to the alpha channel
fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
}
fs << "gl_FragColor.rgb = "
- "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
+ "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb))))));";
+
if (!needs.isOpaque() && needs.isPremultiplied()) {
// and re-premultiply if needed after gamma correction
fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 901e631..b492cb3 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -117,6 +117,11 @@
SHADOW_MASK = 1 << SHADOW_SHIFT,
SHADOW_OFF = 0 << SHADOW_SHIFT,
SHADOW_ON = 1 << SHADOW_SHIFT,
+
+ DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT = 14,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_MASK = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_OFF = 0 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_ON = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
};
inline Key() : mKey(0) {}
@@ -143,6 +148,9 @@
inline bool hasOutputTransformMatrix() const {
return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
}
+ inline bool hasDisplayColorMatrix() const {
+ return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) == DISPLAY_COLOR_TRANSFORM_MATRIX_ON;
+ }
inline bool hasTransformMatrix() const {
return hasInputTransformMatrix() || hasOutputTransformMatrix();
}
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index a62161a..fa6ec10 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -44,6 +44,7 @@
bool hasInputTransformMatrix() const;
bool hasOutputTransformMatrix() const;
bool hasColorMatrix() const;
+ bool hasDisplayColorMatrix() const;
// whether textures are premultiplied
bool isPremultipliedAlpha = false;
@@ -79,6 +80,8 @@
// The color matrix will be applied in linear space right before OETF.
mat4 colorMatrix;
+ // The display color matrix will be applied in gamma space after OETF
+ mat4 displayColorMatrix;
mat4 inputTransformMatrix;
mat4 outputTransformMatrix;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 0b5b1e4..a720a27 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -38,22 +38,27 @@
struct RenderEngineTest : public ::testing::Test {
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());
+ renderengine::RenderEngineCreationArgs reCreationArgs =
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setUseColorManagerment(false)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .build();
+ sRE = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+
+ reCreationArgs.useColorManagement = true;
+ sRECM = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
}
static void TearDownTestSuite() {
// The ordering here is important - sCurrentBuffer must live longer
// than RenderEngine to avoid a null reference on tear-down.
sRE = nullptr;
+ sRECM = nullptr;
sCurrentBuffer = nullptr;
}
@@ -82,6 +87,9 @@
for (uint32_t texName : mTexNames) {
sRE->deleteTextures(1, &texName);
}
+ for (uint32_t texName : mTexNamesCM) {
+ sRECM->deleteTextures(1, &texName);
+ }
}
void writeBufferToFile(const char* basename) {
@@ -250,9 +258,13 @@
void invokeDraw(renderengine::DisplaySettings settings,
std::vector<const renderengine::LayerSettings*> layers,
- sp<GraphicBuffer> buffer) {
+ sp<GraphicBuffer> buffer,
+ bool useColorManagement = false) {
base::unique_fd fence;
- status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
+ status_t status = useColorManagement ?
+ sRECM ->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
+ base::unique_fd(), &fence) :
+ sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
base::unique_fd(), &fence);
sCurrentBuffer = buffer;
@@ -264,7 +276,11 @@
ASSERT_EQ(NO_ERROR, status);
if (layers.size() > 0) {
- ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ if (useColorManagement) {
+ ASSERT_TRUE(sRECM->isFramebufferImageCachedForTesting(buffer->getId()));
+ } else {
+ ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ }
}
}
@@ -319,12 +335,15 @@
void fillBufferLayerTransform();
template <typename SourceVariant>
- void fillBufferWithColorTransform();
+ void fillBufferWithColorTransform(bool useColorManagement = false);
template <typename SourceVariant>
void fillBufferColorTransform();
template <typename SourceVariant>
+ void fillBufferColorTransformCM();
+
+ template <typename SourceVariant>
void fillRedBufferWithRoundedCorners();
template <typename SourceVariant>
@@ -363,6 +382,8 @@
// For now, exercise the GL backend directly so that some caching specifics
// can be tested without changing the interface.
static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
+ // renderengine object with Color Management enabled
+ static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRECM;
// Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
// be freed *after* RenderEngine is destroyed, so that the EGL image is
// destroyed first.
@@ -371,14 +392,17 @@
sp<GraphicBuffer> mBuffer;
std::vector<uint32_t> mTexNames;
+ std::vector<uint32_t> mTexNamesCM;
};
std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
+std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRECM = nullptr;
+
sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
struct ColorSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
- RenderEngineTest* /*fixture*/) {
+ RenderEngineTest* /*fixture*/, bool /*useColorManagement*/ = false) {
layer.source.solidColor = half3(r, g, b);
}
};
@@ -406,11 +430,17 @@
template <typename OpaquenessVariant>
struct BufferSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
- RenderEngineTest* fixture) {
+ RenderEngineTest* fixture,
+ bool useColorManagement = false) {
sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
uint32_t texName;
- fixture->sRE->genTextures(1, &texName);
- fixture->mTexNames.push_back(texName);
+ if (useColorManagement) {
+ fixture->sRECM->genTextures(1, &texName);
+ fixture->mTexNamesCM.push_back(texName);
+ } else {
+ fixture->sRE->genTextures(1, &texName);
+ fixture->mTexNames.push_back(texName);
+ }
uint8_t* pixels;
buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -640,7 +670,7 @@
}
template <typename SourceVariant>
-void RenderEngineTest::fillBufferWithColorTransform() {
+void RenderEngineTest::fillBufferWithColorTransform(bool useColorManagement) {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = Rect(1, 1);
@@ -649,12 +679,12 @@
renderengine::LayerSettings layer;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this, useColorManagement);
layer.alpha = 1.0f;
// construct a fake color matrix
// annihilate green and blue channels
- settings.colorTransform = mat4::scale(vec4(1, 0, 0, 1));
+ settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
// set red channel to red + green
layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
@@ -663,13 +693,19 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers, mBuffer, useColorManagement);
}
template <typename SourceVariant>
void RenderEngineTest::fillBufferColorTransform() {
fillBufferWithColorTransform<SourceVariant>();
- expectBufferColor(fullscreenRect(), 191, 0, 0, 255);
+ expectBufferColor(fullscreenRect(), 172, 0, 0, 255, 1);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformCM() {
+ fillBufferWithColorTransform<SourceVariant>(true);
+ expectBufferColor(fullscreenRect(), 126, 0, 0, 255, 1);
}
template <typename SourceVariant>
@@ -1072,7 +1108,11 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
- fillBufferLayerTransform<ColorSourceVariant>();
+ fillBufferColorTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_colorSource) {
+ fillBufferColorTransformCM<ColorSourceVariant>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
@@ -1128,7 +1168,11 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
- fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+ fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_opaqueBufferSource) {
+ fillBufferColorTransformCM<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
@@ -1184,7 +1228,11 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
- fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+ fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_bufferSource) {
+ fillBufferColorTransformCM<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 1ee8c71..47eb59f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -72,7 +72,7 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.allocator@4.0",
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.1",
@@ -89,7 +89,7 @@
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libgralloctypes",
],
diff --git a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
index afde5f7..f5c5a5a 100644
--- a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
+++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
@@ -52,7 +52,7 @@
// Fuzzer for Serialization operations, this is mostly just lifted from the
// existing test cases to use fuzzed values as inputs.
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+void FuzzSerializeDeserialize(const uint8_t* data, size_t size) {
FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
Payload result;
@@ -106,6 +106,183 @@
Deserialize(&vec_val, &result);
Serialize(t1_val, &result);
Deserialize(&t1_val, &result);
+}
+
+void FuzzDeserializeUint8(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT8, fdp.ConsumeIntegral<uint8_t>()};
+ std::uint8_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint16(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT16, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint16_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint32_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {
+ ENCODING_TYPE_UINT64, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint64_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt8(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT8, fdp.ConsumeIntegral<uint8_t>()};
+ std::int8_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt16(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT16, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int16_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int32_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT64,
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int64_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFloat32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_FLOAT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ float floatResult;
+ Deserialize(&floatResult, &buffer);
+
+ buffer.Rewind();
+ double doubleResult;
+ Deserialize(&doubleResult, &buffer);
+}
+
+void FuzzDeserializeFloat64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {
+ ENCODING_TYPE_FLOAT64, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ double result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixstr(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ std::string s_val = fdp.ConsumeRemainingBytesAsString();
+ Payload buffer = {ENCODING_TYPE_FIXSTR_MAX};
+ for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+ iter++) {
+ buffer.Append(1, *iter);
+ }
+ std::string result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixmap(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_FIXMAP_MAX};
+ // Fill the map with the fuzzed data, not attempting to
+ // make a valid map
+ while (fdp.remaining_bytes() > 0) {
+ buffer.Append(1, fdp.ConsumeIntegral<uint8_t>());
+ }
+
+ std::map<std::uint32_t, std::uint32_t> result;
+ Deserialize(&result, &buffer);
+
+ buffer.Rewind();
+ std::unordered_map<std::uint32_t, std::uint32_t> unorderedResult;
+ Deserialize(&unorderedResult, &buffer);
+}
+
+void FuzzDeserializeVariant(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT16,
+ ENCODING_TYPE_FLOAT32,
+ ENCODING_TYPE_FIXSTR_MAX,
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ // Add the rest of the data as a string
+ std::string s_val = fdp.ConsumeRemainingBytesAsString();
+ for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+ iter++) {
+ buffer.Append(1, *iter);
+ }
+ Variant<int, float, std::string> result;
+ Deserialize(&result, &buffer);
+}
+
+// Attempts to deserialize fuzzed data as various types
+void FuzzDeserialize(const uint8_t* data, size_t size) {
+ FuzzDeserializeUint8(data, size);
+ FuzzDeserializeUint16(data, size);
+ FuzzDeserializeUint32(data, size);
+ FuzzDeserializeUint64(data, size);
+ FuzzDeserializeInt8(data, size);
+ FuzzDeserializeInt16(data, size);
+ FuzzDeserializeInt32(data, size);
+ FuzzDeserializeInt64(data, size);
+ FuzzDeserializeFloat32(data, size);
+ FuzzDeserializeFloat64(data, size);
+ FuzzDeserializeFixstr(data, size);
+ FuzzDeserializeFixmap(data, size);
+ FuzzDeserializeVariant(data, size);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzSerializeDeserialize(data, size);
+ FuzzDeserialize(data, size);
return 0;
}
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 9ca8b0b..393ced7 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -53,25 +53,25 @@
}
llndk_library {
- name: "libEGL",
+ name: "libEGL.llndk",
symbol_file: "libs/libEGL.map.txt",
export_include_dirs: ["include"],
}
llndk_library {
- name: "libGLESv1_CM",
+ name: "libGLESv1_CM.llndk",
symbol_file: "libs/libGLESv1_CM.map.txt",
export_include_dirs: ["include"],
}
llndk_library {
- name: "libGLESv2",
+ name: "libGLESv2.llndk",
symbol_file: "libs/libGLESv2.map.txt",
export_include_dirs: ["include"],
}
llndk_library {
- name: "libGLESv3",
+ name: "libGLESv3.llndk",
symbol_file: "libs/libGLESv3.map.txt",
export_include_dirs: ["include"],
}
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index e8d3684..e7c2e94 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -133,6 +133,7 @@
cc_library_shared {
name: "libEGL",
defaults: ["egl_libs_defaults"],
+ llndk_stubs: "libEGL.llndk",
srcs: [
"EGL/egl_tls.cpp",
"EGL/egl_cache.cpp",
@@ -198,6 +199,7 @@
cc_library_shared {
name: "libGLESv1_CM",
defaults: ["gles_libs_defaults"],
+ llndk_stubs: "libGLESv1_CM.llndk",
srcs: ["GLES_CM/gl.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv1\""],
version_script: "libGLESv1_CM.map.txt",
@@ -209,6 +211,7 @@
cc_library_shared {
name: "libGLESv2",
defaults: ["gles_libs_defaults"],
+ llndk_stubs: "libGLESv2.llndk",
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv2\""],
@@ -223,6 +226,7 @@
cc_library_shared {
name: "libGLESv3",
defaults: ["gles_libs_defaults"],
+ llndk_stubs: "libGLESv3.llndk",
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv3\""],
}
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index 97dc0f1..f82c2a4 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -29,9 +29,13 @@
#include <graphicsenv/GraphicsEnv.h>
#include <time.h>
#include <log/log.h>
+#include <vndksupport/linker.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"));
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index 9c80212..59ef3c8 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -15,6 +15,7 @@
*/
import java.io.PrintStream;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -201,6 +202,33 @@
out.println(iii + ");");
}
+ // Function to automatically generate properly formatted function calls that
+ // comply with clang format rules
+ public static String formatFunctionCall(String indent, String functionCall) {
+ final int MAXLEN = 100;
+ String tokens[] = functionCall.split("\\(|\\)", 2);
+ String params[] = tokens[1].split(",\\s*");
+ String formatted = indent + tokens[0] + "(";
+ char[] chars = new char[indent.length() + tokens[0].length() + 1];
+ Arrays.fill(chars, ' ');
+ String multiIndent = new String(chars);
+ ArrayList<String> lines = new ArrayList<String>();
+ for(int i = 0; i < params.length; i++) {
+ String terminator = ((i == params.length - 1) ? "" : ",");
+ if(indent.length() + formatted.length() + params[i].length() > MAXLEN) {
+ lines.add(formatted);
+ if (!indent.equals(multiIndent)) {
+ indent = multiIndent;
+ }
+ formatted = indent + params[i] + terminator;
+ } else {
+ formatted += (i == 0 ? "" : " ") + params[i] + terminator;
+ }
+ }
+ lines.add(formatted);
+ return String.join("\n", lines);
+ }
+
void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
String iii) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
@@ -1538,14 +1566,19 @@
"_exception ? JNI_ABORT : 0" : "0")) +
");");
} else {
- out.println(indent + indent +
+ String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
+ "_" + cfunc.getArgName(cIndex) + "BufferOffset";
+ String typeCast = "(char *)" + cfunc.getArgName(cIndex);
+ String withOffset = "(void *)(" + typeCast + " - " + bufferOffset + ")";
+ String releasePointerCall = (
"releasePointer(_env, " + array + ", " +
- cfunc.getArgName(cIndex) +
+ withOffset +
", " +
(cfunc.getArgType(cIndex).isConst() ?
"JNI_FALSE" : (emitExceptionCheck ?
"_exception ? JNI_FALSE : JNI_TRUE" : "JNI_TRUE")) +
");");
+ out.println(formatFunctionCall(indent + indent, releasePointerCall));
}
out.println(indent + "}");
}
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 4da6db3..9a9bca1 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..3aa862f
--- /dev/null
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -0,0 +1,157 @@
+/*
+ * 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();
+
+ errno = 0;
+ int fd = bpf::retrieveProgram(kGpuMemTotalProgPath);
+ if (fd < 0) {
+ ALOGE("Failed to retrieve pinned program from %s [%d(%s)]", kGpuMemTotalProgPath, errno,
+ strerror(errno));
+ return;
+ }
+
+ // Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
+ errno = 0;
+ int count = 0;
+ while (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
+ if (++count > kGpuWaitTimeout) {
+ ALOGE("Failed to attach bpf program to %s/%s tracepoint [%d(%s)]", kGpuMemTraceGroup,
+ kGpuMemTotalTracepoint, errno, strerror(errno));
+ return;
+ }
+ // Retry until GPU driver loaded or timeout.
+ sleep(1);
+ }
+
+ // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
+ errno = 0;
+ auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath);
+ if (!map.isValid()) {
+ ALOGE("Failed to create bpf map from %s [%d(%s)]", kGpuMemTotalMapPath, errno,
+ strerror(errno));
+ 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/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 538506d..940a26b 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",
@@ -33,4 +38,5 @@
static_libs: [
"libgmock",
],
+ require_root: true,
}
diff --git a/services/gpuservice/tests/unittests/AndroidTest.xml b/services/gpuservice/tests/unittests/AndroidTest.xml
index 66f51c7..72976a8 100644
--- a/services/gpuservice/tests/unittests/AndroidTest.xml
+++ b/services/gpuservice/tests/unittests/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="cleanup" value="true" />
<option name="push" value="gpuservice_unittest->/data/local/tmp/gpuservice_unittest" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
new file mode 100644
index 0000000..c5f8859
--- /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;
+ 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/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index fe016af..2517060 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -482,6 +482,33 @@
}
/**
+ * Raise ANR if there is no focused window.
+ * Before the ANR is raised, do a final state check:
+ * 1. The currently focused application must be the same one we are waiting for.
+ * 2. Ensure we still don't have a focused window.
+ */
+void InputDispatcher::processNoFocusedWindowAnrLocked() {
+ // Check if the application that we are waiting for is still focused.
+ sp<InputApplicationHandle> focusedApplication =
+ getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
+ if (focusedApplication == nullptr ||
+ focusedApplication->getApplicationToken() !=
+ mAwaitedFocusedApplication->getApplicationToken()) {
+ // Unexpected because we should have reset the ANR timer when focused application changed
+ ALOGE("Waited for a focused window, but focused application has already changed to %s",
+ focusedApplication->getName().c_str());
+ return; // The focused application has changed.
+ }
+
+ const sp<InputWindowHandle>& focusedWindowHandle =
+ getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
+ if (focusedWindowHandle != nullptr) {
+ return; // We now have a focused window. No need for ANR.
+ }
+ onAnrLocked(mAwaitedFocusedApplication);
+}
+
+/**
* Check if any of the connections' wait queues have events that are too old.
* If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
* Return the time at which we should wake up next.
@@ -492,8 +519,9 @@
// Check if we are waiting for a focused window to appear. Raise ANR if waited too long
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
if (currentTime >= *mNoFocusedWindowTimeoutTime) {
- onAnrLocked(mAwaitedFocusedApplication);
+ processNoFocusedWindowAnrLocked();
mAwaitedFocusedApplication.clear();
+ mNoFocusedWindowTimeoutTime = std::nullopt;
return LONG_LONG_MIN;
} else {
// Keep waiting
@@ -518,7 +546,7 @@
connection->responsive = false;
// Stop waking up for this unresponsive connection
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
- onAnrLocked(connection);
+ onAnrLocked(*connection);
return LONG_LONG_MIN;
}
@@ -1477,6 +1505,7 @@
DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
mNoFocusedWindowTimeoutTime = currentTime + timeout;
mAwaitedFocusedApplication = focusedApplicationHandle;
+ mAwaitedApplicationDisplayId = displayId;
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout));
@@ -3567,6 +3596,10 @@
return getValueByKey(mWindowHandlesByDisplay, displayId);
}
+sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+ return getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+}
+
sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
const sp<IBinder>& windowHandleToken) const {
if (windowHandleToken == nullptr) {
@@ -4542,12 +4575,12 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+void InputDispatcher::onAnrLocked(const Connection& connection) {
// Since we are allowing the policy to extend the timeout, maybe the waitQueue
// is already healthy again. Don't raise ANR in this situation
- if (connection->waitQueue.empty()) {
+ if (connection.waitQueue.empty()) {
ALOGI("Not raising ANR because the connection %s has recovered",
- connection->inputChannel->getName().c_str());
+ connection.inputChannel->getName().c_str());
return;
}
/**
@@ -4558,21 +4591,21 @@
* processes the events linearly. So providing information about the oldest entry seems to be
* most useful.
*/
- DispatchEntry* oldestEntry = *connection->waitQueue.begin();
+ DispatchEntry* oldestEntry = *connection.waitQueue.begin();
const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
std::string reason =
android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
- connection->inputChannel->getName().c_str(),
+ connection.inputChannel->getName().c_str(),
ns2ms(currentWait),
oldestEntry->eventEntry->getDescription().c_str());
- updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()),
+ updateLastAnrStateLocked(getWindowHandleLocked(connection.inputChannel->getConnectionToken()),
reason);
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = nullptr;
- commandEntry->inputChannel = connection->inputChannel;
+ commandEntry->inputChannel = connection.inputChannel;
commandEntry->reason = std::move(reason);
postCommandLocked(std::move(commandEntry));
}
@@ -4672,15 +4705,15 @@
void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
const sp<IBinder>& connectionToken,
nsecs_t timeoutExtension) {
+ if (connectionToken == nullptr && application != nullptr) {
+ // The ANR happened because there's no focused window
+ mNoFocusedWindowTimeoutTime = now() + timeoutExtension;
+ mAwaitedFocusedApplication = application;
+ }
+
sp<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
- if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) {
- // Maybe ANR happened because there's no focused window?
- mNoFocusedWindowTimeoutTime = now() + timeoutExtension;
- mAwaitedFocusedApplication = application;
- } else {
- // It's also possible that the connection already disappeared. No action necessary.
- }
+ // It's possible that the connection already disappeared. No action necessary.
return;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e679c6b..31fad1f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -300,6 +300,7 @@
REQUIRES(mLock);
sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
REQUIRES(mLock);
+ sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
@@ -366,6 +367,11 @@
* Used to raise an ANR when we have no focused window.
*/
sp<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+ /**
+ * The displayId that the focused application is associated with.
+ */
+ int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock);
+ void processNoFocusedWindowAnrLocked() REQUIRES(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.
@@ -489,7 +495,7 @@
REQUIRES(mLock);
void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
- void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void onAnrLocked(const Connection& connection) REQUIRES(mLock);
void onAnrLocked(const sp<InputApplicationHandle>& application) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h
index 5a32443..7de9b830 100644
--- a/services/inputflinger/reader/include/TouchVideoDevice.h
+++ b/services/inputflinger/reader/include/TouchVideoDevice.h
@@ -102,7 +102,7 @@
* How many buffers to keep for the internal queue. When the internal buffer
* exceeds this capacity, oldest frames will be dropped.
*/
- static constexpr size_t MAX_QUEUE_SIZE = 10;
+ static constexpr size_t MAX_QUEUE_SIZE = 20;
std::vector<TouchVideoFrame> mFrames;
/**
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 1a4d551..55c1a1d 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -80,6 +80,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);
@@ -312,7 +316,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 +340,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/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/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..cfc2919 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,7 +165,7 @@
TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
: InputMapper(deviceContext),
mSource(0),
- mDeviceMode(DEVICE_MODE_DISABLED),
+ mDeviceMode(DeviceMode::DISABLED),
mRawSurfaceWidth(-1),
mRawSurfaceHeight(-1),
mSurfaceLeft(0),
@@ -179,11 +185,25 @@
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);
+ if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) {
+ // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode.
+ //
+ // RELATIVE_X and RELATIVE_Y motion ranges should be the largest possible relative
+ // motion, i.e. the hardware dimensions, as the finger could move completely across the
+ // touchpad in one sample cycle.
+ const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
+ const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
+ info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat,
+ x.fuzz, x.resolution);
+ info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat,
+ y.fuzz, y.resolution);
+ }
+
if (mOrientedRanges.haveSize) {
info->addMotionRange(mOrientedRanges.size);
}
@@ -218,7 +238,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 +304,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 +333,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 +345,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 +397,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 +429,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 +446,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 +466,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 +511,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 +522,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 +581,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) {
@@ -610,34 +633,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 +668,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 +679,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 +691,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 +782,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 +821,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 +870,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 +924,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 +951,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 +1014,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 +1135,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 +1158,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 +1176,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 +1192,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 +1207,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 +1224,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 +1269,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 +1303,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 +1322,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 +1337,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 +1352,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 +1393,7 @@
mCurrentCookedState.clear();
mLastRawState.clear();
mLastCookedState.clear();
- mPointerUsage = POINTER_USAGE_NONE;
+ mPointerUsage = PointerUsage::NONE;
mSentHoverEnter = false;
mHavePointerIds = false;
mCurrentMotionAborted = false;
@@ -1442,17 +1465,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 +1541,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 +1570,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 +1600,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 +1651,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 +1678,7 @@
}
bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
- if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) {
+ if (mDeviceMode != DeviceMode::DIRECT || !hasExternalStylus()) {
return false;
}
@@ -1697,11 +1721,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 +1794,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 +1915,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 +2049,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 +2066,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 +2111,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 +2144,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 +2165,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 +2192,7 @@
// Distance
float distance;
switch (mCalibration.distanceCalibration) {
- case Calibration::DISTANCE_CALIBRATION_SCALED:
+ case Calibration::DistanceCalibration::SCALED:
distance = in.distance * mDistanceScale;
break;
default:
@@ -2174,7 +2202,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 +2279,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 +2289,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 +2320,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 +2365,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 +2383,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 +2418,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 +2506,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 +2592,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 +2605,7 @@
*outFinishPreviousGesture = true;
mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
mPointerGesture.currentGestureIdBits.clear();
mPointerVelocityControl.reset();
@@ -2598,9 +2637,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 +2672,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 +2701,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 +2730,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 +2784,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 +2797,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 +2821,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 +2864,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 +2874,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 +2894,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 +2918,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 +2927,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 +2973,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 +2993,7 @@
}
if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
- mPointerGesture.currentGestureMode = PointerGesture::PRESS;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS;
mPointerGesture.activeGestureId = 0;
mPointerGesture.referenceIdBits.clear();
mPointerVelocityControl.reset();
@@ -3005,7 +3045,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 +3067,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 +3086,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 +3117,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 +3129,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 +3144,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 +3173,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 +3195,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 +3208,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 +3600,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 +3612,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);
@@ -3907,7 +3951,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/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 1a133dc..668a7ab 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -150,6 +150,7 @@
const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
ADD_FAILURE() << "Did not receive ANR callback";
+ return {};
}
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
// the dispatcher started counting before this function was called
@@ -3031,4 +3032,64 @@
mFocusedWindow->assertNoEvents();
}
+/**
+ * If we have no focused window, and a key comes in, we start the ANR timer.
+ * The focused application should add a focused window before the timer runs out to prevent ANR.
+ *
+ * If the user touches another application during this time, the key should be dropped.
+ * Next, if a new focused window comes in, without toggling the focused application,
+ * then no ANR should occur.
+ *
+ * Normally, we would expect the new focused window to be accompanied by 'setFocusedApplication',
+ * but in some cases the policy may not update the focused application.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
+ sp<FakeApplicationHandle> focusedApplication = new FakeApplicationHandle();
+ focusedApplication->setDispatchingTimeout(60ms);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
+ // The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
+ mFocusedWindow->setFocus(false);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mFocusedWindow->consumeFocusEvent(false);
+
+ // Send a key. The ANR timer should start because there is no focused window.
+ // 'focusedApplication' will get blamed if this timer completes.
+ // Key will not be sent anywhere because we have no focused window. It will remain pending.
+ int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+
+ // Wait until dispatcher starts the "no focused window" timer. If we don't wait here,
+ // then the injected touches won't cause the focused event to get dropped.
+ // The dispatcher only checks for whether the queue should be pruned upon queueing.
+ // If we inject the touch right away and the ANR timer hasn't started, the touch event would
+ // simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'.
+ // For this test, it means that the key would get delivered to the window once it becomes
+ // focused.
+ std::this_thread::sleep_for(10ms);
+
+ // Touch unfocused window. This should force the pending key to get dropped.
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {UNFOCUSED_WINDOW_LOCATION});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // We do not consume the motion right away, because that would require dispatcher to first
+ // process (== drop) the key event, and by that time, ANR will be raised.
+ // Set the focused window first.
+ mFocusedWindow->setFocus(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mFocusedWindow->consumeFocusEvent(true);
+ // We do not call "setFocusedApplication" here, even though the newly focused window belongs
+ // to another application. This could be a bug / behaviour in the policy.
+
+ mUnfocusedWindow->consumeMotionDown();
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // Should not ANR because we actually have a focused window. It was just added too slowly.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled());
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 18bd3d0..58f83b5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -45,16 +45,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 +297,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,
@@ -1873,10 +1883,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 {
@@ -1947,9 +1953,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 +1970,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 +1985,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 ---
@@ -7059,10 +7072,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 +7085,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 +7099,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 +7120,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 +7132,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 +7141,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 ---
@@ -7338,4 +7531,223 @@
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());
+
+ InputDeviceInfo deviceInfo;
+ mDevice->getDeviceInfo(&deviceInfo);
+
+ const InputDeviceInfo::MotionRange* relRangeX =
+ deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(relRangeX, nullptr);
+ ASSERT_EQ(relRangeX->min, -(RAW_X_MAX - RAW_X_MIN));
+ ASSERT_EQ(relRangeX->max, RAW_X_MAX - RAW_X_MIN);
+ const InputDeviceInfo::MotionRange* relRangeY =
+ deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(relRangeY, nullptr);
+ ASSERT_EQ(relRangeY->min, -(RAW_Y_MAX - RAW_Y_MIN));
+ ASSERT_EQ(relRangeY->max, RAW_Y_MAX - RAW_Y_MIN);
+
+ // run captured pointer tests - note that this is unscaled, so input listener events should be
+ // identical to what the hardware sends (accounting for any
+ // calibration).
+ // 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/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h
index da2195c..40dd841 100644
--- a/services/surfaceflinger/Scheduler/TimeKeeper.h
+++ b/services/surfaceflinger/Scheduler/TimeKeeper.h
@@ -43,10 +43,10 @@
virtual ~TimeKeeper();
/*
- * Arms callback to fired in time nanoseconds.
+ * Arms callback to fired when time is current based on CLOCK_MONOTONIC
* There is only one timer, and subsequent calls will reset the callback function and the time.
*/
- virtual void alarmIn(std::function<void()> const& callback, nsecs_t time) = 0;
+ virtual void alarmAt(std::function<void()> const& callback, nsecs_t time) = 0;
/*
* Cancels an existing pending callback
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index 7c5058e..59c336a 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -88,7 +88,7 @@
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
+void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) {
std::lock_guard<decltype(mMutex)> lk(mMutex);
using namespace std::literals;
static constexpr int ns_per_s =
@@ -99,11 +99,11 @@
struct itimerspec old_timer;
struct itimerspec new_timer {
.it_interval = {.tv_sec = 0, .tv_nsec = 0},
- .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
- .tv_nsec = static_cast<long>(fireIn % ns_per_s)},
+ .it_value = {.tv_sec = static_cast<long>(time / ns_per_s),
+ .tv_nsec = static_cast<long>(time % ns_per_s)},
};
- if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+ if (timerfd_settime(mTimerFd, TFD_TIMER_ABSTIME, &new_timer, &old_timer)) {
ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
}
}
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/Timer.h
index a8e2d5a..69ce079 100644
--- a/services/surfaceflinger/Scheduler/Timer.h
+++ b/services/surfaceflinger/Scheduler/Timer.h
@@ -30,9 +30,9 @@
~Timer();
nsecs_t now() const final;
- // NB: alarmIn and alarmCancel are threadsafe; with the last-returning function being effectual
+ // NB: alarmAt and alarmCancel are threadsafe; with the last-returning function being effectual
// Most users will want to serialize thes calls so as to be aware of the timer state.
- void alarmIn(std::function<void()> const& cb, nsecs_t fireIn) final;
+ void alarmAt(std::function<void()> const& cb, nsecs_t time) final;
void alarmCancel() final;
void dump(std::string& result) const final;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index a596bce..ef92680 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -180,10 +180,10 @@
mTimeKeeper->alarmCancel();
}
-void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
+void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
mIntendedWakeupTime = targetTime;
- mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
- targetTime - now);
+ mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
+ mIntendedWakeupTime);
mLastTimerSchedule = mTimeKeeper->now();
}
@@ -243,7 +243,8 @@
std::vector<Invocation> invocations;
{
std::lock_guard<decltype(mMutex)> lk(mMutex);
- mLastTimerCallback = mTimeKeeper->now();
+ auto const now = mTimeKeeper->now();
+ mLastTimerCallback = now;
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
auto& callback = it->second;
auto const wakeupTime = callback->wakeupTime();
@@ -251,7 +252,8 @@
continue;
}
- if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) {
+ auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
+ if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
callback->executing();
invocations.emplace_back(
Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime});
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1342cfc..9d65f2f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2122,7 +2122,8 @@
mTimeStats->incrementCompositionStrategyChanges();
}
- mVSyncModulator->onRefreshed(mHadClientComposition);
+ // TODO: b/160583065 Enable skip validation when SF caches all client composition layers
+ mVSyncModulator->onRefreshed(mHadClientComposition || mReusedClientComposition);
mLayersWithQueuedFrames.clear();
if (mVisibleRegionsDirty) {
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index fe2af80..1532855 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -44,7 +44,7 @@
"libtrace_proto",
],
shared_libs: [
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.composer@2.1",
"libandroid",
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 2861013..3535fbb 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -8,12 +8,12 @@
"FakeComposerUtils.cpp",
"SFFakeHwc_test.cpp"
],
+ require_root: true,
shared_libs: [
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "android.hardware.graphics.composer@2.1-resources",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
@@ -33,6 +33,7 @@
"libutils",
],
static_libs: [
+ "android.hardware.graphics.composer@2.1-resources",
"libcompositionengine",
"libgmock",
"libperfetto_client_experimental",
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 3c4a791..1bbc3f8 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -58,6 +58,7 @@
"RegionSamplingTest.cpp",
"TimeStatsTest.cpp",
"FrameTracerTest.cpp",
+ "TimerTest.cpp",
"TransactionApplicationTest.cpp",
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/TimerTest.cpp b/services/surfaceflinger/tests/unittests/TimerTest.cpp
new file mode 100644
index 0000000..cda6bbf
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TimerTest.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AsyncCallRecorder.h"
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/Timer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace testing;
+using namespace std::literals;
+
+namespace android::scheduler {
+
+struct TimerTest : testing::Test {
+ static constexpr int mIterations = 20;
+
+ AsyncCallRecorder<void (*)()> mCallbackRecorder;
+ Timer mTimer;
+
+ void timerCallback() { mCallbackRecorder.recordCall(); }
+};
+
+TEST_F(TimerTest, callsCallbackIfScheduledInPast) {
+ for (int i = 0; i < mIterations; i++) {
+ mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 10'000'00);
+ EXPECT_TRUE(mCallbackRecorder.waitForCall().has_value());
+ EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value());
+ }
+}
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index d940dc5..fa5cc32 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -63,19 +63,19 @@
class ControllableClock : public TimeKeeper {
public:
ControllableClock() {
- ON_CALL(*this, alarmIn(_, _))
- .WillByDefault(Invoke(this, &ControllableClock::alarmInDefaultBehavior));
+ ON_CALL(*this, alarmAt(_, _))
+ .WillByDefault(Invoke(this, &ControllableClock::alarmAtDefaultBehavior));
ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime));
}
MOCK_CONST_METHOD0(now, nsecs_t());
- MOCK_METHOD2(alarmIn, void(std::function<void()> const&, nsecs_t time));
+ MOCK_METHOD2(alarmAt, void(std::function<void()> const&, nsecs_t time));
MOCK_METHOD0(alarmCancel, void());
MOCK_CONST_METHOD1(dump, void(std::string&));
- void alarmInDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
+ void alarmAtDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
mCallback = callback;
- mNextCallbackTime = time + mCurrentTime;
+ mNextCallbackTime = time;
}
nsecs_t fakeTime() const { return mCurrentTime; }
@@ -115,11 +115,15 @@
operator VSyncDispatch::CallbackToken() const { return mToken; }
- void counter(nsecs_t time, nsecs_t) { mCalls.push_back(time); }
+ void counter(nsecs_t time, nsecs_t wakeup_time) {
+ mCalls.push_back(time);
+ mWakeupTime.push_back(wakeup_time);
+ }
VSyncDispatch& mDispatch;
VSyncDispatch::CallbackToken mToken;
std::vector<nsecs_t> mCalls;
+ std::vector<nsecs_t> mWakeupTime;
};
class PausingCallback {
@@ -187,8 +191,8 @@
class TimeKeeperWrapper : public TimeKeeper {
public:
TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {}
- void alarmIn(std::function<void()> const& callback, nsecs_t time) final {
- mControllableClock.alarmIn(callback, time);
+ void alarmAt(std::function<void()> const& callback, nsecs_t time) final {
+ mControllableClock.alarmAt(callback, time);
}
void alarmCancel() final { mControllableClock.alarmCancel(); }
nsecs_t now() const final { return mControllableClock.now(); }
@@ -217,7 +221,7 @@
};
TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
{
VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
@@ -229,7 +233,7 @@
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) {
auto intended = mPeriod - 230;
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb, 100, intended), ScheduleResult::Scheduled);
@@ -241,7 +245,7 @@
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
- EXPECT_CALL(mMockClock, alarmIn(_, 1050));
+ EXPECT_CALL(mMockClock, alarmAt(_, 1050));
CountingCallback cb(mDispatch);
mDispatch.schedule(cb, 100, mPeriod);
@@ -257,14 +261,14 @@
auto const workDuration = 10 * mPeriod;
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration))
.WillOnce(Return(mPeriod * 11));
- EXPECT_CALL(mMockClock, alarmIn(_, mPeriod - now));
+ EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
CountingCallback cb(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
@@ -273,7 +277,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
@@ -283,7 +287,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
@@ -297,7 +301,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
auto resource = std::make_shared<int>(110);
@@ -327,9 +331,9 @@
.WillOnce(Return(1075));
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 955)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 813)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 162)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 955)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 813)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 975)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
@@ -355,9 +359,9 @@
.WillOnce(Return(10000));
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 750)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 9900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 750)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 9900)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
@@ -369,8 +373,8 @@
TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
@@ -383,9 +387,9 @@
TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
@@ -398,10 +402,10 @@
TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 990)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 10)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1590)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1600)).InSequence(seq);
auto offset = 400;
auto closeOffset = offset + mDispatchGroupThreshold - 1;
@@ -432,9 +436,10 @@
}
TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
- EXPECT_CALL(mMockClock, alarmIn(_, 800));
- EXPECT_CALL(mMockClock, alarmIn(_, 100));
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 800)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb0(mDispatch);
@@ -472,8 +477,8 @@
TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
VSyncDispatch::CallbackToken tmp;
tmp = mDispatch.registerCallback([&](auto, auto) { mDispatch.schedule(tmp, 100, 2000); },
@@ -507,10 +512,10 @@
TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 200)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 150)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1000)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 950)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1950)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
CountingCallback cb(mDispatch);
mDispatch.schedule(cb, 0, 1000);
@@ -527,10 +532,10 @@
TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 400)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 350)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 950)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 850)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1800)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
@@ -548,7 +553,7 @@
TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
@@ -558,9 +563,9 @@
TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
CountingCallback cb0(mDispatch);
mDispatch.schedule(cb0, 500, 1000);
@@ -582,7 +587,7 @@
// b/1450138150
TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
- EXPECT_CALL(mMockClock, alarmIn(_, 500));
+ EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
mMockClock.advanceBy(400);
@@ -612,8 +617,8 @@
TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
CountingCallback cb0(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
advanceToNextCallback();
@@ -621,7 +626,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
- EXPECT_CALL(mMockClock, alarmIn(_, 600));
+ EXPECT_CALL(mMockClock, alarmAt(_, 600));
CountingCallback cb(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
@@ -632,7 +637,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1);
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
VSyncCallbackRegistration cb(
@@ -646,7 +651,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) {
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1);
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
VSyncCallbackRegistration cb(
@@ -664,8 +669,8 @@
// b/154303580
TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 1200)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
@@ -686,8 +691,8 @@
// update later, as opposed to blocking the calling thread.
TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 930)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq);
CountingCallback cb(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
@@ -704,7 +709,7 @@
// b/154303580.
TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
@@ -725,8 +730,8 @@
TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 1280)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
@@ -747,6 +752,31 @@
EXPECT_THAT(cb2.mCalls.size(), Eq(1));
}
+TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) {
+ CountingCallback cb1(mDispatch);
+ CountingCallback cb2(mDispatch);
+
+ Sequence seq;
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ .InSequence(seq)
+ .WillOnce(Return(1000));
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ .InSequence(seq)
+ .WillOnce(Return(1000));
+
+ EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2, 390, 1000), ScheduleResult::Scheduled);
+
+ mMockClock.setLag(100);
+ mMockClock.advanceBy(700);
+
+ ASSERT_THAT(cb1.mWakeupTime.size(), Eq(1));
+ EXPECT_THAT(cb1.mWakeupTime[0], Eq(600));
+ ASSERT_THAT(cb2.mWakeupTime.size(), Eq(1));
+ EXPECT_THAT(cb2.mWakeupTime[0], Eq(610));
+}
+
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
protected:
nsecs_t const mPeriod = 1000;
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index f69de1f..921b095 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -21,7 +21,7 @@
}
llndk_library {
- name: "libvulkan",
+ name: "libvulkan.llndk",
symbol_file: "libvulkan.map.txt",
export_llndk_headers: [
"vulkan_headers_llndk",
@@ -30,6 +30,7 @@
cc_library_shared {
name: "libvulkan",
+ llndk_stubs: "libvulkan.llndk",
clang: true,
sanitize: {
misc_undefined: ["integer"],
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index d9a9427..26052fb 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -226,6 +226,7 @@
INIT_PROC(true, dev, CreateQueryPool);
INIT_PROC(true, dev, DestroyQueryPool);
INIT_PROC(true, dev, GetQueryPoolResults);
+ INIT_PROC(false, dev, ResetQueryPool);
INIT_PROC(true, dev, CreateBuffer);
INIT_PROC(true, dev, DestroyBuffer);
INIT_PROC(true, dev, CreateBufferView);
@@ -337,8 +338,20 @@
INIT_PROC(false, dev, DestroySamplerYcbcrConversion);
INIT_PROC(false, dev, GetDeviceQueue2);
INIT_PROC(false, dev, GetDescriptorSetLayoutSupport);
+ INIT_PROC(false, dev, CreateRenderPass2);
+ INIT_PROC(false, dev, CmdBeginRenderPass2);
+ INIT_PROC(false, dev, CmdNextSubpass2);
+ INIT_PROC(false, dev, CmdEndRenderPass2);
+ INIT_PROC(false, dev, GetSemaphoreCounterValue);
+ INIT_PROC(false, dev, WaitSemaphores);
+ INIT_PROC(false, dev, SignalSemaphore);
INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetAndroidHardwareBufferPropertiesANDROID);
INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetMemoryAndroidHardwareBufferANDROID);
+ INIT_PROC(false, dev, CmdDrawIndirectCount);
+ INIT_PROC(false, dev, CmdDrawIndexedIndirectCount);
+ INIT_PROC(false, dev, GetBufferOpaqueCaptureAddress);
+ INIT_PROC(false, dev, GetBufferDeviceAddress);
+ INIT_PROC(false, dev, GetDeviceMemoryOpaqueCaptureAddress);
// clang-format on
return success;
@@ -391,6 +404,7 @@
VKAPI_ATTR VkResult CreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool);
VKAPI_ATTR void DestroyQueryPool(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult GetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags);
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
VKAPI_ATTR void DestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult CreateBufferView(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView);
@@ -520,8 +534,20 @@
VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo);
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue);
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout);
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo);
VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties);
VKAPI_ATTR VkResult GetMemoryAndroidHardwareBufferANDROID(VkDevice device, const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, struct AHardwareBuffer** pBuffer);
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) {
return GetData(instance).dispatch.EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
@@ -551,6 +577,7 @@
"vkEnumerateInstanceVersion",
"vkEnumeratePhysicalDeviceGroups",
"vkEnumeratePhysicalDeviceGroupsKHR",
+ "vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR",
"vkEnumeratePhysicalDevices",
"vkGetDisplayModeProperties2KHR",
"vkGetDisplayPlaneCapabilities2KHR",
@@ -571,7 +598,7 @@
"vkGetPhysicalDeviceFormatProperties",
"vkGetPhysicalDeviceFormatProperties2",
"vkGetPhysicalDeviceFormatProperties2KHR",
- "vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX",
+ "vkGetPhysicalDeviceFragmentShadingRatesKHR",
"vkGetPhysicalDeviceImageFormatProperties",
"vkGetPhysicalDeviceImageFormatProperties2",
"vkGetPhysicalDeviceImageFormatProperties2KHR",
@@ -583,6 +610,7 @@
"vkGetPhysicalDeviceProperties",
"vkGetPhysicalDeviceProperties2",
"vkGetPhysicalDeviceProperties2KHR",
+ "vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR",
"vkGetPhysicalDeviceQueueFamilyProperties",
"vkGetPhysicalDeviceQueueFamilyProperties2",
"vkGetPhysicalDeviceQueueFamilyProperties2KHR",
@@ -595,6 +623,7 @@
"vkGetPhysicalDeviceSurfaceFormatsKHR",
"vkGetPhysicalDeviceSurfacePresentModesKHR",
"vkGetPhysicalDeviceSurfaceSupportKHR",
+ "vkGetPhysicalDeviceToolPropertiesEXT",
"vkSubmitDebugUtilsMessageEXT",
};
// clang-format on
@@ -646,6 +675,7 @@
{ "vkBindImageMemory2", reinterpret_cast<PFN_vkVoidFunction>(BindImageMemory2) },
{ "vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginQuery) },
{ "vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass) },
+ { "vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass2) },
{ "vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(CmdBindDescriptorSets) },
{ "vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdBindIndexBuffer) },
{ "vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(CmdBindPipeline) },
@@ -665,12 +695,16 @@
{ "vkCmdDraw", reinterpret_cast<PFN_vkVoidFunction>(CmdDraw) },
{ "vkCmdDrawIndexed", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexed) },
{ "vkCmdDrawIndexedIndirect", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexedIndirect) },
+ { "vkCmdDrawIndexedIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexedIndirectCount) },
{ "vkCmdDrawIndirect", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndirect) },
+ { "vkCmdDrawIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndirectCount) },
{ "vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdEndQuery) },
{ "vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass) },
+ { "vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass2) },
{ "vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(CmdExecuteCommands) },
{ "vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdFillBuffer) },
{ "vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(CmdNextSubpass) },
+ { "vkCmdNextSubpass2", reinterpret_cast<PFN_vkVoidFunction>(CmdNextSubpass2) },
{ "vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier) },
{ "vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(CmdPushConstants) },
{ "vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent) },
@@ -709,6 +743,7 @@
{ "vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(CreatePipelineLayout) },
{ "vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(CreateQueryPool) },
{ "vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass) },
+ { "vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass2) },
{ "vkCreateSampler", reinterpret_cast<PFN_vkVoidFunction>(CreateSampler) },
{ "vkCreateSamplerYcbcrConversion", reinterpret_cast<PFN_vkVoidFunction>(CreateSamplerYcbcrConversion) },
{ "vkCreateSemaphore", reinterpret_cast<PFN_vkVoidFunction>(CreateSemaphore) },
@@ -749,13 +784,16 @@
{ "vkFreeDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(FreeDescriptorSets) },
{ "vkFreeMemory", reinterpret_cast<PFN_vkVoidFunction>(FreeMemory) },
{ "vkGetAndroidHardwareBufferPropertiesANDROID", reinterpret_cast<PFN_vkVoidFunction>(GetAndroidHardwareBufferPropertiesANDROID) },
+ { "vkGetBufferDeviceAddress", reinterpret_cast<PFN_vkVoidFunction>(GetBufferDeviceAddress) },
{ "vkGetBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetBufferMemoryRequirements) },
{ "vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(GetBufferMemoryRequirements2) },
+ { "vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetBufferOpaqueCaptureAddress) },
{ "vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(GetDescriptorSetLayoutSupport) },
{ "vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPeerMemoryFeatures) },
{ "vkGetDeviceGroupPresentCapabilitiesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPresentCapabilitiesKHR) },
{ "vkGetDeviceGroupSurfacePresentModesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupSurfacePresentModesKHR) },
{ "vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryCommitment) },
+ { "vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryOpaqueCaptureAddress) },
{ "vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr) },
{ "vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue) },
{ "vkGetDeviceQueue2", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue2) },
@@ -771,6 +809,7 @@
{ "vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(GetPipelineCacheData) },
{ "vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(GetQueryPoolResults) },
{ "vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(GetRenderAreaGranularity) },
+ { "vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(GetSemaphoreCounterValue) },
{ "vkGetSwapchainImagesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR) },
{ "vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(InvalidateMappedMemoryRanges) },
{ "vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(MapMemory) },
@@ -784,12 +823,15 @@
{ "vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(ResetDescriptorPool) },
{ "vkResetEvent", reinterpret_cast<PFN_vkVoidFunction>(ResetEvent) },
{ "vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(ResetFences) },
+ { "vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(ResetQueryPool) },
{ "vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(SetEvent) },
+ { "vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(SignalSemaphore) },
{ "vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(TrimCommandPool) },
{ "vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(UnmapMemory) },
{ "vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSetWithTemplate) },
{ "vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSets) },
{ "vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(WaitForFences) },
+ { "vkWaitSemaphores", reinterpret_cast<PFN_vkVoidFunction>(WaitSemaphores) },
};
// clang-format on
constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
@@ -965,6 +1007,10 @@
return GetData(device).dispatch.GetQueryPoolResults(device, queryPool, firstQuery, queryCount, dataSize, pData, stride, flags);
}
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+ GetData(device).dispatch.ResetQueryPool(device, queryPool, firstQuery, queryCount);
+}
+
VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
return GetData(device).dispatch.CreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
}
@@ -1481,6 +1527,34 @@
GetData(device).dispatch.GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport);
}
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
+ return GetData(device).dispatch.CreateRenderPass2(device, pCreateInfo, pAllocator, pRenderPass);
+}
+
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+ GetData(commandBuffer).dispatch.CmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
+}
+
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+ GetData(commandBuffer).dispatch.CmdNextSubpass2(commandBuffer, pSubpassBeginInfo, pSubpassEndInfo);
+}
+
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+ GetData(commandBuffer).dispatch.CmdEndRenderPass2(commandBuffer, pSubpassEndInfo);
+}
+
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+ return GetData(device).dispatch.GetSemaphoreCounterValue(device, semaphore, pValue);
+}
+
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+ return GetData(device).dispatch.WaitSemaphores(device, pWaitInfo, timeout);
+}
+
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+ return GetData(device).dispatch.SignalSemaphore(device, pSignalInfo);
+}
+
VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
return GetData(device).dispatch.GetAndroidHardwareBufferPropertiesANDROID(device, buffer, pProperties);
}
@@ -1489,6 +1563,26 @@
return GetData(device).dispatch.GetMemoryAndroidHardwareBufferANDROID(device, pInfo, pBuffer);
}
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ GetData(commandBuffer).dispatch.CmdDrawIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ GetData(commandBuffer).dispatch.CmdDrawIndexedIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return GetData(device).dispatch.GetBufferOpaqueCaptureAddress(device, pInfo);
+}
+
+VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return GetData(device).dispatch.GetBufferDeviceAddress(device, pInfo);
+}
+
+VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+ return GetData(device).dispatch.GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
+}
+
} // anonymous namespace
@@ -1755,6 +1849,11 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR void vkResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+ vulkan::api::ResetQueryPool(device, queryPool, firstQuery, queryCount);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR VkResult vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
return vulkan::api::CreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
}
@@ -2400,6 +2499,41 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
+ return vulkan::api::CreateRenderPass2(device, pCreateInfo, pAllocator, pRenderPass);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+ vulkan::api::CmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+ vulkan::api::CmdNextSubpass2(commandBuffer, pSubpassBeginInfo, pSubpassEndInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+ vulkan::api::CmdEndRenderPass2(commandBuffer, pSubpassEndInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+ return vulkan::api::GetSemaphoreCounterValue(device, semaphore, pValue);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkWaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+ return vulkan::api::WaitSemaphores(device, pWaitInfo, timeout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkSignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+ return vulkan::api::SignalSemaphore(device, pSignalInfo);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR VkResult vkGetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
return vulkan::api::GetAndroidHardwareBufferPropertiesANDROID(device, buffer, pProperties);
}
@@ -2409,4 +2543,29 @@
return vulkan::api::GetMemoryAndroidHardwareBufferANDROID(device, pInfo, pBuffer);
}
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ vulkan::api::CmdDrawIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ vulkan::api::CmdDrawIndexedIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR uint64_t vkGetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return vulkan::api::GetBufferOpaqueCaptureAddress(device, pInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkDeviceAddress vkGetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return vulkan::api::GetBufferDeviceAddress(device, pInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR uint64_t vkGetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+ return vulkan::api::GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
+}
+
// clang-format on
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 2195845..ad5cc34 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -99,6 +99,7 @@
PFN_vkCreateQueryPool CreateQueryPool;
PFN_vkDestroyQueryPool DestroyQueryPool;
PFN_vkGetQueryPoolResults GetQueryPoolResults;
+ PFN_vkResetQueryPool ResetQueryPool;
PFN_vkCreateBuffer CreateBuffer;
PFN_vkDestroyBuffer DestroyBuffer;
PFN_vkCreateBufferView CreateBufferView;
@@ -210,8 +211,20 @@
PFN_vkDestroySamplerYcbcrConversion DestroySamplerYcbcrConversion;
PFN_vkGetDeviceQueue2 GetDeviceQueue2;
PFN_vkGetDescriptorSetLayoutSupport GetDescriptorSetLayoutSupport;
+ PFN_vkCreateRenderPass2 CreateRenderPass2;
+ PFN_vkCmdBeginRenderPass2 CmdBeginRenderPass2;
+ PFN_vkCmdNextSubpass2 CmdNextSubpass2;
+ PFN_vkCmdEndRenderPass2 CmdEndRenderPass2;
+ PFN_vkGetSemaphoreCounterValue GetSemaphoreCounterValue;
+ PFN_vkWaitSemaphores WaitSemaphores;
+ PFN_vkSignalSemaphore SignalSemaphore;
PFN_vkGetAndroidHardwareBufferPropertiesANDROID GetAndroidHardwareBufferPropertiesANDROID;
PFN_vkGetMemoryAndroidHardwareBufferANDROID GetMemoryAndroidHardwareBufferANDROID;
+ PFN_vkCmdDrawIndirectCount CmdDrawIndirectCount;
+ PFN_vkCmdDrawIndexedIndirectCount CmdDrawIndexedIndirectCount;
+ PFN_vkGetBufferOpaqueCaptureAddress GetBufferOpaqueCaptureAddress;
+ PFN_vkGetBufferDeviceAddress GetBufferDeviceAddress;
+ PFN_vkGetDeviceMemoryOpaqueCaptureAddress GetDeviceMemoryOpaqueCaptureAddress;
// clang-format on
};
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 01cbf39..c63fdf5 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -549,6 +549,7 @@
case ProcHook::GOOGLE_display_timing:
case ProcHook::EXTENSION_CORE_1_0:
case ProcHook::EXTENSION_CORE_1_1:
+ case ProcHook::EXTENSION_CORE_1_2:
case ProcHook::EXTENSION_COUNT:
// Device and meta extensions. If we ever get here it's a bug in
// our code. But enumerating them lets us avoid having a default
@@ -598,6 +599,7 @@
case ProcHook::ANDROID_native_buffer:
case ProcHook::EXTENSION_CORE_1_0:
case ProcHook::EXTENSION_CORE_1_1:
+ case ProcHook::EXTENSION_CORE_1_2:
case ProcHook::EXTENSION_COUNT:
// Instance and meta extensions. If we ever get here it's a bug
// in our code. But enumerating them lets us avoid having a
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 43c4d14..62538c1 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -51,6 +51,7 @@
EXTENSION_CORE_1_0,
EXTENSION_CORE_1_1,
+ EXTENSION_CORE_1_2,
EXTENSION_COUNT,
EXTENSION_UNKNOWN,
};
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index 0be66c9..df97d7f 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -12,6 +12,7 @@
vkBindImageMemory2; # introduced=28
vkCmdBeginQuery;
vkCmdBeginRenderPass;
+ vkCmdBeginRenderPass2; # introduced=31
vkCmdBindDescriptorSets;
vkCmdBindIndexBuffer;
vkCmdBindPipeline;
@@ -31,12 +32,16 @@
vkCmdDraw;
vkCmdDrawIndexed;
vkCmdDrawIndexedIndirect;
+ vkCmdDrawIndexedIndirectCount; # introduced=31
vkCmdDrawIndirect;
+ vkCmdDrawIndirectCount; # introduced=31
vkCmdEndQuery;
vkCmdEndRenderPass;
+ vkCmdEndRenderPass2; # introduced=31
vkCmdExecuteCommands;
vkCmdFillBuffer;
vkCmdNextSubpass;
+ vkCmdNextSubpass2; # introduced=31
vkCmdPipelineBarrier;
vkCmdPushConstants;
vkCmdResetEvent;
@@ -76,6 +81,7 @@
vkCreatePipelineLayout;
vkCreateQueryPool;
vkCreateRenderPass;
+ vkCreateRenderPass2; # introduced=31
vkCreateSampler;
vkCreateSamplerYcbcrConversion; # introduced=28
vkCreateSemaphore;
@@ -119,13 +125,16 @@
vkFreeDescriptorSets;
vkFreeMemory;
vkGetAndroidHardwareBufferPropertiesANDROID; # introduced=28
+ vkGetBufferDeviceAddress; # introduced=31
vkGetBufferMemoryRequirements;
vkGetBufferMemoryRequirements2; # introduced=28
+ vkGetBufferOpaqueCaptureAddress; # introduced=31
vkGetDescriptorSetLayoutSupport; # introduced=28
vkGetDeviceGroupPeerMemoryFeatures; # introduced=28
vkGetDeviceGroupPresentCapabilitiesKHR; # introduced=28
vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28
vkGetDeviceMemoryCommitment;
+ vkGetDeviceMemoryOpaqueCaptureAddress; # introduced=31
vkGetDeviceProcAddr;
vkGetDeviceQueue;
vkGetDeviceQueue2; # introduced=28
@@ -163,6 +172,7 @@
vkGetPipelineCacheData;
vkGetQueryPoolResults;
vkGetRenderAreaGranularity;
+ vkGetSemaphoreCounterValue; # introduced=31
vkGetSwapchainImagesKHR;
vkInvalidateMappedMemoryRanges;
vkMapMemory;
@@ -176,12 +186,15 @@
vkResetDescriptorPool;
vkResetEvent;
vkResetFences;
+ vkResetQueryPool; # introduced=31
vkSetEvent;
+ vkSignalSemaphore; # introduced=31
vkTrimCommandPool; # introduced=28
vkUnmapMemory;
vkUpdateDescriptorSets;
vkUpdateDescriptorSetWithTemplate; # introduced=28
vkWaitForFences;
+ vkWaitSemaphores; # introduced=31
local:
*;
};
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 4647a80..b94233b 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -1123,6 +1123,14 @@
return VK_SUCCESS;
}
+VkResult CreateRenderPass2(VkDevice device,
+ const VkRenderPassCreateInfo2*,
+ const VkAllocationCallbacks* /*allocator*/,
+ VkRenderPass* pRenderPass) {
+ *pRenderPass = AllocHandle<VkRenderPass>(device, HandleType::kRenderPass);
+ return VK_SUCCESS;
+}
+
// -----------------------------------------------------------------------------
// No-op entrypoints
@@ -1568,6 +1576,55 @@
void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) {
}
+void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+}
+
+void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+}
+
+void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+}
+
+void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+}
+
+VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return VK_SUCCESS;
+}
+
+VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return VK_SUCCESS;
+}
+
+VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return VK_SUCCESS;
+}
+
+void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+}
+
+void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+}
+
+uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return 0;
+}
+
+VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return (VkDeviceAddress)0;
+}
+
+uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return 0;
+}
+
#pragma clang diagnostic pop
// clang-format on
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index b8d7d2b..edda12c 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -67,6 +67,7 @@
{"vkBindImageMemory2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkBindImageMemory2>(BindImageMemory2))},
{"vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginQuery>(CmdBeginQuery))},
{"vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass>(CmdBeginRenderPass))},
+ {"vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass2>(CmdBeginRenderPass2))},
{"vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindDescriptorSets>(CmdBindDescriptorSets))},
{"vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindIndexBuffer>(CmdBindIndexBuffer))},
{"vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindPipeline>(CmdBindPipeline))},
@@ -86,12 +87,16 @@
{"vkCmdDraw", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDraw>(CmdDraw))},
{"vkCmdDrawIndexed", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexed>(CmdDrawIndexed))},
{"vkCmdDrawIndexedIndirect", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexedIndirect>(CmdDrawIndexedIndirect))},
+ {"vkCmdDrawIndexedIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexedIndirectCount>(CmdDrawIndexedIndirectCount))},
{"vkCmdDrawIndirect", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndirect>(CmdDrawIndirect))},
+ {"vkCmdDrawIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndirectCount>(CmdDrawIndirectCount))},
{"vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndQuery>(CmdEndQuery))},
{"vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRenderPass>(CmdEndRenderPass))},
+ {"vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRenderPass2>(CmdEndRenderPass2))},
{"vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdExecuteCommands>(CmdExecuteCommands))},
{"vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdFillBuffer>(CmdFillBuffer))},
{"vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdNextSubpass>(CmdNextSubpass))},
+ {"vkCmdNextSubpass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdNextSubpass2>(CmdNextSubpass2))},
{"vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier>(CmdPipelineBarrier))},
{"vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushConstants>(CmdPushConstants))},
{"vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent>(CmdResetEvent))},
@@ -131,6 +136,7 @@
{"vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreatePipelineLayout>(CreatePipelineLayout))},
{"vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateQueryPool>(CreateQueryPool))},
{"vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateRenderPass>(CreateRenderPass))},
+ {"vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateRenderPass2>(CreateRenderPass2))},
{"vkCreateSampler", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSampler>(CreateSampler))},
{"vkCreateSamplerYcbcrConversion", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSamplerYcbcrConversion>(CreateSamplerYcbcrConversion))},
{"vkCreateSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSemaphore>(CreateSemaphore))},
@@ -172,11 +178,14 @@
{"vkFreeCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeCommandBuffers>(FreeCommandBuffers))},
{"vkFreeDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeDescriptorSets>(FreeDescriptorSets))},
{"vkFreeMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeMemory>(FreeMemory))},
+ {"vkGetBufferDeviceAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferDeviceAddress>(GetBufferDeviceAddress))},
{"vkGetBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferMemoryRequirements>(GetBufferMemoryRequirements))},
{"vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferMemoryRequirements2>(GetBufferMemoryRequirements2))},
+ {"vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferOpaqueCaptureAddress>(GetBufferOpaqueCaptureAddress))},
{"vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDescriptorSetLayoutSupport>(GetDescriptorSetLayoutSupport))},
{"vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceGroupPeerMemoryFeatures>(GetDeviceGroupPeerMemoryFeatures))},
{"vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryCommitment>(GetDeviceMemoryCommitment))},
+ {"vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryOpaqueCaptureAddress>(GetDeviceMemoryOpaqueCaptureAddress))},
{"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr))},
{"vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceQueue>(GetDeviceQueue))},
{"vkGetDeviceQueue2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceQueue2>(GetDeviceQueue2))},
@@ -215,6 +224,7 @@
{"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPipelineCacheData>(GetPipelineCacheData))},
{"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))},
{"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
+ {"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
{"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
{"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
{"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
@@ -229,12 +239,15 @@
{"vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetDescriptorPool>(ResetDescriptorPool))},
{"vkResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetEvent>(ResetEvent))},
{"vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetFences>(ResetFences))},
+ {"vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetQueryPool>(ResetQueryPool))},
{"vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetEvent>(SetEvent))},
+ {"vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSignalSemaphore>(SignalSemaphore))},
{"vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkTrimCommandPool>(TrimCommandPool))},
{"vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUnmapMemory>(UnmapMemory))},
{"vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSetWithTemplate>(UpdateDescriptorSetWithTemplate))},
{"vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSets>(UpdateDescriptorSets))},
{"vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkWaitForFences>(WaitForFences))},
+ {"vkWaitSemaphores", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkWaitSemaphores>(WaitSemaphores))},
// clang-format on
};
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 0d3f688..e59cae9 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -79,6 +79,7 @@
VKAPI_ATTR VkResult CreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool);
VKAPI_ATTR void DestroyQueryPool(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult GetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags);
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
VKAPI_ATTR void DestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult CreateBufferView(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView);
@@ -207,6 +208,18 @@
VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo);
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue);
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout);
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo);
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
// clang-format on
} // namespace null_driver
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index ef0719d..e9a96f3 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -25,6 +25,7 @@
_BLOCKED_EXTENSIONS = [
'VK_EXT_acquire_xlib_display',
'VK_EXT_direct_mode_display',
+ 'VK_EXT_directfb_surface',
'VK_EXT_display_control',
'VK_EXT_display_surface_counter',
'VK_EXT_full_screen_exclusive',
@@ -302,12 +303,12 @@
else:
f.write('INIT_PROC(')
- if name in version_dict and version_dict[name] == 'VK_VERSION_1_1':
+ if name in _OPTIONAL_COMMANDS:
f.write('false, ')
- elif name in _OPTIONAL_COMMANDS:
- f.write('false, ')
- else:
+ elif version_dict[name] == 'VK_VERSION_1_0':
f.write('true, ')
+ else:
+ f.write('false, ')
if is_instance_dispatched(name):
f.write('instance, ')
@@ -376,7 +377,7 @@
for exts in root.iter('extensions'):
for extension in exts:
- apiversion = ''
+ apiversion = 'VK_VERSION_1_0'
if extension.tag == 'extension':
extname = extension.get('name')
for req in extension:
@@ -387,8 +388,7 @@
cmd_name = commands.get('name')
if cmd_name not in extension_dict:
extension_dict[cmd_name] = extname
- if apiversion:
- version_dict[cmd_name] = apiversion
+ version_dict[cmd_name] = apiversion
for feature in root.iter('feature'):
apiversion = feature.get('name')
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index b0b466c..bfc240e 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -57,9 +57,17 @@
template <typename T> struct EnumTraits;
template <> struct EnumTraits<VkPhysicalDeviceType> {
- static uint32_t min() { return VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE; }
- static uint32_t max() { return VK_PHYSICAL_DEVICE_TYPE_END_RANGE; }
- static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+ static bool exist(uint32_t e) {
+ switch (e) {
+ case VK_PHYSICAL_DEVICE_TYPE_OTHER:
+ case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+ case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+ case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+ case VK_PHYSICAL_DEVICE_TYPE_CPU:
+ return true;
+ }
+ return false;
+ }
};
template <> struct EnumTraits<VkFormat> {
@@ -250,6 +258,40 @@
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+ case VK_FORMAT_G8B8G8R8_422_UNORM:
+ case VK_FORMAT_B8G8R8G8_422_UNORM:
+ case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
+ case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
+ case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
+ case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
+ case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
+ case VK_FORMAT_R10X6_UNORM_PACK16:
+ case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
+ case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
+ case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
+ case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
+ case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
+ case VK_FORMAT_R12X4_UNORM_PACK16:
+ case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
+ case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
+ case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
+ case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
+ case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
+ case VK_FORMAT_G16B16G16R16_422_UNORM:
+ case VK_FORMAT_B16G16R16G16_422_UNORM:
+ case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
+ case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
+ case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
+ case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
+ case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
@@ -258,40 +300,22 @@
case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
- case VK_FORMAT_G8B8G8R8_422_UNORM_KHR:
- case VK_FORMAT_B8G8R8G8_422_UNORM_KHR:
- case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR:
- case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR:
- case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR:
- case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR:
- case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR:
- case VK_FORMAT_R10X6_UNORM_PACK16_KHR:
- case VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR:
- case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR:
- case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR:
- case VK_FORMAT_R12X4_UNORM_PACK16_KHR:
- case VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR:
- case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR:
- case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR:
- case VK_FORMAT_G16B16G16R16_422_UNORM_KHR:
- case VK_FORMAT_B16G16R16G16_422_UNORM_KHR:
- case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR:
- case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR:
- case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR:
- case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR:
- case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR:
+ case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT:
+ case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT:
return true;
}
return false;
@@ -300,9 +324,14 @@
template <>
struct EnumTraits<VkPointClippingBehavior> {
- static uint32_t min() { return VK_POINT_CLIPPING_BEHAVIOR_BEGIN_RANGE; }
- static uint32_t max() { return VK_POINT_CLIPPING_BEHAVIOR_END_RANGE; }
- static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+ static bool exist(uint32_t e) {
+ switch (e) {
+ case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES:
+ case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY:
+ return true;
+ }
+ return false;
+ }
};
template <>
@@ -336,9 +365,26 @@
template <>
struct EnumTraits<VkDriverIdKHR> {
- static uint32_t min() { return VK_DRIVER_ID_BEGIN_RANGE_KHR; }
- static uint32_t max() { return VK_DRIVER_ID_END_RANGE_KHR; }
- static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+ static bool exist(uint32_t e) {
+ switch (e) {
+ case VK_DRIVER_ID_AMD_PROPRIETARY:
+ case VK_DRIVER_ID_AMD_OPEN_SOURCE:
+ case VK_DRIVER_ID_MESA_RADV:
+ case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
+ case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
+ case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
+ case VK_DRIVER_ID_IMAGINATION_PROPRIETARY:
+ case VK_DRIVER_ID_QUALCOMM_PROPRIETARY:
+ case VK_DRIVER_ID_ARM_PROPRIETARY:
+ case VK_DRIVER_ID_GOOGLE_SWIFTSHADER:
+ case VK_DRIVER_ID_GGP_PROPRIETARY:
+ case VK_DRIVER_ID_BROADCOM_PROPRIETARY:
+ case VK_DRIVER_ID_MESA_LLVMPIPE:
+ case VK_DRIVER_ID_MOLTENVK:
+ return true;
+ }
+ return false;
+ }
};
// VkSparseImageFormatProperties
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 84cfe5e..73586d4 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -165,7 +165,9 @@
VkFormatProperties format_properties = {};
for (VkFormat format = VK_FORMAT_R4G4_UNORM_PACK8;
- format <= VK_FORMAT_END_RANGE;
+ // TODO(http://b/171403054): avoid hard-coding last value in the
+ // contiguous range
+ format <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK;
format = static_cast<VkFormat>(format + 1)) {
vkGetPhysicalDeviceFormatProperties(physical_device, format,
&format_properties);
@@ -178,6 +180,8 @@
if (device.properties.apiVersion >= VK_API_VERSION_1_1) {
for (VkFormat format = VK_FORMAT_G8B8G8R8_422_UNORM;
+ // TODO(http://b/171403054): avoid hard-coding last value in the
+ // contiguous range
format <= VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM;
format = static_cast<VkFormat>(format + 1)) {
vkGetPhysicalDeviceFormatProperties(physical_device, format,