Merge "Add Audio HAL V7"
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 4058934..1f055f3 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -105,6 +105,7 @@
name: "dumpstate",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
"dumpstate.cpp",
"main.cpp",
],
@@ -132,6 +133,7 @@
name: "dumpstate_test",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
"dumpstate.cpp",
"tests/dumpstate_test.cpp",
],
@@ -148,6 +150,7 @@
name: "dumpstate_smoke_test",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
"dumpstate.cpp",
"tests/dumpstate_smoke_test.cpp",
],
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
new file mode 100644
index 0000000..e174c8e
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpPool.h"
+
+#include <array>
+#include <thread>
+
+#include <log/log.h>
+
+#include "dumpstate.h"
+#include "DumpstateInternal.h"
+#include "DumpstateUtil.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+const std::string DumpPool::PREFIX_TMPFILE_NAME = "dump-tmp.";
+
+DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false),
+ log_duration_(true) {
+ assert(!tmp_root.empty());
+ deleteTempFiles(tmp_root_);
+}
+
+DumpPool::~DumpPool() {
+ shutdown();
+}
+
+void DumpPool::start(int thread_counts) {
+ assert(thread_counts > 0);
+ assert(threads_.empty());
+ if (thread_counts > MAX_THREAD_COUNT) {
+ thread_counts = MAX_THREAD_COUNT;
+ }
+ MYLOGI("Start thread pool:%d", thread_counts);
+ shutdown_ = false;
+ for (int i = 0; i < thread_counts; i++) {
+ threads_.emplace_back(std::thread([=]() {
+ setThreadName(pthread_self(), i + 1);
+ loop();
+ }));
+ }
+}
+
+void DumpPool::shutdown() {
+ std::unique_lock lock(lock_);
+ if (shutdown_ || threads_.empty()) {
+ return;
+ }
+ while (!tasks_.empty()) tasks_.pop();
+ futures_map_.clear();
+
+ shutdown_ = true;
+ condition_variable_.notify_all();
+ lock.unlock();
+
+ for (auto& thread : threads_) {
+ thread.join();
+ }
+ threads_.clear();
+ deleteTempFiles(tmp_root_);
+ MYLOGI("shutdown thread pool");
+}
+
+void DumpPool::waitForTask(const std::string& task_name, const std::string& title,
+ int out_fd) {
+ DurationReporter duration_reporter("Wait for " + task_name, true);
+ auto iterator = futures_map_.find(task_name);
+ if (iterator == futures_map_.end()) {
+ MYLOGW("Task %s does not exist", task_name.c_str());
+ return;
+ }
+ Future future = iterator->second;
+ futures_map_.erase(iterator);
+
+ std::string result = future.get();
+ if (result.empty()) {
+ return;
+ }
+ DumpFileToFd(out_fd, title, result);
+ if (unlink(result.c_str())) {
+ MYLOGE("Failed to unlink (%s): %s\n", result.c_str(), strerror(errno));
+ }
+}
+
+void DumpPool::setLogDuration(bool log_duration) {
+ log_duration_ = log_duration;
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void()>>(std::function<void()> dump_func,
+ const std::string& duration_title, int out_fd) {
+ DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+ /*verbose =*/false, out_fd);
+ std::invoke(dump_func);
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void(int)>>(std::function<void(int)> dump_func,
+ const std::string& duration_title, int out_fd) {
+ DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+ /*verbose =*/false, out_fd);
+ std::invoke(dump_func, out_fd);
+}
+
+std::unique_ptr<DumpPool::TmpFile> DumpPool::createTempFile() {
+ auto tmp_file_ptr = std::make_unique<TmpFile>();
+ std::string file_name_format = "%s/" + PREFIX_TMPFILE_NAME + "XXXXXX";
+ snprintf(tmp_file_ptr->path, sizeof(tmp_file_ptr->path), file_name_format.c_str(),
+ tmp_root_.c_str());
+ tmp_file_ptr->fd.reset(TEMP_FAILURE_RETRY(
+ mkostemp(tmp_file_ptr->path, O_CLOEXEC)));
+ if (tmp_file_ptr->fd.get() == -1) {
+ MYLOGE("open(%s, %s)\n", tmp_file_ptr->path, strerror(errno));
+ tmp_file_ptr = nullptr;
+ return tmp_file_ptr;
+ }
+ return tmp_file_ptr;
+}
+
+void DumpPool::deleteTempFiles(const std::string& folder) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
+ &closedir);
+ if (!dir_ptr) {
+ MYLOGE("Failed to opendir (%s): %s\n", folder.c_str(), strerror(errno));
+ return;
+ }
+ int dir_fd = dirfd(dir_ptr.get());
+ if (dir_fd < 0) {
+ MYLOGE("Failed to get fd of dir (%s): %s\n", folder.c_str(),
+ strerror(errno));
+ return;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir_ptr.get()))) {
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+ std::string file_name(de->d_name);
+ if (file_name.find(PREFIX_TMPFILE_NAME) != 0) {
+ continue;
+ }
+ if (unlinkat(dir_fd, file_name.c_str(), 0)) {
+ MYLOGE("Failed to unlink (%s): %s\n", file_name.c_str(),
+ strerror(errno));
+ }
+ }
+}
+
+void DumpPool::setThreadName(const pthread_t thread, int id) {
+ std::array<char, 15> name;
+ snprintf(name.data(), name.size(), "dumpstate_%d", id);
+ pthread_setname_np(thread, name.data());
+}
+
+void DumpPool::loop() {
+ std::unique_lock lock(lock_);
+ while (!shutdown_) {
+ if (tasks_.empty()) {
+ condition_variable_.wait(lock);
+ continue;
+ } else {
+ std::packaged_task<std::string()> task = std::move(tasks_.front());
+ tasks_.pop();
+ lock.unlock();
+ std::invoke(task);
+ lock.lock();
+ }
+ }
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h
new file mode 100644
index 0000000..a4ea875
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
+
+#include <future>
+#include <map>
+#include <queue>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+class DumpPoolTest;
+
+/*
+ * A thread pool with the fixed number of threads to execute multiple dump tasks
+ * simultaneously for the dumpstate. The dump task is a callable function. It
+ * could include a file descriptor as a parameter to redirect dump results, if
+ * it needs to output results to the bugreport. This can avoid messing up
+ * bugreport's results when multiple dump tasks are running at the same time.
+ * Takes an example below for the usage of the DumpPool:
+ *
+ * void DumpFoo(int out_fd) {
+ * dprintf(out_fd, "Dump result to out_fd ...");
+ * }
+ * ...
+ * DumpPool pool(tmp_root);
+ * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
+ * ...
+ * pool.waitForTask("TaskName");
+ *
+ * DumpFoo is a callable function included a out_fd parameter. Using the
+ * enqueueTaskWithFd method in DumpPool to enqueue the task to the pool. The
+ * std::placeholders::_1 is a placeholder for DumpPool to pass a fd argument.
+ */
+class DumpPool {
+ friend class android::os::dumpstate::DumpPoolTest;
+
+ public:
+ /*
+ * Creates a thread pool.
+ *
+ * |tmp_root| A path to a temporary folder for threads to create temporary
+ * files.
+ */
+ explicit DumpPool(const std::string& tmp_root);
+ ~DumpPool();
+
+ /*
+ * Starts the threads in the pool.
+ *
+ * |thread_counts| the number of threads to start.
+ */
+ void start(int thread_counts = MAX_THREAD_COUNT);
+
+ /*
+ * Requests to shutdown the pool and waits until all threads exit the loop.
+ */
+ void shutdown();
+
+ /*
+ * Adds a task into the queue of the thread pool.
+ *
+ * |task_name| The name of the task. It's also the title of the
+ * DurationReporter log.
+ * |f| Callable function to execute the task.
+ * |args| A list of arguments.
+ *
+ * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
+ */
+ template<class F, class... Args> void enqueueTask(const std::string& task_name, F&& f,
+ Args&&... args) {
+ std::function<void(void)> func = std::bind(std::forward<F>(f),
+ std::forward<Args>(args)...);
+ futures_map_[task_name] = post(task_name, func);
+ if (threads_.empty()) {
+ start();
+ }
+ }
+
+ /*
+ * Adds a task into the queue of the thread pool. The task takes a file
+ * descriptor as a parameter to redirect dump results to a temporary file.
+ *
+ * |task_name| The name of the task. It's also the title of the
+ * DurationReporter log.
+ * |f| Callable function to execute the task.
+ * |args| A list of arguments. A placeholder std::placeholders::_1 as a fd
+ * argument needs to be included here.
+ */
+ template<class F, class... Args> void enqueueTaskWithFd(const std::string& task_name, F&& f,
+ Args&&... args) {
+ std::function<void(int)> func = std::bind(std::forward<F>(f),
+ std::forward<Args>(args)...);
+ futures_map_[task_name] = post(task_name, func);
+ if (threads_.empty()) {
+ start();
+ }
+ }
+
+ /*
+ * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
+ */
+ void waitForTask(const std::string& task_name) {
+ waitForTask(task_name, "", STDOUT_FILENO);
+ }
+
+ /*
+ * Waits until the task is finished. Dumps the task results to the specified
+ * out_fd.
+ *
+ * |task_name| The name of the task.
+ * |title| Dump title string to the out_fd, an empty string for nothing.
+ * |out_fd| The target file to dump the result from the task.
+ */
+ void waitForTask(const std::string& task_name, const std::string& title, int out_fd);
+
+ static const std::string PREFIX_TMPFILE_NAME;
+
+ private:
+ using Task = std::packaged_task<std::string()>;
+ using Future = std::shared_future<std::string>;
+
+ template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
+
+ template<class T> Future post(const std::string& task_name, T dump_func) {
+ Task packaged_task([=]() {
+ std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
+ if (!tmp_file_ptr) {
+ return std::string("");
+ }
+ invokeTask(dump_func, task_name, tmp_file_ptr->fd.get());
+ fsync(tmp_file_ptr->fd.get());
+ return std::string(tmp_file_ptr->path);
+ });
+ std::unique_lock lock(lock_);
+ auto future = packaged_task.get_future().share();
+ tasks_.push(std::move(packaged_task));
+ condition_variable_.notify_one();
+ return future;
+ }
+
+ typedef struct {
+ android::base::unique_fd fd;
+ char path[1024];
+ } TmpFile;
+
+ std::unique_ptr<TmpFile> createTempFile();
+ void deleteTempFiles(const std::string& folder);
+ void setThreadName(const pthread_t thread, int id);
+ void loop();
+
+ /*
+ * For test purpose only. Enables or disables logging duration of the task.
+ *
+ * |log_duration| if true, DurationReporter is initiated to log duration of
+ * the task.
+ */
+ void setLogDuration(bool log_duration);
+
+ private:
+ static const int MAX_THREAD_COUNT = 4;
+
+ /* A path to a temporary folder for threads to create temporary files. */
+ std::string tmp_root_;
+ bool shutdown_;
+ bool log_duration_; // For test purpose only, the default value is true.
+ std::mutex lock_; // A lock for the tasks_.
+ std::condition_variable condition_variable_;
+
+ std::vector<std::thread> threads_;
+ std::queue<Task> tasks_;
+ std::map<std::string, Future> futures_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(DumpPool);
+};
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+#endif //FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index 4b69607..eeaa5a3 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -180,6 +180,7 @@
std::string PropertiesHelper::build_type_ = "";
int PropertiesHelper::dry_run_ = -1;
int PropertiesHelper::unroot_ = -1;
+int PropertiesHelper::parallel_run_ = -1;
bool PropertiesHelper::IsUserBuild() {
if (build_type_.empty()) {
@@ -202,6 +203,14 @@
return unroot_ == 1;
}
+bool PropertiesHelper::IsParallelRun() {
+ if (parallel_run_ == -1) {
+ parallel_run_ = android::base::GetBoolProperty("dumpstate.parallel_run",
+ /* default_value = */true) ? 1 : 0;
+ }
+ return parallel_run_ == 1;
+}
+
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
if (fd.get() < 0) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index b7ac25c..b099443 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -176,10 +176,18 @@
*/
static bool IsUnroot();
+ /*
+ * Whether or not the parallel run is enabled. Setting the system property
+ * 'dumpstate.parallel_run' to false to disable it, otherwise it returns
+ * true by default.
+ */
+ static bool IsParallelRun();
+
private:
static std::string build_type_;
static int dry_run_;
static int unroot_;
+ static int parallel_run_;
};
/*
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8879aed..7d195b4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -95,6 +95,7 @@
using ::android::hardware::dumpstate::V1_1::toString;
using ::std::literals::chrono_literals::operator""ms;
using ::std::literals::chrono_literals::operator""s;
+using ::std::placeholders::_1;
// TODO: remove once moved to namespace
using android::defaultServiceManager;
@@ -113,6 +114,7 @@
using android::os::IDumpstateListener;
using android::os::dumpstate::CommandOptions;
using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::DumpPool;
using android::os::dumpstate::PropertiesHelper;
// Keep in sync with
@@ -196,8 +198,26 @@
func_ptr(__VA_ARGS__); \
RETURN_IF_USER_DENIED_CONSENT();
+// Runs func_ptr, and logs a duration report after it's finished.
+#define RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, ...) \
+ { \
+ DurationReporter duration_reporter_in_macro(log_title); \
+ func_ptr(__VA_ARGS__); \
+ }
+
+// Similar with RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK, an additional duration report
+// is output after a slow function is finished.
+#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(log_title, func_ptr, ...) \
+ RETURN_IF_USER_DENIED_CONSENT(); \
+ RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, __VA_ARGS__); \
+ RETURN_IF_USER_DENIED_CONSENT();
+
static const char* WAKE_LOCK_NAME = "dumpstate_wakelock";
+// Names of parallel tasks, they are used for the DumpPool to identify the dump
+// task and the log title of the duration report.
+static const std::string DUMP_TRACES_TASK = "DUMP TRACES";
+
namespace android {
namespace os {
namespace {
@@ -762,8 +782,9 @@
RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
CommandOptions::WithTimeout(1).Always().Build());
printf("Bugreport format version: %s\n", version_.c_str());
- printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s bugreport_mode=%s\n", id_, pid_,
- PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->bugreport_mode.c_str());
+ printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n",
+ id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
+ options_->args.c_str(), options_->bugreport_mode.c_str());
printf("\n");
}
@@ -1307,12 +1328,12 @@
static void DumpHals() {
if (!ds.IsZipping()) {
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"},
+ RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
return;
}
DurationReporter duration_reporter("DUMP HALS");
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"},
+ RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
using android::hidl::manager::V1_0::IServiceManager;
@@ -1680,7 +1701,18 @@
time_t logcat_ts = time(nullptr);
/* collect stack traces from Dalvik and native processes (needs root) */
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path);
+ if (dump_pool_) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ // One thread is enough since we only need to enqueue DumpTraces here.
+ dump_pool_->start(/* thread_counts = */1);
+
+ // DumpTraces takes long time, post it to the another thread in the
+ // pool, if pool is available
+ dump_pool_->enqueueTask(DUMP_TRACES_TASK, &Dumpstate::DumpTraces, &ds, &dump_traces_path);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_TRACES_TASK, ds.DumpTraces,
+ &dump_traces_path);
+ }
/* Run some operations that require root. */
ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
@@ -1723,6 +1755,15 @@
DumpFile("PSI memory", "/proc/pressure/memory");
DumpFile("PSI io", "/proc/pressure/io");
+ if (dump_pool_) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ dump_pool_->waitForTask(DUMP_TRACES_TASK);
+
+ // Current running thread in the pool is the root user also. Shutdown
+ // the pool and restart later to ensure all threads in the pool could
+ // drop the root user.
+ dump_pool_->shutdown();
+ }
if (!DropRootUser()) {
return Dumpstate::RunStatus::ERROR;
}
@@ -1881,8 +1922,6 @@
}
Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
- DurationReporter duration_reporter("DUMP TRACES");
-
const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
const size_t buf_size = temp_file_pattern.length() + 1;
std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
@@ -2554,6 +2593,7 @@
*/
Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
const std::string& calling_package) {
+ DurationReporter duration_reporter("RUN INTERNAL", /* logcat_only = */true);
LogDumpOptions(*options_);
if (!options_->ValidateOptions()) {
MYLOGE("Invalid options specified\n");
@@ -2715,6 +2755,13 @@
// Don't buffer stdout
setvbuf(stdout, nullptr, _IONBF, 0);
+ // Enable the parallel run if the client requests to output to a file.
+ EnableParallelRunIfNeeded();
+ // Using scope guard to make sure the dump pool can be shut down correctly.
+ auto scope_guard_to_shutdown_pool = android::base::make_scope_guard([=]() {
+ ShutdownDumpPool();
+ });
+
// NOTE: there should be no stdout output until now, otherwise it would break the header.
// In particular, DurationReport objects should be created passing 'title, NULL', so their
// duration is logged into MYLOG instead.
@@ -2881,6 +2928,23 @@
android::os::UnlinkAndLogOnError(path_);
}
+void Dumpstate::EnableParallelRunIfNeeded() {
+ // The thread pool needs to create temporary files to receive dump results.
+ // That's why we only enable it when the bugreport client chooses to output
+ // to a file.
+ if (!PropertiesHelper::IsParallelRun() || !options_->OutputToFile()) {
+ return;
+ }
+ dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
+}
+
+void Dumpstate::ShutdownDumpPool() {
+ if (dump_pool_) {
+ dump_pool_->shutdown();
+ dump_pool_ = nullptr;
+ }
+}
+
Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
MYLOGD("User denied consent; deleting files and returning\n");
CleanupTmpFiles();
@@ -2999,8 +3063,9 @@
return singleton_;
}
-DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose)
- : title_(title), logcat_only_(logcat_only), verbose_(verbose) {
+DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose,
+ int duration_fd) : title_(title), logcat_only_(logcat_only), verbose_(verbose),
+ duration_fd_(duration_fd) {
if (!title_.empty()) {
started_ = Nanotime();
}
@@ -3014,7 +3079,8 @@
}
if (!logcat_only_) {
// Use "Yoda grammar" to make it easier to grep|sort sections.
- printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str());
+ dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n",
+ elapsed, title_.c_str());
}
}
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0d25d30..d400dc7 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -35,6 +35,7 @@
#include <ziparchive/zip_writer.h>
#include "DumpstateUtil.h"
+#include "DumpPool.h"
// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
// std::vector<std::string>
@@ -75,7 +76,7 @@
class DurationReporter {
public:
explicit DurationReporter(const std::string& title, bool logcat_only = false,
- bool verbose = false);
+ bool verbose = false, int duration_fd = STDOUT_FILENO);
~DurationReporter();
@@ -84,6 +85,7 @@
bool logcat_only_;
bool verbose_;
uint64_t started_;
+ int duration_fd_;
DISALLOW_COPY_AND_ASSIGN(DurationReporter);
};
@@ -193,7 +195,7 @@
* that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
*/
class Dumpstate {
- friend class DumpstateTest;
+ friend class android::os::dumpstate::DumpstateTest;
public:
enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
@@ -490,6 +492,9 @@
// List of open ANR dump files.
std::vector<DumpData> anr_data_;
+ // A thread pool to execute dump tasks simultaneously if the parallel run is enabled.
+ std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_;
+
// A callback to IncidentCompanion service, which checks user consent for sharing the
// bugreport with the calling app. If the user has not responded yet to the dialog it will
// be neither confirmed nor denied.
@@ -528,6 +533,10 @@
// but leaves the log file alone.
void CleanupTmpFiles();
+ // Create the thread pool to enable the parallel run function.
+ void EnableParallelRunIfNeeded();
+ void ShutdownDumpPool();
+
RunStatus HandleUserConsentDenied();
// Copies bugreport artifacts over to the caller's directories provided there is user consent or
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index c7df1bb..b3cb434 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -21,6 +21,7 @@
#include "DumpstateService.h"
#include "android/os/BnDumpstate.h"
#include "dumpstate.h"
+#include "DumpPool.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -46,6 +47,7 @@
using ::android::hardware::dumpstate::V1_1::DumpstateMode;
using ::testing::EndsWith;
+using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::IsEmpty;
using ::testing::IsNull;
@@ -95,6 +97,10 @@
PropertiesHelper::unroot_ = unroot;
}
+ void SetParallelRun(bool parallel_run) const {
+ PropertiesHelper::parallel_run_ = parallel_run;
+ }
+
bool IsStandalone() const {
return calls_ == 1;
}
@@ -542,6 +548,10 @@
ds.options_.reset(new Dumpstate::DumpOptions());
}
+ void TearDown() {
+ ds.ShutdownDumpPool();
+ }
+
// Runs a command and capture `stdout` and `stderr`.
int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options = CommandOptions::DEFAULT) {
@@ -569,6 +579,10 @@
ds.progress_.reset(new Progress(initial_max, progress, 1.2));
}
+ void EnableParallelRunIfNeeded() {
+ ds.EnableParallelRunIfNeeded();
+ }
+
std::string GetProgressMessage(int progress, int max,
int old_max = 0, bool update_progress = true) {
EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
@@ -1007,6 +1021,27 @@
ds.listener_.clear();
}
+TEST_F(DumpstateTest, DumpPool_withOutputToFileAndParallelRunEnabled_notNull) {
+ ds.options_->use_socket = false;
+ SetParallelRun(true);
+ EnableParallelRunIfNeeded();
+ EXPECT_TRUE(ds.options_->OutputToFile());
+ EXPECT_TRUE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withNotOutputToFile_isNull) {
+ ds.options_->use_socket = true;
+ EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.options_->OutputToFile());
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) {
+ SetParallelRun(false);
+ EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
class DumpstateServiceTest : public DumpstateBaseTest {
public:
DumpstateService dss;
@@ -1618,6 +1653,103 @@
EXPECT_THAT(out, EndsWith("skipped on dry run\n"));
}
+class DumpPoolTest : public DumpstateBaseTest {
+ public:
+ void SetUp() {
+ dump_pool_ = std::make_unique<DumpPool>(kTestDataPath);
+ DumpstateBaseTest::SetUp();
+ CreateOutputFile();
+ }
+
+ void CreateOutputFile() {
+ out_path_ = kTestDataPath + "out.txt";
+ out_fd_.reset(TEMP_FAILURE_RETRY(open(out_path_.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+ ASSERT_GE(out_fd_.get(), 0) << "could not create FD for path "
+ << out_path_;
+ }
+
+ int getTempFileCounts(const std::string& folder) {
+ int count = 0;
+ std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
+ &closedir);
+ if (!dir_ptr) {
+ return -1;
+ }
+ int dir_fd = dirfd(dir_ptr.get());
+ if (dir_fd < 0) {
+ return -1;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir_ptr.get()))) {
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+ std::string file_name(de->d_name);
+ if (file_name.find(DumpPool::PREFIX_TMPFILE_NAME) != 0) {
+ continue;
+ }
+ count++;
+ }
+ return count;
+ }
+
+ void setLogDuration(bool log_duration) {
+ dump_pool_->setLogDuration(log_duration);
+ }
+
+ std::unique_ptr<DumpPool> dump_pool_;
+ android::base::unique_fd out_fd_;
+ std::string out_path_;
+};
+
+TEST_F(DumpPoolTest, EnqueueTaskWithFd) {
+ auto dump_func_1 = [](int out_fd) {
+ dprintf(out_fd, "A");
+ };
+ auto dump_func_2 = [](int out_fd) {
+ dprintf(out_fd, "B");
+ sleep(1);
+ };
+ auto dump_func_3 = [](int out_fd) {
+ dprintf(out_fd, "C");
+ };
+ setLogDuration(/* log_duration = */false);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"1", dump_func_1, std::placeholders::_1);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"2", dump_func_2, std::placeholders::_1);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"3", dump_func_3, std::placeholders::_1);
+
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->waitForTask("2", "", out_fd_.get());
+ dump_pool_->waitForTask("3", "", out_fd_.get());
+ dump_pool_->shutdown();
+
+ std::string result;
+ ReadFileToString(out_path_, &result);
+ EXPECT_THAT(result, StrEq("A\nB\nC\n"));
+ EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
+TEST_F(DumpPoolTest, EnqueueTask_withDurationLog) {
+ bool run_1 = false;
+ auto dump_func_1 = [&]() {
+ run_1 = true;
+ };
+
+ dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->shutdown();
+
+ std::string result;
+ ReadFileToString(out_path_, &result);
+ EXPECT_TRUE(run_1);
+ EXPECT_THAT(result, StrEq("------ 0.000s was the duration of '1' ------\n"));
+ EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
+
} // namespace dumpstate
} // namespace os
} // namespace android
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 8ff4dd8..17015a4 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -17,7 +17,9 @@
"InstalldNativeService.cpp",
"QuotaUtils.cpp",
"dexopt.cpp",
+ "execv_helper.cpp",
"globals.cpp",
+ "run_dex2oat.cpp",
"utils.cpp",
"utils_default.cpp",
"view_compiler.cpp",
@@ -103,6 +105,30 @@
}
//
+// Unit tests
+//
+
+cc_test_host {
+ name: "run_dex2oat_test",
+ test_suites: ["general-tests"],
+ clang: true,
+ srcs: [
+ "run_dex2oat_test.cpp",
+ "run_dex2oat.cpp",
+ "execv_helper.cpp",
+ ],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "libbase",
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ //"libinstalld",
+ ],
+ test_config: "run_dex2oat_test.xml",
+}
+
+//
// Executable
//
@@ -163,8 +189,7 @@
filegroup {
name: "installd_aidl",
srcs: [
- "binder/android/os/IInstalld.aidl",
- "binder/android/os/storage/CrateMetadata.aidl",
+ "binder/**/*.aidl",
],
path: "binder",
}
@@ -205,9 +230,11 @@
srcs: [
"dexopt.cpp",
+ "execv_helper.cpp",
"globals.cpp",
"otapreopt.cpp",
"otapreopt_utils.cpp",
+ "run_dex2oat.cpp",
"utils.cpp",
"utils_default.cpp",
"view_compiler.cpp",
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e7014c8..eb1bbd9 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -420,33 +420,6 @@
return true;
}
-binder::Status InstalldNativeService::createAppDataBatched(
- const std::optional<std::vector<std::optional<std::string>>>& uuids,
- const std::optional<std::vector<std::optional<std::string>>>& packageNames,
- int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
- const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
- int64_t* _aidl_return) {
- ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
- ATRACE_BEGIN("createAppDataBatched");
- binder::Status ret;
- for (size_t i = 0; i < uuids->size(); i++) {
- std::optional<std::string> packageName = packageNames->at(i);
- if (!packageName) {
- continue;
- }
- ret = createAppData(uuids->at(i), *packageName, userId, flags, appIds[i],
- seInfos[i], targetSdkVersions[i], _aidl_return);
- if (!ret.isOk()) {
- ATRACE_END();
- return ret;
- }
- }
- ATRACE_END();
- return ok();
-}
-
binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
@@ -528,6 +501,38 @@
return ok();
}
+
+binder::Status InstalldNativeService::createAppData(
+ const android::os::CreateAppDataArgs& args,
+ android::os::CreateAppDataResult* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ int64_t ceDataInode = -1;
+ auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId,
+ args.seInfo, args.targetSdkVersion, &ceDataInode);
+ _aidl_return->ceDataInode = ceDataInode;
+ _aidl_return->exceptionCode = status.exceptionCode();
+ _aidl_return->exceptionMessage = status.exceptionMessage();
+ return ok();
+}
+
+binder::Status InstalldNativeService::createAppDataBatched(
+ const std::vector<android::os::CreateAppDataArgs>& args,
+ std::vector<android::os::CreateAppDataResult>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ std::vector<android::os::CreateAppDataResult> results;
+ for (auto arg : args) {
+ android::os::CreateAppDataResult result;
+ createAppData(arg, &result);
+ results.push_back(result);
+ }
+ *_aidl_return = results;
+ return ok();
+}
+
binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 9819327..4966b96 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -44,15 +44,18 @@
int32_t userSerial, int32_t flags);
binder::Status destroyUserData(const std::optional<std::string>& uuid, int32_t userId,
int32_t flags);
- binder::Status createAppDataBatched(
- const std::optional<std::vector<std::optional<std::string>>>& uuids,
- const std::optional<std::vector<std::optional<std::string>>>& packageNames,
- int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
- const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
- int64_t* _aidl_return);
+
binder::Status createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+
+ binder::Status createAppData(
+ const android::os::CreateAppDataArgs& args,
+ android::os::CreateAppDataResult* _aidl_return);
+ binder::Status createAppDataBatched(
+ const std::vector<android::os::CreateAppDataArgs>& args,
+ std::vector<android::os::CreateAppDataResult>* _aidl_return);
+
binder::Status restoreconAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo);
diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING
index c6583a1..3f0fb6d 100644
--- a/cmds/installd/TEST_MAPPING
+++ b/cmds/installd/TEST_MAPPING
@@ -15,6 +15,9 @@
{
"name": "installd_utils_test"
},
+ {
+ "name": "run_dex2oat_test"
+ },
// AdoptableHostTest moves packages, part of which is handled by installd
{
"name": "AdoptableHostTest"
diff --git a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
new file mode 100644
index 0000000..96d7faa
--- /dev/null
+++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable CreateAppDataArgs {
+ @nullable @utf8InCpp String uuid;
+ @utf8InCpp String packageName;
+ int userId;
+ int flags;
+ int appId;
+ @utf8InCpp String seInfo;
+ int targetSdkVersion;
+}
diff --git a/cmds/installd/binder/android/os/CreateAppDataResult.aidl b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
new file mode 100644
index 0000000..3b8fa6b
--- /dev/null
+++ b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable CreateAppDataResult {
+ long ceDataInode;
+ int exceptionCode;
+ @utf8InCpp String exceptionMessage;
+}
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index eeda6c5..2538e22 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -21,11 +21,9 @@
void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags);
void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags);
- long createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
- int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion);
- long createAppDataBatched(in @nullable @utf8InCpp String[] uuids,
- in @nullable @utf8InCpp String[] packageNames, in int userId, int flags, in int[] appIds,
- in @utf8InCpp String[] seInfos, in int[] targetSdkVersions);
+ android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args);
+ android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args);
+
void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, int flags, int appId, @utf8InCpp String seInfo);
void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 82be007..2b36067 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -50,11 +50,14 @@
#include "dexopt.h"
#include "dexopt_return_codes.h"
+#include "execv_helper.h"
#include "globals.h"
#include "installd_deps.h"
#include "otapreopt_utils.h"
+#include "run_dex2oat.h"
#include "utils.h"
+using android::base::Basename;
using android::base::EndsWith;
using android::base::GetBoolProperty;
using android::base::GetProperty;
@@ -67,16 +70,6 @@
namespace android {
namespace installd {
-// Should minidebug info be included in compiled artifacts? Even if this value is
-// "true," usage might still be conditional to other constraints, e.g., system
-// property overrides.
-static constexpr bool kEnableMinidebugInfo = true;
-
-static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo";
-static constexpr bool kMinidebugInfoSystemPropertyDefault = false;
-static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info";
-static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none";
-
// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
struct FreeDelete {
@@ -186,93 +179,6 @@
return clear_current_profile(package_name, location, user, /*is_secondary_dex*/false);
}
-static std::vector<std::string> SplitBySpaces(const std::string& str) {
- if (str.empty()) {
- return {};
- }
- return android::base::Split(str, " ");
-}
-
-static const char* get_location_from_path(const char* path) {
- static constexpr char kLocationSeparator = '/';
- const char *location = strrchr(path, kLocationSeparator);
- if (location == nullptr) {
- return path;
- } else {
- // Skip the separator character.
- return location + 1;
- }
-}
-
-// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations
-// need to be performed between the fork and exec.
-class ExecVHelper {
- public:
- // Store a placeholder for the binary name.
- ExecVHelper() : args_(1u, std::string()) {}
-
- void PrepareArgs(const std::string& bin) {
- CHECK(!args_.empty());
- CHECK(args_[0].empty());
- args_[0] = bin;
- // Write char* into array.
- for (const std::string& arg : args_) {
- argv_.push_back(arg.c_str());
- }
- argv_.push_back(nullptr); // Add null terminator.
- }
-
- [[ noreturn ]]
- void Exec(int exit_code) {
- execv(argv_[0], (char * const *)&argv_[0]);
- PLOG(ERROR) << "execv(" << argv_[0] << ") failed";
- exit(exit_code);
- }
-
- // Add an arg if it's not empty.
- void AddArg(const std::string& arg) {
- if (!arg.empty()) {
- args_.push_back(arg);
- }
- }
-
- // Add a runtime arg if it's not empty.
- void AddRuntimeArg(const std::string& arg) {
- if (!arg.empty()) {
- args_.push_back("--runtime-arg");
- args_.push_back(arg);
- }
- }
-
- protected:
- // Holder arrays for backing arg storage.
- std::vector<std::string> args_;
-
- // Argument poiners.
- std::vector<const char*> argv_;
-};
-
-static std::string MapPropertyToArg(const std::string& property,
- const std::string& format,
- const std::string& default_value = "") {
- std::string prop = GetProperty(property, default_value);
- if (!prop.empty()) {
- return StringPrintf(format.c_str(), prop.c_str());
- }
- return "";
-}
-
-static std::string MapPropertyToArgWithBackup(const std::string& property,
- const std::string& backupProperty,
- const std::string& format,
- const std::string& default_value = "") {
- std::string value = GetProperty(property, default_value);
- if (!value.empty()) {
- return StringPrintf(format.c_str(), value.c_str());
- }
- return MapPropertyToArg(backupProperty, format, default_value);
-}
-
// Determines which binary we should use for execution (the debug or non-debug version).
// e.g. dex2oatd vs dex2oat
static const char* select_execution_binary(const char* binary, const char* debug_binary,
@@ -311,9 +217,6 @@
static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
// Feature flag name for running the JIT in Zygote experiment, b/119800099.
static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image";
-// Location of the JIT Zygote image.
-static const char* kJitZygoteImage =
- "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
// Phenotype property name for enabling profiling the boot class path.
static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
@@ -328,289 +231,6 @@
return profile_boot_class_path == "true";
}
-class RunDex2Oat : public ExecVHelper {
- public:
- RunDex2Oat(int zip_fd,
- int oat_fd,
- int input_vdex_fd,
- int output_vdex_fd,
- int image_fd,
- const char* input_file_name,
- const char* output_file_name,
- int swap_fd,
- const char* instruction_set,
- const char* compiler_filter,
- bool debuggable,
- bool post_bootcomplete,
- bool for_restore,
- bool background_job_compile,
- int profile_fd,
- const char* class_loader_context,
- const std::string& class_loader_context_fds,
- int target_sdk_version,
- bool enable_hidden_api_checks,
- bool generate_compact_dex,
- int dex_metadata_fd,
- const char* compilation_reason) {
- // Get the relative path to the input file.
- const char* relative_input_file_name = get_location_from_path(input_file_name);
-
- std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s");
- std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s");
-
- std::string threads_format = "-j%s";
- std::string dex2oat_threads_arg = post_bootcomplete
- ? (for_restore
- ? MapPropertyToArgWithBackup(
- "dalvik.vm.restore-dex2oat-threads",
- "dalvik.vm.dex2oat-threads",
- threads_format)
- : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))
- : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format);
- std::string cpu_set_format = "--cpu-set=%s";
- std::string dex2oat_cpu_set_arg = post_bootcomplete
- ? (for_restore
- ? MapPropertyToArgWithBackup(
- "dalvik.vm.restore-dex2oat-cpu-set",
- "dalvik.vm.dex2oat-cpu-set",
- cpu_set_format)
- : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format))
- : MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format);
-
- std::string bootclasspath;
- char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
- if (dex2oat_bootclasspath != nullptr) {
- bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath);
- }
- // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
- // BOOTCLASSPATH.
-
- const std::string dex2oat_isa_features_key =
- StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
- std::string instruction_set_features_arg =
- MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");
-
- const std::string dex2oat_isa_variant_key =
- StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);
- std::string instruction_set_variant_arg =
- MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");
-
- const char* dex2oat_norelocation = "-Xnorelocate";
-
- const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
- std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
- ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str());
-
- // If we are booting without the real /data, don't spend time compiling.
- std::string vold_decrypt = GetProperty("vold.decrypt", "");
- bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
- vold_decrypt == "1";
-
- std::string updatable_bcp_packages =
- MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
- "--updatable-bcp-packages-file=%s");
- if (updatable_bcp_packages.empty()) {
- // Make dex2oat fail by providing non-existent file name.
- updatable_bcp_packages = "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
- }
-
- std::string resolve_startup_string_arg =
- MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings",
- "--resolve-startup-const-strings=%s");
- if (resolve_startup_string_arg.empty()) {
- // If empty, fall back to system property.
- resolve_startup_string_arg =
- MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
- "--resolve-startup-const-strings=%s");
- }
-
- const std::string image_block_size_arg =
- MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
- "--max-image-block-size=%s");
-
- const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
-
- std::string image_format_arg;
- if (image_fd >= 0) {
- image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s");
- }
-
- std::string dex2oat_large_app_threshold_arg =
- MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
-
-
-
- // Decide whether to use dex2oat64.
- bool use_dex2oat64 = false;
- // Check whether the device even supports 64-bit ABIs.
- if (!GetProperty("ro.product.cpu.abilist64", "").empty()) {
- use_dex2oat64 = GetBoolProperty("dalvik.vm.dex2oat64.enabled", false);
- }
- const char* dex2oat_bin = select_execution_binary(
- (use_dex2oat64 ? kDex2oat64Path : kDex2oat32Path),
- (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path),
- background_job_compile);
-
- bool generate_minidebug_info = kEnableMinidebugInfo &&
- GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
-
- std::string boot_image;
- std::string use_jitzygote_image =
- server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
- ENABLE_JITZYGOTE_IMAGE,
- /*default_value=*/ "");
-
- if (use_jitzygote_image == "true" || IsBootClassPathProfilingEnable()) {
- boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
- } else {
- boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
- }
-
- // clang FORTIFY doesn't let us use strlen in constant array bounds, so we
- // use arraysize instead.
- std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd);
- std::string zip_location_arg = StringPrintf("--zip-location=%s", relative_input_file_name);
- std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd);
- std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd);
- std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd);
- std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name);
- std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set);
- std::string dex2oat_compiler_filter_arg;
- std::string dex2oat_swap_fd;
- std::string dex2oat_image_fd;
- std::string target_sdk_version_arg;
- if (target_sdk_version != 0) {
- target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version);
- }
- std::string class_loader_context_arg;
- std::string class_loader_context_fds_arg;
- if (class_loader_context != nullptr) {
- class_loader_context_arg = StringPrintf("--class-loader-context=%s",
- class_loader_context);
- if (!class_loader_context_fds.empty()) {
- class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s",
- class_loader_context_fds.c_str());
- }
- }
-
- if (swap_fd >= 0) {
- dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd);
- }
- if (image_fd >= 0) {
- dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd);
- }
-
- // Compute compiler filter.
- bool have_dex2oat_relocation_skip_flag = false;
- if (skip_compilation) {
- dex2oat_compiler_filter_arg = "--compiler-filter=extract";
- have_dex2oat_relocation_skip_flag = true;
- } else if (compiler_filter != nullptr) {
- dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter);
- }
-
- if (dex2oat_compiler_filter_arg.empty()) {
- dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
- "--compiler-filter=%s");
- }
-
- // Check whether all apps should be compiled debuggable.
- if (!debuggable) {
- debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";
- }
- std::string profile_arg;
- if (profile_fd != -1) {
- profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd);
- }
-
- // Get the directory of the apk to pass as a base classpath directory.
- std::string base_dir;
- std::string apk_dir(input_file_name);
- unsigned long dir_index = apk_dir.rfind('/');
- bool has_base_dir = dir_index != std::string::npos;
- if (has_base_dir) {
- apk_dir = apk_dir.substr(0, dir_index);
- base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str());
- }
-
- std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd);
-
- std::string compilation_reason_arg = compilation_reason == nullptr
- ? ""
- : std::string("--compilation-reason=") + compilation_reason;
-
- ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);
-
- // Disable cdex if update input vdex is true since this combination of options is not
- // supported.
- const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd);
-
- AddArg(zip_fd_arg);
- AddArg(zip_location_arg);
- AddArg(input_vdex_fd_arg);
- AddArg(output_vdex_fd_arg);
- AddArg(oat_fd_arg);
- AddArg(oat_location_arg);
- AddArg(instruction_set_arg);
-
- AddArg(instruction_set_variant_arg);
- AddArg(instruction_set_features_arg);
-
- AddArg(boot_image);
-
- AddRuntimeArg(bootclasspath);
- AddRuntimeArg(dex2oat_Xms_arg);
- AddRuntimeArg(dex2oat_Xmx_arg);
-
- AddArg(updatable_bcp_packages);
- AddArg(resolve_startup_string_arg);
- AddArg(image_block_size_arg);
- AddArg(dex2oat_compiler_filter_arg);
- AddArg(dex2oat_threads_arg);
- AddArg(dex2oat_cpu_set_arg);
- AddArg(dex2oat_swap_fd);
- AddArg(dex2oat_image_fd);
-
- if (generate_debug_info) {
- AddArg("--generate-debug-info");
- }
- if (debuggable) {
- AddArg("--debuggable");
- }
- AddArg(image_format_arg);
- AddArg(dex2oat_large_app_threshold_arg);
-
- if (have_dex2oat_relocation_skip_flag) {
- AddRuntimeArg(dex2oat_norelocation);
- }
- AddArg(profile_arg);
- AddArg(base_dir);
- AddArg(class_loader_context_arg);
- AddArg(class_loader_context_fds_arg);
- if (generate_minidebug_info) {
- AddArg(kMinidebugDex2oatFlag);
- }
- if (disable_cdex) {
- AddArg(kDisableCompactDexFlag);
- }
- AddRuntimeArg(target_sdk_version_arg);
- if (enable_hidden_api_checks) {
- AddRuntimeArg("-Xhidden-api-policy:enabled");
- }
-
- if (dex_metadata_fd > -1) {
- AddArg(dex_metadata_fd_arg);
- }
-
- AddArg(compilation_reason_arg);
-
- // Do not add args after dex2oat_flags, they should override others for debugging.
- args_.insert(args_.end(), dex2oat_flags_args.begin(), dex2oat_flags_args.end());
-
- PrepareArgs(dex2oat_bin);
- }
-};
-
/*
* Whether dexopt should use a swap file when compiling an APK.
*
@@ -868,6 +488,7 @@
/*for_boot_image*/false);
}
+ using ExecVHelper::Exec; // To suppress -Wno-overloaded-virtual
void Exec() {
ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec);
}
@@ -1022,7 +643,7 @@
PLOG(ERROR) << "installd cannot open " << code_path.c_str();
return false;
}
- dex_locations.push_back(get_location_from_path(code_path.c_str()));
+ dex_locations.push_back(Basename(code_path));
apk_fds.push_back(std::move(apk_fd));
@@ -2220,9 +1841,29 @@
}
}
+ std::string jitzygote_flag = server_configurable_flags::GetServerConfigurableFlag(
+ RUNTIME_NATIVE_BOOT_NAMESPACE,
+ ENABLE_JITZYGOTE_IMAGE,
+ /*default_value=*/ "");
+ bool use_jitzygote_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable();
+
+ // Decide whether to use dex2oat64.
+ bool use_dex2oat64 = false;
+ // Check whether the device even supports 64-bit ABIs.
+ if (!GetProperty("ro.product.cpu.abilist64", "").empty()) {
+ use_dex2oat64 = GetBoolProperty("dalvik.vm.dex2oat64.enabled", false);
+ }
+ const char* dex2oat_bin = select_execution_binary(
+ (use_dex2oat64 ? kDex2oat64Path : kDex2oat32Path),
+ (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path),
+ background_job_compile);
+
+ auto execv_helper = std::make_unique<ExecVHelper>();
+
LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---";
- RunDex2Oat runner(input_fd.get(),
+ RunDex2Oat runner(dex2oat_bin, execv_helper.get());
+ runner.Initialize(input_fd.get(),
out_oat_fd.get(),
in_vdex_fd.get(),
out_vdex_fd.get(),
@@ -2235,7 +1876,6 @@
debuggable,
boot_complete,
for_restore,
- background_job_compile,
reference_profile_fd.get(),
class_loader_context,
join_fds(context_input_fds),
@@ -2243,6 +1883,7 @@
enable_hidden_api_checks,
generate_compact_dex,
dex_metadata_fd.get(),
+ use_jitzygote_image,
compilation_reason);
pid_t pid = fork();
diff --git a/cmds/installd/execv_helper.cpp b/cmds/installd/execv_helper.cpp
new file mode 100644
index 0000000..a2d240a
--- /dev/null
+++ b/cmds/installd/execv_helper.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 "installd"
+
+#include "execv_helper.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+namespace android {
+namespace installd {
+
+// Store a placeholder for the binary name.
+ExecVHelper::ExecVHelper() : args_(1u, std::string()) {}
+
+ExecVHelper::~ExecVHelper() {}
+
+void ExecVHelper::PrepareArgs(const std::string& bin) {
+ CHECK(!args_.empty());
+ CHECK(args_[0].empty());
+ args_[0] = bin;
+ // Write char* into array.
+ for (const std::string& arg : args_) {
+ argv_.push_back(arg.c_str());
+ }
+ argv_.push_back(nullptr); // Add null terminator.
+}
+
+void ExecVHelper::Exec(int exit_code) {
+ execv(argv_[0], (char * const *)&argv_[0]);
+ PLOG(ERROR) << "execv(" << argv_[0] << ") failed";
+ exit(exit_code);
+}
+
+void ExecVHelper::AddArg(const std::string& arg) {
+ if (!arg.empty()) {
+ args_.push_back(arg);
+ }
+}
+
+void ExecVHelper::AddRuntimeArg(const std::string& arg) {
+ if (!arg.empty()) {
+ args_.push_back("--runtime-arg");
+ args_.push_back(arg);
+ }
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/execv_helper.h b/cmds/installd/execv_helper.h
new file mode 100644
index 0000000..9adfc0e
--- /dev/null
+++ b/cmds/installd/execv_helper.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_EXECV_HELPER_H
+#define ANDROID_INSTALLD_EXECV_HELPER_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace installd {
+
+// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations
+// need to be performed between the fork and exec.
+class ExecVHelper {
+ public:
+ ExecVHelper();
+ virtual ~ExecVHelper();
+
+ [[ noreturn ]]
+ virtual void Exec(int exit_code);
+
+ void PrepareArgs(const std::string& bin);
+
+ // Add an arg if it's not empty.
+ void AddArg(const std::string& arg);
+
+ // Add a runtime arg if it's not empty.
+ void AddRuntimeArg(const std::string& arg);
+
+ protected:
+ // Holder arrays for backing arg storage.
+ std::vector<std::string> args_;
+
+ // Argument poiners.
+ std::vector<const char*> argv_;
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_EXECV_HELPER_H
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
new file mode 100644
index 0000000..8cac58f
--- /dev/null
+++ b/cmds/installd/run_dex2oat.cpp
@@ -0,0 +1,379 @@
+/*
+ * 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 "installd"
+
+#include "run_dex2oat.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <log/log.h>
+#include <server_configurable_flags/get_flags.h>
+
+using android::base::Basename;
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+namespace {
+
+// Should minidebug info be included in compiled artifacts? Even if this value is
+// "true," usage might still be conditional to other constraints, e.g., system
+// property overrides.
+static constexpr bool kEnableMinidebugInfo = true;
+
+static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo";
+static constexpr bool kMinidebugInfoSystemPropertyDefault = false;
+static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info";
+static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none";
+
+// Location of the JIT Zygote image.
+static const char* kJitZygoteImage =
+ "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
+
+std::vector<std::string> SplitBySpaces(const std::string& str) {
+ if (str.empty()) {
+ return {};
+ }
+ return android::base::Split(str, " ");
+}
+
+} // namespace
+
+RunDex2Oat::RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper)
+ : dex2oat_bin_(dex2oat_bin), execv_helper_(execv_helper) {}
+
+void RunDex2Oat::Initialize(int zip_fd,
+ int oat_fd,
+ int input_vdex_fd,
+ int output_vdex_fd,
+ int image_fd,
+ const char* input_file_name,
+ const char* output_file_name,
+ int swap_fd,
+ const char* instruction_set,
+ const char* compiler_filter,
+ bool debuggable,
+ bool post_bootcomplete,
+ bool for_restore,
+ int profile_fd,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds,
+ int target_sdk_version,
+ bool enable_hidden_api_checks,
+ bool generate_compact_dex,
+ int dex_metadata_fd,
+ bool use_jitzygote_image,
+ const char* compilation_reason) {
+ // Get the relative path to the input file.
+ std::string input_basename = Basename(input_file_name);
+
+ std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s");
+ std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s");
+
+ std::string threads_format = "-j%s";
+ std::string dex2oat_threads_arg = post_bootcomplete
+ ? (for_restore
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.restore-dex2oat-threads",
+ "dalvik.vm.dex2oat-threads",
+ threads_format)
+ : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))
+ : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format);
+ std::string cpu_set_format = "--cpu-set=%s";
+ std::string dex2oat_cpu_set_arg = post_bootcomplete
+ ? (for_restore
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.restore-dex2oat-cpu-set",
+ "dalvik.vm.dex2oat-cpu-set",
+ cpu_set_format)
+ : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format))
+ : MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format);
+
+ std::string bootclasspath;
+ char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
+ if (dex2oat_bootclasspath != nullptr) {
+ bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath);
+ }
+ // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
+ // BOOTCLASSPATH.
+
+ const std::string dex2oat_isa_features_key =
+ StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
+ std::string instruction_set_features_arg =
+ MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");
+
+ const std::string dex2oat_isa_variant_key =
+ StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);
+ std::string instruction_set_variant_arg =
+ MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");
+
+ const char* dex2oat_norelocation = "-Xnorelocate";
+
+ const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
+ std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
+ ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str());
+
+ // If we are booting without the real /data, don't spend time compiling.
+ std::string vold_decrypt = GetProperty("vold.decrypt", "");
+ bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
+ vold_decrypt == "1";
+
+ std::string updatable_bcp_packages =
+ MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
+ "--updatable-bcp-packages-file=%s");
+ if (updatable_bcp_packages.empty()) {
+ // Make dex2oat fail by providing non-existent file name.
+ updatable_bcp_packages = "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
+ }
+
+ std::string resolve_startup_string_arg =
+ MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings",
+ "--resolve-startup-const-strings=%s");
+ if (resolve_startup_string_arg.empty()) {
+ // If empty, fall back to system property.
+ resolve_startup_string_arg =
+ MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
+ "--resolve-startup-const-strings=%s");
+ }
+
+ const std::string image_block_size_arg =
+ MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
+ "--max-image-block-size=%s");
+
+ const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
+
+ std::string image_format_arg;
+ if (image_fd >= 0) {
+ image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s");
+ }
+
+ std::string dex2oat_large_app_threshold_arg =
+ MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
+
+ bool generate_minidebug_info = kEnableMinidebugInfo &&
+ GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
+
+ std::string boot_image;
+ if (use_jitzygote_image) {
+ boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
+ } else {
+ boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
+ }
+
+ // clang FORTIFY doesn't let us use strlen in constant array bounds, so we
+ // use arraysize instead.
+ std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd);
+ std::string zip_location_arg = StringPrintf("--zip-location=%s", input_basename.c_str());
+ std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd);
+ std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd);
+ std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd);
+ std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name);
+ std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set);
+ std::string dex2oat_compiler_filter_arg;
+ std::string dex2oat_swap_fd;
+ std::string dex2oat_image_fd;
+ std::string target_sdk_version_arg;
+ if (target_sdk_version != 0) {
+ target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version);
+ }
+ std::string class_loader_context_arg;
+ std::string class_loader_context_fds_arg;
+ if (class_loader_context != nullptr) {
+ class_loader_context_arg = StringPrintf("--class-loader-context=%s",
+ class_loader_context);
+ if (!class_loader_context_fds.empty()) {
+ class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s",
+ class_loader_context_fds.c_str());
+ }
+ }
+
+ if (swap_fd >= 0) {
+ dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd);
+ }
+ if (image_fd >= 0) {
+ dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd);
+ }
+
+ // Compute compiler filter.
+ bool have_dex2oat_relocation_skip_flag = false;
+ if (skip_compilation) {
+ dex2oat_compiler_filter_arg = "--compiler-filter=extract";
+ have_dex2oat_relocation_skip_flag = true;
+ } else if (compiler_filter != nullptr) {
+ dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter);
+ }
+
+ if (dex2oat_compiler_filter_arg.empty()) {
+ dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
+ "--compiler-filter=%s");
+ }
+
+ // Check whether all apps should be compiled debuggable.
+ if (!debuggable) {
+ debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";
+ }
+ std::string profile_arg;
+ if (profile_fd != -1) {
+ profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd);
+ }
+
+ // Get the directory of the apk to pass as a base classpath directory.
+ std::string base_dir;
+ std::string apk_dir(input_file_name);
+ unsigned long dir_index = apk_dir.rfind('/');
+ bool has_base_dir = dir_index != std::string::npos;
+ if (has_base_dir) {
+ apk_dir = apk_dir.substr(0, dir_index);
+ base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str());
+ }
+
+ std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd);
+
+ std::string compilation_reason_arg = compilation_reason == nullptr
+ ? ""
+ : std::string("--compilation-reason=") + compilation_reason;
+
+ ALOGV("Running %s in=%s out=%s\n", dex2oat_bin_.c_str(), input_basename.c_str(),
+ output_file_name);
+
+ // Disable cdex if update input vdex is true since this combination of options is not
+ // supported.
+ const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd);
+
+ AddArg(zip_fd_arg);
+ AddArg(zip_location_arg);
+ AddArg(input_vdex_fd_arg);
+ AddArg(output_vdex_fd_arg);
+ AddArg(oat_fd_arg);
+ AddArg(oat_location_arg);
+ AddArg(instruction_set_arg);
+
+ AddArg(instruction_set_variant_arg);
+ AddArg(instruction_set_features_arg);
+
+ AddArg(boot_image);
+
+ AddRuntimeArg(bootclasspath);
+ AddRuntimeArg(dex2oat_Xms_arg);
+ AddRuntimeArg(dex2oat_Xmx_arg);
+
+ AddArg(updatable_bcp_packages);
+ AddArg(resolve_startup_string_arg);
+ AddArg(image_block_size_arg);
+ AddArg(dex2oat_compiler_filter_arg);
+ AddArg(dex2oat_threads_arg);
+ AddArg(dex2oat_cpu_set_arg);
+ AddArg(dex2oat_swap_fd);
+ AddArg(dex2oat_image_fd);
+
+ if (generate_debug_info) {
+ AddArg("--generate-debug-info");
+ }
+ if (debuggable) {
+ AddArg("--debuggable");
+ }
+ AddArg(image_format_arg);
+ AddArg(dex2oat_large_app_threshold_arg);
+
+ if (have_dex2oat_relocation_skip_flag) {
+ AddRuntimeArg(dex2oat_norelocation);
+ }
+ AddArg(profile_arg);
+ AddArg(base_dir);
+ AddArg(class_loader_context_arg);
+ AddArg(class_loader_context_fds_arg);
+ if (generate_minidebug_info) {
+ AddArg(kMinidebugDex2oatFlag);
+ }
+ if (disable_cdex) {
+ AddArg(kDisableCompactDexFlag);
+ }
+ AddRuntimeArg(target_sdk_version_arg);
+ if (enable_hidden_api_checks) {
+ AddRuntimeArg("-Xhidden-api-policy:enabled");
+ }
+
+ if (dex_metadata_fd > -1) {
+ AddArg(dex_metadata_fd_arg);
+ }
+
+ AddArg(compilation_reason_arg);
+
+ // Do not add args after dex2oat_flags, they should override others for debugging.
+ for (auto it = dex2oat_flags_args.begin(); it != dex2oat_flags_args.end(); ++it) {
+ AddArg(*it);
+ }
+
+ execv_helper_->PrepareArgs(dex2oat_bin_);
+}
+
+RunDex2Oat::~RunDex2Oat() {}
+
+void RunDex2Oat::Exec(int exit_code) {
+ LOG(ERROR) << "RunDex2Oat::Exec";
+ execv_helper_->Exec(exit_code);
+}
+
+void RunDex2Oat::AddArg(const std::string& arg) {
+ execv_helper_->AddArg(arg);
+}
+
+void RunDex2Oat::AddRuntimeArg(const std::string& arg) {
+ execv_helper_->AddRuntimeArg(arg);
+}
+
+std::string RunDex2Oat::GetProperty(const std::string& key,
+ const std::string& default_value) {
+ return android::base::GetProperty(key, default_value);
+}
+
+bool RunDex2Oat::GetBoolProperty(const std::string& key, bool default_value) {
+ return android::base::GetBoolProperty(key, default_value);
+}
+
+std::string RunDex2Oat::MapPropertyToArg(const std::string& property,
+ const std::string& format,
+ const std::string& default_value) {
+ std::string prop = GetProperty(property, default_value);
+ if (!prop.empty()) {
+ return StringPrintf(format.c_str(), prop.c_str());
+ }
+ return "";
+}
+
+std::string RunDex2Oat::MapPropertyToArgWithBackup(
+ const std::string& property,
+ const std::string& backupProperty,
+ const std::string& format,
+ const std::string& default_value) {
+ std::string value = GetProperty(property, default_value);
+ if (!value.empty()) {
+ return StringPrintf(format.c_str(), value.c_str());
+ }
+ return MapPropertyToArg(backupProperty, format, default_value);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
new file mode 100644
index 0000000..e4450b0
--- /dev/null
+++ b/cmds/installd/run_dex2oat.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_RUN_DEX2OAT_H
+#define ANDROID_INSTALLD_RUN_DEX2OAT_H
+
+#include <memory>
+#include <string>
+
+#include "execv_helper.h"
+
+namespace android {
+namespace installd {
+
+class RunDex2Oat {
+ public:
+ explicit RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper);
+ virtual ~RunDex2Oat();
+
+ void Initialize(int zip_fd,
+ int oat_fd,
+ int input_vdex_fd,
+ int output_vdex_fd,
+ int image_fd,
+ const char* input_file_name,
+ const char* output_file_name,
+ int swap_fd,
+ const char* instruction_set,
+ const char* compiler_filter,
+ bool debuggable,
+ bool post_bootcomplete,
+ bool for_restore,
+ int profile_fd,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds,
+ int target_sdk_version,
+ bool enable_hidden_api_checks,
+ bool generate_compact_dex,
+ int dex_metadata_fd,
+ bool use_jitzygote_image,
+ const char* compilation_reason);
+
+ void Exec(int exit_code);
+
+ protected:
+ virtual std::string GetProperty(const std::string& key, const std::string& default_value);
+ virtual bool GetBoolProperty(const std::string& key, bool default_value);
+
+ private:
+ void AddArg(const std::string& arg);
+ void AddRuntimeArg(const std::string& arg);
+
+ std::string MapPropertyToArg(const std::string& property,
+ const std::string& format,
+ const std::string& default_value = "");
+
+ std::string MapPropertyToArgWithBackup(const std::string& property,
+ const std::string& backupProperty,
+ const std::string& format,
+ const std::string& default_value = "");
+
+ const std::string dex2oat_bin_;
+ ExecVHelper* execv_helper_; // not owned
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_RUN_DEX2OAT_H
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
new file mode 100644
index 0000000..b1f429d
--- /dev/null
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -0,0 +1,588 @@
+/*
+ * 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 <map>
+#include <memory>
+#include <string>
+
+#include <android-base/logging.h>
+
+#include <gtest/gtest.h>
+
+#include "execv_helper.h"
+#include "run_dex2oat.h"
+
+namespace android {
+namespace installd {
+
+class RunDex2OatTest : public testing::Test {
+ public:
+ static constexpr const char* INPUT_PATH = "/dir/input/basename.apk";
+ static constexpr const char* OUTPUT_PATH = "/dir/output/basename.oat";
+ static constexpr const char* FLAG_UNUSED = "{{FLAG_UNUSED}}";
+
+ static constexpr int ZIP_FD = 1;
+ static constexpr int OAT_FD = 2;
+ static constexpr int INPUT_VDEX_FD = 3;
+ static constexpr int OUTPUT_VDEX_FD = 4;
+ static constexpr int IMAGE_FD = 5;
+ static constexpr int PROFILE_FD = 6;
+ static constexpr int DEX_METADATA_FD = 7;
+ static constexpr int SWAP_FD = 8;
+
+ using FakeSystemProperties = std::map<std::string, std::string>;
+
+ // A fake RunDex2Oat that allows to override (fake) system properties and starts with none.
+ class FakeRunDex2Oat : public RunDex2Oat {
+ private:
+ static constexpr const char* TRUE_STR = "true";
+ static constexpr const char* FALSE_STR = "false";
+
+ public:
+ FakeRunDex2Oat(ExecVHelper* execv_helper, FakeSystemProperties* properties)
+ : RunDex2Oat("/dir/bin/dex2oat", execv_helper), properties_(properties) { }
+
+ virtual ~FakeRunDex2Oat() {}
+
+ virtual std::string GetProperty(const std::string& key,
+ const std::string& default_value) override {
+ if (!properties_) {
+ return default_value;
+ }
+ auto iter = properties_->find(key);
+ if (iter == properties_->end()) {
+ return default_value;
+ }
+ return iter->second;
+ }
+
+ virtual bool GetBoolProperty(const std::string& key, bool default_value) override {
+ std::string value = GetProperty(key, "");
+ if (value == "") {
+ return default_value;
+ }
+ return value == TRUE_STR;
+ }
+
+ private:
+ FakeSystemProperties* properties_;
+ };
+
+ struct RunDex2OatArgs {
+ static std::unique_ptr<RunDex2OatArgs> MakeDefaultTestArgs() {
+ auto args = std::make_unique<RunDex2OatArgs>();
+ args->input_file_name = INPUT_PATH;
+ args->zip_fd = ZIP_FD;
+ args->output_file_name = OUTPUT_PATH;
+ args->oat_fd = OAT_FD;
+ args->input_vdex_fd = INPUT_VDEX_FD;
+ args->output_vdex_fd = OUTPUT_VDEX_FD;
+ args->instruction_set = "arm64";
+ args->compilation_reason = "rundex2oattest";
+ return args;
+ }
+
+ int zip_fd = -1;
+ int oat_fd = -1;
+ int input_vdex_fd = -1;
+ int output_vdex_fd = -1;
+ int image_fd = -1;
+ const char* input_file_name = nullptr;
+ const char* output_file_name = nullptr;
+ int swap_fd = -1;
+ const char* instruction_set = nullptr;
+ const char* compiler_filter = "extract";
+ bool debuggable = false;
+ bool post_bootcomplete = false;
+ bool for_restore = false;
+ int profile_fd = -1;
+ const char* class_loader_context = nullptr;
+ std::string class_loader_context_fds;
+ int target_sdk_version = 0;
+ bool enable_hidden_api_checks = false;
+ bool generate_compact_dex = true;
+ int dex_metadata_fd = -1;
+ bool use_jitzygote_image = false;
+ const char* compilation_reason = nullptr;
+ };
+
+ class FakeExecVHelper : public ExecVHelper {
+ public:
+ bool HasArg(const std::string& arg) const {
+ auto end = argv_.end() - 1; // To exclude the terminating nullptr
+ return find(argv_.begin(), end, arg) != end;
+ }
+
+ bool FlagNotUsed(const std::string& flag) const {
+ auto has_prefix = [flag](const char* arg) {
+ return strncmp(arg, flag.c_str(), flag.size()) == 0;
+ };
+ auto end = argv_.end() - 1; // To exclude the terminating nullptr
+ return find_if(argv_.begin(), end, has_prefix) == end;
+ }
+
+ virtual void Exec(int exit_code) override {
+ std::string cmd;
+ for (auto arg : argv_) {
+ if (arg == nullptr) {
+ continue;
+ }
+ cmd += arg;
+ cmd += " ";
+ }
+ LOG(DEBUG) << "FakeExecVHelper exit_code: " << exit_code << " cmd: " << cmd;
+ }
+ };
+
+ virtual void SetUp() override {
+ execv_helper_.reset(new FakeExecVHelper());
+ system_properties_.clear();
+ initializeDefaultExpectedFlags();
+ }
+
+ // Initializes the default flags expected to a run. It currently matches to the expected flags
+ // with RunDex2OatArgs::MakeDefaultTestArgs.
+ //
+ // default_expected_flags_ defines a mapping of <flag_name, expected_value>, where flag_name is
+ // something like "--flag-name", and expected_value can be "=value" or ":value" (depending on
+ // its delimiter), "" (if no value is needed), or a special value of FLAG_UNUSED to indicates
+ // that it should not be used.
+ void initializeDefaultExpectedFlags() {
+ default_expected_flags_.clear();
+
+ // Files
+ default_expected_flags_["--zip-fd"] = "=" + std::to_string(ZIP_FD);
+ default_expected_flags_["--zip-location"] = "=basename.apk";
+ default_expected_flags_["--oat-fd"] = "=" + std::to_string(OAT_FD);
+ default_expected_flags_["--oat-location"] = "=" + std::string(OUTPUT_PATH);
+ default_expected_flags_["--input-vdex-fd"] = "=" + std::to_string(INPUT_VDEX_FD);
+ default_expected_flags_["--output-vdex-fd"] = "=" + std::to_string(OUTPUT_VDEX_FD);
+ default_expected_flags_["--classpath-dir"] = "=/dir/input";
+ default_expected_flags_["--app-image-fd"] = FLAG_UNUSED;
+ default_expected_flags_["--profile-file-fd"] = FLAG_UNUSED;
+ default_expected_flags_["--swap-fd"] = FLAG_UNUSED;
+ default_expected_flags_["--class-loader-context"] = FLAG_UNUSED;
+ default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED;
+ default_expected_flags_["--updatable-bcp-packages-file"] =
+ "=/nonx/updatable-bcp-packages.txt";
+
+ // Arch
+ default_expected_flags_["--instruction-set"] = "=arm64";
+ default_expected_flags_["--instruction-set-features"] = FLAG_UNUSED;
+ default_expected_flags_["--instruction-set-variant"] = FLAG_UNUSED;
+ default_expected_flags_["--cpu-set"] = FLAG_UNUSED;
+
+ // Misc
+ default_expected_flags_["--compiler-filter"] = "=extract";
+ default_expected_flags_["--compilation-reason"] = "=rundex2oattest";
+ default_expected_flags_["--compact-dex-level"] = FLAG_UNUSED;
+ default_expected_flags_["-j"] = FLAG_UNUSED;
+ default_expected_flags_["--max-image-block-size"] = FLAG_UNUSED;
+ default_expected_flags_["--very-large-app-threshold"] = FLAG_UNUSED;
+ default_expected_flags_["--resolve-startup-const-strings"] = FLAG_UNUSED;
+
+ // Debug
+ default_expected_flags_["--debuggable"] = FLAG_UNUSED;
+ default_expected_flags_["--generate-debug-info"] = FLAG_UNUSED;
+ default_expected_flags_["--generate-mini-debug-info"] = FLAG_UNUSED;
+
+ // Runtime
+ // TODO(victorhsieh): Check if the previous flag is actually --runtime-arg.
+ default_expected_flags_["-Xms"] = FLAG_UNUSED;
+ default_expected_flags_["-Xmx"] = FLAG_UNUSED;
+ default_expected_flags_["-Xbootclasspath"] = FLAG_UNUSED;
+ default_expected_flags_["-Xtarget-sdk-version"] = FLAG_UNUSED;
+ default_expected_flags_["-Xhidden-api-policy"] = FLAG_UNUSED;
+ default_expected_flags_["-Xnorelocate"] = FLAG_UNUSED;
+
+ // Test only
+ default_expected_flags_["--foo"] = FLAG_UNUSED;
+ default_expected_flags_["--bar"] = FLAG_UNUSED;
+ default_expected_flags_["--baz"] = FLAG_UNUSED;
+ }
+
+ void SetExpectedFlagUsed(const std::string& flag, const std::string& value) {
+ auto iter = default_expected_flags_.find(flag);
+ ASSERT_NE(iter, default_expected_flags_.end()) << "Must define the default value";
+ iter->second = value;
+ }
+
+ void VerifyExpectedFlags() {
+ for (auto const& [flag, value] : default_expected_flags_) {
+ if (value == FLAG_UNUSED) {
+ EXPECT_TRUE(execv_helper_->FlagNotUsed(flag))
+ << "Flag " << flag << " should be unused, but got the value " << value;
+ } else if (value == "") {
+ EXPECT_TRUE(execv_helper_->HasArg(flag))
+ << "Flag " << flag << " should be specified without value, but got " << value;
+ } else {
+ EXPECT_TRUE(execv_helper_->HasArg(flag + value))
+ << "Flag " << flag << "=" << value << " is not specificed";
+ }
+ }
+ }
+
+ void setSystemProperty(const std::string& key, const std::string& value) {
+ system_properties_[key] = value;
+ }
+
+ void CallRunDex2Oat(std::unique_ptr<RunDex2OatArgs> args) {
+ FakeRunDex2Oat runner(execv_helper_.get(), &system_properties_);
+ runner.Initialize(args->zip_fd,
+ args->oat_fd,
+ args->input_vdex_fd,
+ args->output_vdex_fd,
+ args->image_fd,
+ args->input_file_name,
+ args->output_file_name,
+ args->swap_fd,
+ args->instruction_set,
+ args->compiler_filter,
+ args->debuggable,
+ args->post_bootcomplete,
+ args->for_restore,
+ args->profile_fd,
+ args->class_loader_context,
+ args->class_loader_context_fds,
+ args->target_sdk_version,
+ args->enable_hidden_api_checks,
+ args->generate_compact_dex,
+ args->dex_metadata_fd,
+ args->use_jitzygote_image,
+ args->compilation_reason);
+ runner.Exec(/*exit_code=*/ 0);
+ }
+
+ private:
+ std::unique_ptr<FakeExecVHelper> execv_helper_;
+ std::map<std::string, std::string> default_expected_flags_;
+ FakeSystemProperties system_properties_;
+};
+
+TEST_F(RunDex2OatTest, BasicInputOutput) {
+ auto execv_helper = std::make_unique<FakeExecVHelper>();
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithAllOtherInputFds) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->image_fd = IMAGE_FD;
+ args->profile_fd = PROFILE_FD;
+ args->swap_fd = SWAP_FD;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--app-image-fd", "=" + std::to_string(IMAGE_FD));
+ SetExpectedFlagUsed("--profile-file-fd", "=" + std::to_string(PROFILE_FD));
+ SetExpectedFlagUsed("--swap-fd", "=" + std::to_string(SWAP_FD));
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithClassLoaderContext) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->class_loader_context = "CLASS_LOADER_CONTEXT";
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--class-loader-context", "=CLASS_LOADER_CONTEXT");
+ SetExpectedFlagUsed("--class-loader-context-fds", FLAG_UNUSED);
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithClassLoaderContextAndFds) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->class_loader_context = "CLASS_LOADER_CONTEXT";
+ args->class_loader_context_fds = "CLASS_LOADER_CONTEXT_FDS";
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--class-loader-context", "=CLASS_LOADER_CONTEXT");
+ SetExpectedFlagUsed("--class-loader-context-fds", "=CLASS_LOADER_CONTEXT_FDS");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, WithOnlyClassLoaderContextFds) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->class_loader_context_fds = "CLASS_LOADER_CONTEXT_FDS";
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--class-loader-context", FLAG_UNUSED);
+ SetExpectedFlagUsed("--class-loader-context-fds", FLAG_UNUSED);
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DEX2OATBOOTCLASSPATH) {
+ ASSERT_EQ(nullptr, getenv("DEX2OATBOOTCLASSPATH"));
+ ASSERT_EQ(0, setenv("DEX2OATBOOTCLASSPATH", "foobar", /*override=*/ false))
+ << "Failed to setenv: " << strerror(errno);
+
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("-Xbootclasspath", ":foobar");
+ VerifyExpectedFlags();
+
+ ASSERT_EQ(0, unsetenv("DEX2OATBOOTCLASSPATH"))
+ << "Failed to setenv: " << strerror(errno);
+}
+
+TEST_F(RunDex2OatTest, UpdatableBootClassPath) {
+ setSystemProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", "/path/to/file");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--updatable-bcp-packages-file", "=/path/to/file");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DoNotGenerateCompactDex) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->generate_compact_dex = false;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--compact-dex-level", "=none");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DoNotGenerateCompactDexWithVdexInPlaceUpdate) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->generate_compact_dex = true;
+ args->input_vdex_fd = INPUT_VDEX_FD;
+ args->output_vdex_fd = INPUT_VDEX_FD;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--compact-dex-level", "=none");
+ SetExpectedFlagUsed("--output-vdex-fd", "=" + std::to_string(INPUT_VDEX_FD));
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ISA) {
+ setSystemProperty("dalvik.vm.isa.x86.features", "a-x86-feature");
+ setSystemProperty("dalvik.vm.isa.x86.variant", "a-x86-variant");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->instruction_set = "x86";
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--instruction-set", "=x86");
+ SetExpectedFlagUsed("--instruction-set-features", "=a-x86-feature");
+ SetExpectedFlagUsed("--instruction-set-variant", "=a-x86-variant");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPreBootComplete) {
+ setSystemProperty("dalvik.vm.boot-dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = false;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteNotForRestore) {
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = false;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore) {
+ setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "1,2");
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "2,3");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore_Backup) {
+ setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "");
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, Runtime) {
+ setSystemProperty("dalvik.vm.dex2oat-Xms", "1234m");
+ setSystemProperty("dalvik.vm.dex2oat-Xmx", "5678m");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->target_sdk_version = 30;
+ args->enable_hidden_api_checks = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-Xms", "1234m");
+ SetExpectedFlagUsed("-Xmx", "5678m");
+ SetExpectedFlagUsed("-Xtarget-sdk-version", ":30");
+ SetExpectedFlagUsed("-Xhidden-api-policy", ":enabled");
+ SetExpectedFlagUsed("-Xnorelocate", FLAG_UNUSED);
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) {
+ setSystemProperty("vold.decrypt", "trigger_restart_min_framework");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--compiler-filter", "=extract");
+ SetExpectedFlagUsed("-Xnorelocate", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) {
+ setSystemProperty("vold.decrypt", "1");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--compiler-filter", "=extract");
+ SetExpectedFlagUsed("-Xnorelocate", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) {
+ setSystemProperty("dalvik.vm.dex2oat-filter", "speed");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->compiler_filter = nullptr;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--compiler-filter", "=speed");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ResolveStartupStartings) {
+ setSystemProperty("dalvik.vm.dex2oat-resolve-startup-strings", "false");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--resolve-startup-const-strings", "=false");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ResolveStartupStartingsOverride) {
+ setSystemProperty("dalvik.vm.dex2oat-resolve-startup-strings", "false");
+ setSystemProperty("persist.device_config.runtime.dex2oat_resolve_startup_strings", "true");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--resolve-startup-const-strings", "=true");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPreBootComplete) {
+ setSystemProperty("dalvik.vm.boot-dex2oat-threads", "2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = false;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteNotForRestore) {
+ setSystemProperty("dalvik.vm.dex2oat-threads", "3");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = false;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "3");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore) {
+ setSystemProperty("dalvik.vm.restore-dex2oat-threads", "4");
+ setSystemProperty("dalvik.vm.dex2oat-threads", "5");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "4");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore_Backup) {
+ setSystemProperty("dalvik.vm.restore-dex2oat-threads", "");
+ setSystemProperty("dalvik.vm.dex2oat-threads", "5");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->for_restore = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "5");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, Debuggable) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->debuggable = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--debuggable", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, AlwaysDebuggable) {
+ setSystemProperty("dalvik.vm.always_debuggable", "1");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--debuggable", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, GenerateDebugInfo) {
+ setSystemProperty("debug.generate-debug-info", "true");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--generate-debug-info", "");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, HiddenApiCheck) {
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->enable_hidden_api_checks = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-Xhidden-api-policy", ":enabled");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, Misc) {
+ setSystemProperty("dalvik.vm.dex2oat-max-image-block-size", "524288");
+ setSystemProperty("dalvik.vm.dex2oat-very-large", "100000");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--max-image-block-size", "=524288");
+ SetExpectedFlagUsed("--very-large-app-threshold", "=100000");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ExtraFlags) {
+ setSystemProperty("dalvik.vm.dex2oat-flags", "--foo=123 --bar:456 --baz");
+ CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
+
+ SetExpectedFlagUsed("--foo", "=123");
+ SetExpectedFlagUsed("--bar", ":456");
+ SetExpectedFlagUsed("--baz", "");
+ VerifyExpectedFlags();
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/run_dex2oat_test.xml b/cmds/installd/run_dex2oat_test.xml
new file mode 100644
index 0000000..2467fe4
--- /dev/null
+++ b/cmds/installd/run_dex2oat_test.xml
@@ -0,0 +1,24 @@
+<?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="Unittest of run_dex2oat">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <option name="null-device" value="true" />
+ <test class="com.android.tradefed.testtype.HostGTest" >
+ <option name="module-name" value="run_dex2oat_test" />
+ </test>
+</configuration>
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index a805a48..92958d9 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -916,6 +916,18 @@
}
}
+// Get all values of enum type T, assuming the first value is 0 and the last value is T::LAST.
+// T::LAST is not included in the returned list.
+template <typename T>
+std::vector<T> GetAllValues() {
+ using BaseType = std::underlying_type_t<T>;
+ std::vector<T> ret;
+ for (BaseType i = 0; i < static_cast<BaseType>(T::LAST); ++i) {
+ ret.push_back(static_cast<T>(i));
+ }
+ return ret;
+}
+
void ListCommand::registerAllOptions() {
int v = mOptions.size();
// A list of acceptable command line options
@@ -989,6 +1001,15 @@
" - declared: only declared in VINTF manifest but is not registered to hwservicemanager;\n"
" - N/A: no information for passthrough HALs."});
+ mOptions.push_back({'A', "all", no_argument, v++,
+ [](ListCommand* thiz, const char*) {
+ auto allColumns = GetAllValues<TableColumnType>();
+ thiz->mSelectedColumns.insert(thiz->mSelectedColumns.end(),
+ allColumns.begin(), allColumns.end());
+ return OK;
+ },
+ "print all columns"});
+
// long options without short alternatives
mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
thiz->mVintf = true;
@@ -1019,46 +1040,55 @@
thiz->mNeat = true;
return OK;
}, "output is machine parsable (no explanatory text).\nCannot be used with --debug."});
- mOptions.push_back({'\0', "types", required_argument, v++, [](ListCommand* thiz, const char* arg) {
- if (!arg) { return USAGE; }
+ mOptions.push_back(
+ {'\0', "types", required_argument, v++,
+ [](ListCommand* thiz, const char* arg) {
+ if (!arg) {
+ return USAGE;
+ }
- static const std::map<std::string, HalType> kHalTypeMap {
- {"binderized", HalType::BINDERIZED_SERVICES},
- {"b", HalType::BINDERIZED_SERVICES},
- {"passthrough_clients", HalType::PASSTHROUGH_CLIENTS},
- {"c", HalType::PASSTHROUGH_CLIENTS},
- {"passthrough_libs", HalType::PASSTHROUGH_LIBRARIES},
- {"l", HalType::PASSTHROUGH_LIBRARIES},
- {"vintf", HalType::VINTF_MANIFEST},
- {"v", HalType::VINTF_MANIFEST},
- {"lazy", HalType::LAZY_HALS},
- {"z", HalType::LAZY_HALS},
- };
+ static const std::map<std::string, std::vector<HalType>> kHalTypeMap{
+ {"binderized", {HalType::BINDERIZED_SERVICES}},
+ {"b", {HalType::BINDERIZED_SERVICES}},
+ {"passthrough_clients", {HalType::PASSTHROUGH_CLIENTS}},
+ {"c", {HalType::PASSTHROUGH_CLIENTS}},
+ {"passthrough_libs", {HalType::PASSTHROUGH_LIBRARIES}},
+ {"l", {HalType::PASSTHROUGH_LIBRARIES}},
+ {"vintf", {HalType::VINTF_MANIFEST}},
+ {"v", {HalType::VINTF_MANIFEST}},
+ {"lazy", {HalType::LAZY_HALS}},
+ {"z", {HalType::LAZY_HALS}},
+ {"all", GetAllValues<HalType>()},
+ {"a", GetAllValues<HalType>()},
+ };
- std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
- for (const auto& halTypeArg : halTypesArgs) {
- if (halTypeArg.empty()) continue;
+ std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
+ for (const auto& halTypeArg : halTypesArgs) {
+ if (halTypeArg.empty()) continue;
- const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
- if (halTypeIter == kHalTypeMap.end()) {
+ const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
+ if (halTypeIter == kHalTypeMap.end()) {
+ thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
+ return USAGE;
+ }
- thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
- return USAGE;
- }
+ // Append unique (non-repeated) HAL types to the reporting list
+ for (auto halType : halTypeIter->second) {
+ if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
+ thiz->mListTypes.end()) {
+ thiz->mListTypes.push_back(halType);
+ }
+ }
+ }
- // Append unique (non-repeated) HAL types to the reporting list
- HalType halType = halTypeIter->second;
- if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
- thiz->mListTypes.end()) {
- thiz->mListTypes.push_back(halType);
- }
- }
-
- if (thiz->mListTypes.empty()) { return USAGE; }
- return OK;
- }, "comma-separated list of one or more sections.\nThe output is restricted to the selected "
- "section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
- "passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."});
+ if (thiz->mListTypes.empty()) {
+ return USAGE;
+ }
+ return OK;
+ },
+ "comma-separated list of one or more sections.\nThe output is restricted to the "
+ "selected section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
+ "passthrough_libs), (v|vintf), (z|lazy), and (a|all).\nDefault is `b,c,l`."});
}
// Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index acc0dcf..412aadd 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -52,6 +52,9 @@
PASSTHROUGH_LIBRARIES,
VINTF_MANIFEST,
LAZY_HALS,
+
+ // Not a real HalType. Used to determine all HalTypes.
+ LAST,
};
class ListCommand : public Command {
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index 0ff0c96..3c36813 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -35,19 +35,25 @@
using Pids = std::vector<int32_t>;
enum class TableColumnType : unsigned int {
- INTERFACE_NAME,
+ INTERFACE_NAME = 0,
TRANSPORT,
SERVER_PID,
- SERVER_CMD,
SERVER_ADDR,
- CLIENT_PIDS,
- CLIENT_CMDS,
ARCH,
THREADS,
RELEASED,
HASH,
VINTF,
SERVICE_STATUS,
+ CLIENT_PIDS,
+
+ // Not a real TableColumnType. Used to determine all TableColumnTypes.
+ LAST,
+
+ // Not included in all TableColumnTypes because they replace *PID(S) when the
+ // --cmdline option is set.
+ SERVER_CMD,
+ CLIENT_CMDS,
};
enum : unsigned int {
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index afe5d63..9964888 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -708,8 +708,8 @@
TEST_F(ListTest, UnknownHalType) {
optind = 1; // mimic Lshal::parseArg()
- EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,a"})));
- EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: a"));
+ EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,r"})));
+ EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: r"));
}
TEST_F(ListTest, Vintf) {
@@ -793,6 +793,71 @@
EXPECT_EQ("", err.str());
}
+TEST_F(ListTest, AllColumns) {
+ // clang-format off
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+ "a.h.foo1@1.0::IFoo/1 hwbinder 1 0000000000002711 64 11/21 N 0000000000000000000000000000000000000000000000000000000000000000 X alive 2 4\n"
+ "a.h.foo2@2.0::IFoo/2 hwbinder 2 0000000000002712 64 12/22 Y 0202020202020202020202020202020202020202020202020202020202020202 X alive 3 5\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough N/A N/A 32 N/A ? X N/A 4 6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough N/A N/A 32 N/A ? X N/A 5 7\n"
+ "\n"
+ "[fake description 2]\n"
+ "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough N/A N/A 32 N/A ? X N/A 6 8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough N/A N/A 32 N/A ? X N/A 7 9\n"
+ "\n";
+ // clang-format on
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--all"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, AllColumnsWithCmd) {
+ // clang-format off
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+ "a.h.foo1@1.0::IFoo/1 hwbinder command_line_1 0000000000002711 64 11/21 N 0000000000000000000000000000000000000000000000000000000000000000 X alive command_line_2;command_line_4\n"
+ "a.h.foo2@2.0::IFoo/2 hwbinder command_line_2 0000000000002712 64 12/22 Y 0202020202020202020202020202020202020202020202020202020202020202 X alive command_line_3;command_line_5\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough N/A 32 N/A ? X N/A command_line_4;command_line_6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough N/A 32 N/A ? X N/A command_line_5;command_line_7\n"
+ "\n"
+ "[fake description 2]\n"
+ "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough N/A 32 N/A ? X N/A command_line_6;command_line_8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough N/A 32 N/A ? X N/A command_line_7;command_line_9\n"
+ "\n";
+ // clang-format on
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-Am"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, AllSections) {
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--types=all"})));
+ using HalTypeBase = std::underlying_type_t<HalType>;
+ for (HalTypeBase i = 0; i < static_cast<HalTypeBase>(HalType::LAST); ++i) {
+ EXPECT_THAT(out.str(), HasSubstr("[fake description " + std::to_string(i) + "]"));
+ }
+ EXPECT_THAT(out.str(),
+ Not(HasSubstr("[fake description " +
+ std::to_string(static_cast<HalTypeBase>(HalType::LAST)) + "]")));
+ EXPECT_EQ("", err.str());
+}
+
// Fake service returned by mocked IServiceManager::get for DumpDebug.
// The interfaceChain and getHashChain functions returns
// foo(id - 1) -> foo(id - 2) -> ... foo1 -> IBase.
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 1f9892a..e80c321 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -213,17 +213,18 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- auto entry = mNameToService.emplace(name, Service {
+ // Overwrite the old service if it exists
+ mNameToService[name] = Service {
.binder = binder,
.allowIsolated = allowIsolated,
.dumpPriority = dumpPriority,
.debugPid = ctx.debugPid,
- });
+ };
auto it = mNameToRegistrationCallback.find(name);
if (it != mNameToRegistrationCallback.end()) {
for (const sp<IServiceCallback>& cb : it->second) {
- entry.first->second.guaranteeClient = true;
+ mNameToService[name].guaranteeClient = true;
// permission checked in registerForNotifications
cb->onRegistration(name, binder);
}
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 25245be..fb9f9df 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -135,6 +135,26 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
}
+TEST(AddService, OverwriteExistingService) {
+ auto sm = getPermissiveServiceManager();
+ sp<IBinder> serviceA = getBinder();
+ EXPECT_TRUE(sm->addService("foo", serviceA, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ sp<IBinder> outA;
+ EXPECT_TRUE(sm->getService("foo", &outA).isOk());
+ EXPECT_EQ(serviceA, outA);
+
+ // serviceA should be overwritten by serviceB
+ sp<IBinder> serviceB = getBinder();
+ EXPECT_TRUE(sm->addService("foo", serviceB, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ sp<IBinder> outB;
+ EXPECT_TRUE(sm->getService("foo", &outB).isOk());
+ EXPECT_EQ(serviceB, outB);
+}
+
TEST(AddService, NoPermissions) {
std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index b574098..3ada05c 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -25,8 +25,9 @@
repeated SurfaceChange surface_change = 1;
repeated DisplayChange display_change = 2;
- required bool synchronous = 3;
- required bool animation = 4;
+ required bool synchronous = 3;
+ required bool animation = 4;
+ optional Origin origin = 5;
}
message SurfaceChange {
@@ -208,4 +209,9 @@
message ShadowRadiusChange {
required float radius = 1;
+}
+
+message Origin {
+ required int32 pid = 1;
+ required int32 uid = 2;
}
\ No newline at end of file
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index b327d76..ec2de61 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -19,6 +19,7 @@
#include <input/Input.h>
#include <android/keycodes.h>
+#include <unordered_map>
#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
@@ -35,428 +36,435 @@
int value;
};
+// NOTE: If you add a new keycode here you must also add it to several other files.
+// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
+#define KEYCODES_SEQUENCE \
+ DEFINE_KEYCODE(UNKNOWN), \
+ DEFINE_KEYCODE(SOFT_LEFT), \
+ DEFINE_KEYCODE(SOFT_RIGHT), \
+ DEFINE_KEYCODE(HOME), \
+ DEFINE_KEYCODE(BACK), \
+ DEFINE_KEYCODE(CALL), \
+ DEFINE_KEYCODE(ENDCALL), \
+ DEFINE_KEYCODE(0), \
+ DEFINE_KEYCODE(1), \
+ DEFINE_KEYCODE(2), \
+ DEFINE_KEYCODE(3), \
+ DEFINE_KEYCODE(4), \
+ DEFINE_KEYCODE(5), \
+ DEFINE_KEYCODE(6), \
+ DEFINE_KEYCODE(7), \
+ DEFINE_KEYCODE(8), \
+ DEFINE_KEYCODE(9), \
+ DEFINE_KEYCODE(STAR), \
+ DEFINE_KEYCODE(POUND), \
+ DEFINE_KEYCODE(DPAD_UP), \
+ DEFINE_KEYCODE(DPAD_DOWN), \
+ DEFINE_KEYCODE(DPAD_LEFT), \
+ DEFINE_KEYCODE(DPAD_RIGHT), \
+ DEFINE_KEYCODE(DPAD_CENTER), \
+ DEFINE_KEYCODE(VOLUME_UP), \
+ DEFINE_KEYCODE(VOLUME_DOWN), \
+ DEFINE_KEYCODE(POWER), \
+ DEFINE_KEYCODE(CAMERA), \
+ DEFINE_KEYCODE(CLEAR), \
+ DEFINE_KEYCODE(A), \
+ DEFINE_KEYCODE(B), \
+ DEFINE_KEYCODE(C), \
+ DEFINE_KEYCODE(D), \
+ DEFINE_KEYCODE(E), \
+ DEFINE_KEYCODE(F), \
+ DEFINE_KEYCODE(G), \
+ DEFINE_KEYCODE(H), \
+ DEFINE_KEYCODE(I), \
+ DEFINE_KEYCODE(J), \
+ DEFINE_KEYCODE(K), \
+ DEFINE_KEYCODE(L), \
+ DEFINE_KEYCODE(M), \
+ DEFINE_KEYCODE(N), \
+ DEFINE_KEYCODE(O), \
+ DEFINE_KEYCODE(P), \
+ DEFINE_KEYCODE(Q), \
+ DEFINE_KEYCODE(R), \
+ DEFINE_KEYCODE(S), \
+ DEFINE_KEYCODE(T), \
+ DEFINE_KEYCODE(U), \
+ DEFINE_KEYCODE(V), \
+ DEFINE_KEYCODE(W), \
+ DEFINE_KEYCODE(X), \
+ DEFINE_KEYCODE(Y), \
+ DEFINE_KEYCODE(Z), \
+ DEFINE_KEYCODE(COMMA), \
+ DEFINE_KEYCODE(PERIOD), \
+ DEFINE_KEYCODE(ALT_LEFT), \
+ DEFINE_KEYCODE(ALT_RIGHT), \
+ DEFINE_KEYCODE(SHIFT_LEFT), \
+ DEFINE_KEYCODE(SHIFT_RIGHT), \
+ DEFINE_KEYCODE(TAB), \
+ DEFINE_KEYCODE(SPACE), \
+ DEFINE_KEYCODE(SYM), \
+ DEFINE_KEYCODE(EXPLORER), \
+ DEFINE_KEYCODE(ENVELOPE), \
+ DEFINE_KEYCODE(ENTER), \
+ DEFINE_KEYCODE(DEL), \
+ DEFINE_KEYCODE(GRAVE), \
+ DEFINE_KEYCODE(MINUS), \
+ DEFINE_KEYCODE(EQUALS), \
+ DEFINE_KEYCODE(LEFT_BRACKET), \
+ DEFINE_KEYCODE(RIGHT_BRACKET), \
+ DEFINE_KEYCODE(BACKSLASH), \
+ DEFINE_KEYCODE(SEMICOLON), \
+ DEFINE_KEYCODE(APOSTROPHE), \
+ DEFINE_KEYCODE(SLASH), \
+ DEFINE_KEYCODE(AT), \
+ DEFINE_KEYCODE(NUM), \
+ DEFINE_KEYCODE(HEADSETHOOK), \
+ DEFINE_KEYCODE(FOCUS), \
+ DEFINE_KEYCODE(PLUS), \
+ DEFINE_KEYCODE(MENU), \
+ DEFINE_KEYCODE(NOTIFICATION), \
+ DEFINE_KEYCODE(SEARCH), \
+ DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), \
+ DEFINE_KEYCODE(MEDIA_STOP), \
+ DEFINE_KEYCODE(MEDIA_NEXT), \
+ DEFINE_KEYCODE(MEDIA_PREVIOUS), \
+ DEFINE_KEYCODE(MEDIA_REWIND), \
+ DEFINE_KEYCODE(MEDIA_FAST_FORWARD), \
+ DEFINE_KEYCODE(MUTE), \
+ DEFINE_KEYCODE(PAGE_UP), \
+ DEFINE_KEYCODE(PAGE_DOWN), \
+ DEFINE_KEYCODE(PICTSYMBOLS), \
+ DEFINE_KEYCODE(SWITCH_CHARSET), \
+ DEFINE_KEYCODE(BUTTON_A), \
+ DEFINE_KEYCODE(BUTTON_B), \
+ DEFINE_KEYCODE(BUTTON_C), \
+ DEFINE_KEYCODE(BUTTON_X), \
+ DEFINE_KEYCODE(BUTTON_Y), \
+ DEFINE_KEYCODE(BUTTON_Z), \
+ DEFINE_KEYCODE(BUTTON_L1), \
+ DEFINE_KEYCODE(BUTTON_R1), \
+ DEFINE_KEYCODE(BUTTON_L2), \
+ DEFINE_KEYCODE(BUTTON_R2), \
+ DEFINE_KEYCODE(BUTTON_THUMBL), \
+ DEFINE_KEYCODE(BUTTON_THUMBR), \
+ DEFINE_KEYCODE(BUTTON_START), \
+ DEFINE_KEYCODE(BUTTON_SELECT), \
+ DEFINE_KEYCODE(BUTTON_MODE), \
+ DEFINE_KEYCODE(ESCAPE), \
+ DEFINE_KEYCODE(FORWARD_DEL), \
+ DEFINE_KEYCODE(CTRL_LEFT), \
+ DEFINE_KEYCODE(CTRL_RIGHT), \
+ DEFINE_KEYCODE(CAPS_LOCK), \
+ DEFINE_KEYCODE(SCROLL_LOCK), \
+ DEFINE_KEYCODE(META_LEFT), \
+ DEFINE_KEYCODE(META_RIGHT), \
+ DEFINE_KEYCODE(FUNCTION), \
+ DEFINE_KEYCODE(SYSRQ), \
+ DEFINE_KEYCODE(BREAK), \
+ DEFINE_KEYCODE(MOVE_HOME), \
+ DEFINE_KEYCODE(MOVE_END), \
+ DEFINE_KEYCODE(INSERT), \
+ DEFINE_KEYCODE(FORWARD), \
+ DEFINE_KEYCODE(MEDIA_PLAY), \
+ DEFINE_KEYCODE(MEDIA_PAUSE), \
+ DEFINE_KEYCODE(MEDIA_CLOSE), \
+ DEFINE_KEYCODE(MEDIA_EJECT), \
+ DEFINE_KEYCODE(MEDIA_RECORD), \
+ DEFINE_KEYCODE(F1), \
+ DEFINE_KEYCODE(F2), \
+ DEFINE_KEYCODE(F3), \
+ DEFINE_KEYCODE(F4), \
+ DEFINE_KEYCODE(F5), \
+ DEFINE_KEYCODE(F6), \
+ DEFINE_KEYCODE(F7), \
+ DEFINE_KEYCODE(F8), \
+ DEFINE_KEYCODE(F9), \
+ DEFINE_KEYCODE(F10), \
+ DEFINE_KEYCODE(F11), \
+ DEFINE_KEYCODE(F12), \
+ DEFINE_KEYCODE(NUM_LOCK), \
+ DEFINE_KEYCODE(NUMPAD_0), \
+ DEFINE_KEYCODE(NUMPAD_1), \
+ DEFINE_KEYCODE(NUMPAD_2), \
+ DEFINE_KEYCODE(NUMPAD_3), \
+ DEFINE_KEYCODE(NUMPAD_4), \
+ DEFINE_KEYCODE(NUMPAD_5), \
+ DEFINE_KEYCODE(NUMPAD_6), \
+ DEFINE_KEYCODE(NUMPAD_7), \
+ DEFINE_KEYCODE(NUMPAD_8), \
+ DEFINE_KEYCODE(NUMPAD_9), \
+ DEFINE_KEYCODE(NUMPAD_DIVIDE), \
+ DEFINE_KEYCODE(NUMPAD_MULTIPLY), \
+ DEFINE_KEYCODE(NUMPAD_SUBTRACT), \
+ DEFINE_KEYCODE(NUMPAD_ADD), \
+ DEFINE_KEYCODE(NUMPAD_DOT), \
+ DEFINE_KEYCODE(NUMPAD_COMMA), \
+ DEFINE_KEYCODE(NUMPAD_ENTER), \
+ DEFINE_KEYCODE(NUMPAD_EQUALS), \
+ DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), \
+ DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), \
+ DEFINE_KEYCODE(VOLUME_MUTE), \
+ DEFINE_KEYCODE(INFO), \
+ DEFINE_KEYCODE(CHANNEL_UP), \
+ DEFINE_KEYCODE(CHANNEL_DOWN), \
+ DEFINE_KEYCODE(ZOOM_IN), \
+ DEFINE_KEYCODE(ZOOM_OUT), \
+ DEFINE_KEYCODE(TV), \
+ DEFINE_KEYCODE(WINDOW), \
+ DEFINE_KEYCODE(GUIDE), \
+ DEFINE_KEYCODE(DVR), \
+ DEFINE_KEYCODE(BOOKMARK), \
+ DEFINE_KEYCODE(CAPTIONS), \
+ DEFINE_KEYCODE(SETTINGS), \
+ DEFINE_KEYCODE(TV_POWER), \
+ DEFINE_KEYCODE(TV_INPUT), \
+ DEFINE_KEYCODE(STB_POWER), \
+ DEFINE_KEYCODE(STB_INPUT), \
+ DEFINE_KEYCODE(AVR_POWER), \
+ DEFINE_KEYCODE(AVR_INPUT), \
+ DEFINE_KEYCODE(PROG_RED), \
+ DEFINE_KEYCODE(PROG_GREEN), \
+ DEFINE_KEYCODE(PROG_YELLOW), \
+ DEFINE_KEYCODE(PROG_BLUE), \
+ DEFINE_KEYCODE(APP_SWITCH), \
+ DEFINE_KEYCODE(BUTTON_1), \
+ DEFINE_KEYCODE(BUTTON_2), \
+ DEFINE_KEYCODE(BUTTON_3), \
+ DEFINE_KEYCODE(BUTTON_4), \
+ DEFINE_KEYCODE(BUTTON_5), \
+ DEFINE_KEYCODE(BUTTON_6), \
+ DEFINE_KEYCODE(BUTTON_7), \
+ DEFINE_KEYCODE(BUTTON_8), \
+ DEFINE_KEYCODE(BUTTON_9), \
+ DEFINE_KEYCODE(BUTTON_10), \
+ DEFINE_KEYCODE(BUTTON_11), \
+ DEFINE_KEYCODE(BUTTON_12), \
+ DEFINE_KEYCODE(BUTTON_13), \
+ DEFINE_KEYCODE(BUTTON_14), \
+ DEFINE_KEYCODE(BUTTON_15), \
+ DEFINE_KEYCODE(BUTTON_16), \
+ DEFINE_KEYCODE(LANGUAGE_SWITCH), \
+ DEFINE_KEYCODE(MANNER_MODE), \
+ DEFINE_KEYCODE(3D_MODE), \
+ DEFINE_KEYCODE(CONTACTS), \
+ DEFINE_KEYCODE(CALENDAR), \
+ DEFINE_KEYCODE(MUSIC), \
+ DEFINE_KEYCODE(CALCULATOR), \
+ DEFINE_KEYCODE(ZENKAKU_HANKAKU), \
+ DEFINE_KEYCODE(EISU), \
+ DEFINE_KEYCODE(MUHENKAN), \
+ DEFINE_KEYCODE(HENKAN), \
+ DEFINE_KEYCODE(KATAKANA_HIRAGANA), \
+ DEFINE_KEYCODE(YEN), \
+ DEFINE_KEYCODE(RO), \
+ DEFINE_KEYCODE(KANA), \
+ DEFINE_KEYCODE(ASSIST), \
+ DEFINE_KEYCODE(BRIGHTNESS_DOWN), \
+ DEFINE_KEYCODE(BRIGHTNESS_UP), \
+ DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), \
+ DEFINE_KEYCODE(SLEEP), \
+ DEFINE_KEYCODE(WAKEUP), \
+ DEFINE_KEYCODE(PAIRING), \
+ DEFINE_KEYCODE(MEDIA_TOP_MENU), \
+ DEFINE_KEYCODE(11), \
+ DEFINE_KEYCODE(12), \
+ DEFINE_KEYCODE(LAST_CHANNEL), \
+ DEFINE_KEYCODE(TV_DATA_SERVICE), \
+ DEFINE_KEYCODE(VOICE_ASSIST), \
+ DEFINE_KEYCODE(TV_RADIO_SERVICE), \
+ DEFINE_KEYCODE(TV_TELETEXT), \
+ DEFINE_KEYCODE(TV_NUMBER_ENTRY), \
+ DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), \
+ DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), \
+ DEFINE_KEYCODE(TV_SATELLITE), \
+ DEFINE_KEYCODE(TV_SATELLITE_BS), \
+ DEFINE_KEYCODE(TV_SATELLITE_CS), \
+ DEFINE_KEYCODE(TV_SATELLITE_SERVICE), \
+ DEFINE_KEYCODE(TV_NETWORK), \
+ DEFINE_KEYCODE(TV_ANTENNA_CABLE), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_1), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_2), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_3), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_4), \
+ DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), \
+ DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), \
+ DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), \
+ DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), \
+ DEFINE_KEYCODE(TV_INPUT_VGA_1), \
+ DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), \
+ DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), \
+ DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), \
+ DEFINE_KEYCODE(TV_ZOOM_MODE), \
+ DEFINE_KEYCODE(TV_CONTENTS_MENU), \
+ DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), \
+ DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), \
+ DEFINE_KEYCODE(HELP), \
+ DEFINE_KEYCODE(NAVIGATE_PREVIOUS), \
+ DEFINE_KEYCODE(NAVIGATE_NEXT), \
+ DEFINE_KEYCODE(NAVIGATE_IN), \
+ DEFINE_KEYCODE(NAVIGATE_OUT), \
+ DEFINE_KEYCODE(STEM_PRIMARY), \
+ DEFINE_KEYCODE(STEM_1), \
+ DEFINE_KEYCODE(STEM_2), \
+ DEFINE_KEYCODE(STEM_3), \
+ DEFINE_KEYCODE(DPAD_UP_LEFT), \
+ DEFINE_KEYCODE(DPAD_DOWN_LEFT), \
+ DEFINE_KEYCODE(DPAD_UP_RIGHT), \
+ DEFINE_KEYCODE(DPAD_DOWN_RIGHT), \
+ DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), \
+ DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), \
+ DEFINE_KEYCODE(MEDIA_STEP_FORWARD), \
+ DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), \
+ DEFINE_KEYCODE(SOFT_SLEEP), \
+ DEFINE_KEYCODE(CUT), \
+ DEFINE_KEYCODE(COPY), \
+ DEFINE_KEYCODE(PASTE), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), \
+ DEFINE_KEYCODE(ALL_APPS), \
+ DEFINE_KEYCODE(REFRESH), \
+ DEFINE_KEYCODE(THUMBS_UP), \
+ DEFINE_KEYCODE(THUMBS_DOWN), \
+ DEFINE_KEYCODE(PROFILE_SWITCH)
-static const InputEventLabel KEYCODES[] = {
- // NOTE: If you add a new keycode here you must also add it to several other files.
- // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
- DEFINE_KEYCODE(UNKNOWN),
- DEFINE_KEYCODE(SOFT_LEFT),
- DEFINE_KEYCODE(SOFT_RIGHT),
- DEFINE_KEYCODE(HOME),
- DEFINE_KEYCODE(BACK),
- DEFINE_KEYCODE(CALL),
- DEFINE_KEYCODE(ENDCALL),
- DEFINE_KEYCODE(0),
- DEFINE_KEYCODE(1),
- DEFINE_KEYCODE(2),
- DEFINE_KEYCODE(3),
- DEFINE_KEYCODE(4),
- DEFINE_KEYCODE(5),
- DEFINE_KEYCODE(6),
- DEFINE_KEYCODE(7),
- DEFINE_KEYCODE(8),
- DEFINE_KEYCODE(9),
- DEFINE_KEYCODE(STAR),
- DEFINE_KEYCODE(POUND),
- DEFINE_KEYCODE(DPAD_UP),
- DEFINE_KEYCODE(DPAD_DOWN),
- DEFINE_KEYCODE(DPAD_LEFT),
- DEFINE_KEYCODE(DPAD_RIGHT),
- DEFINE_KEYCODE(DPAD_CENTER),
- DEFINE_KEYCODE(VOLUME_UP),
- DEFINE_KEYCODE(VOLUME_DOWN),
- DEFINE_KEYCODE(POWER),
- DEFINE_KEYCODE(CAMERA),
- DEFINE_KEYCODE(CLEAR),
- DEFINE_KEYCODE(A),
- DEFINE_KEYCODE(B),
- DEFINE_KEYCODE(C),
- DEFINE_KEYCODE(D),
- DEFINE_KEYCODE(E),
- DEFINE_KEYCODE(F),
- DEFINE_KEYCODE(G),
- DEFINE_KEYCODE(H),
- DEFINE_KEYCODE(I),
- DEFINE_KEYCODE(J),
- DEFINE_KEYCODE(K),
- DEFINE_KEYCODE(L),
- DEFINE_KEYCODE(M),
- DEFINE_KEYCODE(N),
- DEFINE_KEYCODE(O),
- DEFINE_KEYCODE(P),
- DEFINE_KEYCODE(Q),
- DEFINE_KEYCODE(R),
- DEFINE_KEYCODE(S),
- DEFINE_KEYCODE(T),
- DEFINE_KEYCODE(U),
- DEFINE_KEYCODE(V),
- DEFINE_KEYCODE(W),
- DEFINE_KEYCODE(X),
- DEFINE_KEYCODE(Y),
- DEFINE_KEYCODE(Z),
- DEFINE_KEYCODE(COMMA),
- DEFINE_KEYCODE(PERIOD),
- DEFINE_KEYCODE(ALT_LEFT),
- DEFINE_KEYCODE(ALT_RIGHT),
- DEFINE_KEYCODE(SHIFT_LEFT),
- DEFINE_KEYCODE(SHIFT_RIGHT),
- DEFINE_KEYCODE(TAB),
- DEFINE_KEYCODE(SPACE),
- DEFINE_KEYCODE(SYM),
- DEFINE_KEYCODE(EXPLORER),
- DEFINE_KEYCODE(ENVELOPE),
- DEFINE_KEYCODE(ENTER),
- DEFINE_KEYCODE(DEL),
- DEFINE_KEYCODE(GRAVE),
- DEFINE_KEYCODE(MINUS),
- DEFINE_KEYCODE(EQUALS),
- DEFINE_KEYCODE(LEFT_BRACKET),
- DEFINE_KEYCODE(RIGHT_BRACKET),
- DEFINE_KEYCODE(BACKSLASH),
- DEFINE_KEYCODE(SEMICOLON),
- DEFINE_KEYCODE(APOSTROPHE),
- DEFINE_KEYCODE(SLASH),
- DEFINE_KEYCODE(AT),
- DEFINE_KEYCODE(NUM),
- DEFINE_KEYCODE(HEADSETHOOK),
- DEFINE_KEYCODE(FOCUS), // *Camera* focus
- DEFINE_KEYCODE(PLUS),
- DEFINE_KEYCODE(MENU),
- DEFINE_KEYCODE(NOTIFICATION),
- DEFINE_KEYCODE(SEARCH),
- DEFINE_KEYCODE(MEDIA_PLAY_PAUSE),
- DEFINE_KEYCODE(MEDIA_STOP),
- DEFINE_KEYCODE(MEDIA_NEXT),
- DEFINE_KEYCODE(MEDIA_PREVIOUS),
- DEFINE_KEYCODE(MEDIA_REWIND),
- DEFINE_KEYCODE(MEDIA_FAST_FORWARD),
- DEFINE_KEYCODE(MUTE),
- DEFINE_KEYCODE(PAGE_UP),
- DEFINE_KEYCODE(PAGE_DOWN),
- DEFINE_KEYCODE(PICTSYMBOLS),
- DEFINE_KEYCODE(SWITCH_CHARSET),
- DEFINE_KEYCODE(BUTTON_A),
- DEFINE_KEYCODE(BUTTON_B),
- DEFINE_KEYCODE(BUTTON_C),
- DEFINE_KEYCODE(BUTTON_X),
- DEFINE_KEYCODE(BUTTON_Y),
- DEFINE_KEYCODE(BUTTON_Z),
- DEFINE_KEYCODE(BUTTON_L1),
- DEFINE_KEYCODE(BUTTON_R1),
- DEFINE_KEYCODE(BUTTON_L2),
- DEFINE_KEYCODE(BUTTON_R2),
- DEFINE_KEYCODE(BUTTON_THUMBL),
- DEFINE_KEYCODE(BUTTON_THUMBR),
- DEFINE_KEYCODE(BUTTON_START),
- DEFINE_KEYCODE(BUTTON_SELECT),
- DEFINE_KEYCODE(BUTTON_MODE),
- DEFINE_KEYCODE(ESCAPE),
- DEFINE_KEYCODE(FORWARD_DEL),
- DEFINE_KEYCODE(CTRL_LEFT),
- DEFINE_KEYCODE(CTRL_RIGHT),
- DEFINE_KEYCODE(CAPS_LOCK),
- DEFINE_KEYCODE(SCROLL_LOCK),
- DEFINE_KEYCODE(META_LEFT),
- DEFINE_KEYCODE(META_RIGHT),
- DEFINE_KEYCODE(FUNCTION),
- DEFINE_KEYCODE(SYSRQ),
- DEFINE_KEYCODE(BREAK),
- DEFINE_KEYCODE(MOVE_HOME),
- DEFINE_KEYCODE(MOVE_END),
- DEFINE_KEYCODE(INSERT),
- DEFINE_KEYCODE(FORWARD),
- DEFINE_KEYCODE(MEDIA_PLAY),
- DEFINE_KEYCODE(MEDIA_PAUSE),
- DEFINE_KEYCODE(MEDIA_CLOSE),
- DEFINE_KEYCODE(MEDIA_EJECT),
- DEFINE_KEYCODE(MEDIA_RECORD),
- DEFINE_KEYCODE(F1),
- DEFINE_KEYCODE(F2),
- DEFINE_KEYCODE(F3),
- DEFINE_KEYCODE(F4),
- DEFINE_KEYCODE(F5),
- DEFINE_KEYCODE(F6),
- DEFINE_KEYCODE(F7),
- DEFINE_KEYCODE(F8),
- DEFINE_KEYCODE(F9),
- DEFINE_KEYCODE(F10),
- DEFINE_KEYCODE(F11),
- DEFINE_KEYCODE(F12),
- DEFINE_KEYCODE(NUM_LOCK),
- DEFINE_KEYCODE(NUMPAD_0),
- DEFINE_KEYCODE(NUMPAD_1),
- DEFINE_KEYCODE(NUMPAD_2),
- DEFINE_KEYCODE(NUMPAD_3),
- DEFINE_KEYCODE(NUMPAD_4),
- DEFINE_KEYCODE(NUMPAD_5),
- DEFINE_KEYCODE(NUMPAD_6),
- DEFINE_KEYCODE(NUMPAD_7),
- DEFINE_KEYCODE(NUMPAD_8),
- DEFINE_KEYCODE(NUMPAD_9),
- DEFINE_KEYCODE(NUMPAD_DIVIDE),
- DEFINE_KEYCODE(NUMPAD_MULTIPLY),
- DEFINE_KEYCODE(NUMPAD_SUBTRACT),
- DEFINE_KEYCODE(NUMPAD_ADD),
- DEFINE_KEYCODE(NUMPAD_DOT),
- DEFINE_KEYCODE(NUMPAD_COMMA),
- DEFINE_KEYCODE(NUMPAD_ENTER),
- DEFINE_KEYCODE(NUMPAD_EQUALS),
- DEFINE_KEYCODE(NUMPAD_LEFT_PAREN),
- DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN),
- DEFINE_KEYCODE(VOLUME_MUTE),
- DEFINE_KEYCODE(INFO),
- DEFINE_KEYCODE(CHANNEL_UP),
- DEFINE_KEYCODE(CHANNEL_DOWN),
- DEFINE_KEYCODE(ZOOM_IN),
- DEFINE_KEYCODE(ZOOM_OUT),
- DEFINE_KEYCODE(TV),
- DEFINE_KEYCODE(WINDOW),
- DEFINE_KEYCODE(GUIDE),
- DEFINE_KEYCODE(DVR),
- DEFINE_KEYCODE(BOOKMARK),
- DEFINE_KEYCODE(CAPTIONS),
- DEFINE_KEYCODE(SETTINGS),
- DEFINE_KEYCODE(TV_POWER),
- DEFINE_KEYCODE(TV_INPUT),
- DEFINE_KEYCODE(STB_POWER),
- DEFINE_KEYCODE(STB_INPUT),
- DEFINE_KEYCODE(AVR_POWER),
- DEFINE_KEYCODE(AVR_INPUT),
- DEFINE_KEYCODE(PROG_RED),
- DEFINE_KEYCODE(PROG_GREEN),
- DEFINE_KEYCODE(PROG_YELLOW),
- DEFINE_KEYCODE(PROG_BLUE),
- DEFINE_KEYCODE(APP_SWITCH),
- DEFINE_KEYCODE(BUTTON_1),
- DEFINE_KEYCODE(BUTTON_2),
- DEFINE_KEYCODE(BUTTON_3),
- DEFINE_KEYCODE(BUTTON_4),
- DEFINE_KEYCODE(BUTTON_5),
- DEFINE_KEYCODE(BUTTON_6),
- DEFINE_KEYCODE(BUTTON_7),
- DEFINE_KEYCODE(BUTTON_8),
- DEFINE_KEYCODE(BUTTON_9),
- DEFINE_KEYCODE(BUTTON_10),
- DEFINE_KEYCODE(BUTTON_11),
- DEFINE_KEYCODE(BUTTON_12),
- DEFINE_KEYCODE(BUTTON_13),
- DEFINE_KEYCODE(BUTTON_14),
- DEFINE_KEYCODE(BUTTON_15),
- DEFINE_KEYCODE(BUTTON_16),
- DEFINE_KEYCODE(LANGUAGE_SWITCH),
- DEFINE_KEYCODE(MANNER_MODE),
- DEFINE_KEYCODE(3D_MODE),
- DEFINE_KEYCODE(CONTACTS),
- DEFINE_KEYCODE(CALENDAR),
- DEFINE_KEYCODE(MUSIC),
- DEFINE_KEYCODE(CALCULATOR),
- DEFINE_KEYCODE(ZENKAKU_HANKAKU),
- DEFINE_KEYCODE(EISU),
- DEFINE_KEYCODE(MUHENKAN),
- DEFINE_KEYCODE(HENKAN),
- DEFINE_KEYCODE(KATAKANA_HIRAGANA),
- DEFINE_KEYCODE(YEN),
- DEFINE_KEYCODE(RO),
- DEFINE_KEYCODE(KANA),
- DEFINE_KEYCODE(ASSIST),
- DEFINE_KEYCODE(BRIGHTNESS_DOWN),
- DEFINE_KEYCODE(BRIGHTNESS_UP),
- DEFINE_KEYCODE(MEDIA_AUDIO_TRACK),
- DEFINE_KEYCODE(SLEEP),
- DEFINE_KEYCODE(WAKEUP),
- DEFINE_KEYCODE(PAIRING),
- DEFINE_KEYCODE(MEDIA_TOP_MENU),
- DEFINE_KEYCODE(11),
- DEFINE_KEYCODE(12),
- DEFINE_KEYCODE(LAST_CHANNEL),
- DEFINE_KEYCODE(TV_DATA_SERVICE),
- DEFINE_KEYCODE(VOICE_ASSIST),
- DEFINE_KEYCODE(TV_RADIO_SERVICE),
- DEFINE_KEYCODE(TV_TELETEXT),
- DEFINE_KEYCODE(TV_NUMBER_ENTRY),
- DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG),
- DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL),
- DEFINE_KEYCODE(TV_SATELLITE),
- DEFINE_KEYCODE(TV_SATELLITE_BS),
- DEFINE_KEYCODE(TV_SATELLITE_CS),
- DEFINE_KEYCODE(TV_SATELLITE_SERVICE),
- DEFINE_KEYCODE(TV_NETWORK),
- DEFINE_KEYCODE(TV_ANTENNA_CABLE),
- DEFINE_KEYCODE(TV_INPUT_HDMI_1),
- DEFINE_KEYCODE(TV_INPUT_HDMI_2),
- DEFINE_KEYCODE(TV_INPUT_HDMI_3),
- DEFINE_KEYCODE(TV_INPUT_HDMI_4),
- DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1),
- DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2),
- DEFINE_KEYCODE(TV_INPUT_COMPONENT_1),
- DEFINE_KEYCODE(TV_INPUT_COMPONENT_2),
- DEFINE_KEYCODE(TV_INPUT_VGA_1),
- DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION),
- DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP),
- DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN),
- DEFINE_KEYCODE(TV_ZOOM_MODE),
- DEFINE_KEYCODE(TV_CONTENTS_MENU),
- DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU),
- DEFINE_KEYCODE(TV_TIMER_PROGRAMMING),
- DEFINE_KEYCODE(HELP),
- DEFINE_KEYCODE(NAVIGATE_PREVIOUS),
- DEFINE_KEYCODE(NAVIGATE_NEXT),
- DEFINE_KEYCODE(NAVIGATE_IN),
- DEFINE_KEYCODE(NAVIGATE_OUT),
- DEFINE_KEYCODE(STEM_PRIMARY),
- DEFINE_KEYCODE(STEM_1),
- DEFINE_KEYCODE(STEM_2),
- DEFINE_KEYCODE(STEM_3),
- DEFINE_KEYCODE(DPAD_UP_LEFT),
- DEFINE_KEYCODE(DPAD_DOWN_LEFT),
- DEFINE_KEYCODE(DPAD_UP_RIGHT),
- DEFINE_KEYCODE(DPAD_DOWN_RIGHT),
- DEFINE_KEYCODE(MEDIA_SKIP_FORWARD),
- DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD),
- DEFINE_KEYCODE(MEDIA_STEP_FORWARD),
- DEFINE_KEYCODE(MEDIA_STEP_BACKWARD),
- DEFINE_KEYCODE(SOFT_SLEEP),
- DEFINE_KEYCODE(CUT),
- DEFINE_KEYCODE(COPY),
- DEFINE_KEYCODE(PASTE),
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP),
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN),
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT),
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
- DEFINE_KEYCODE(ALL_APPS),
- DEFINE_KEYCODE(REFRESH),
- DEFINE_KEYCODE(THUMBS_UP),
- DEFINE_KEYCODE(THUMBS_DOWN),
- DEFINE_KEYCODE(PROFILE_SWITCH),
-
- { nullptr, 0 }
+static const std::unordered_map<std::string, int> KEYCODES = {
+ KEYCODES_SEQUENCE
};
-static const InputEventLabel AXES[] = {
- DEFINE_AXIS(X),
- DEFINE_AXIS(Y),
- DEFINE_AXIS(PRESSURE),
- DEFINE_AXIS(SIZE),
- DEFINE_AXIS(TOUCH_MAJOR),
- DEFINE_AXIS(TOUCH_MINOR),
- DEFINE_AXIS(TOOL_MAJOR),
- DEFINE_AXIS(TOOL_MINOR),
- DEFINE_AXIS(ORIENTATION),
- DEFINE_AXIS(VSCROLL),
- DEFINE_AXIS(HSCROLL),
- DEFINE_AXIS(Z),
- DEFINE_AXIS(RX),
- DEFINE_AXIS(RY),
- DEFINE_AXIS(RZ),
- DEFINE_AXIS(HAT_X),
- DEFINE_AXIS(HAT_Y),
- DEFINE_AXIS(LTRIGGER),
- DEFINE_AXIS(RTRIGGER),
- DEFINE_AXIS(THROTTLE),
- DEFINE_AXIS(RUDDER),
- DEFINE_AXIS(WHEEL),
- DEFINE_AXIS(GAS),
- DEFINE_AXIS(BRAKE),
- DEFINE_AXIS(DISTANCE),
- DEFINE_AXIS(TILT),
- DEFINE_AXIS(SCROLL),
- DEFINE_AXIS(RELATIVE_X),
- DEFINE_AXIS(RELATIVE_Y),
- DEFINE_AXIS(GENERIC_1),
- DEFINE_AXIS(GENERIC_2),
- DEFINE_AXIS(GENERIC_3),
- DEFINE_AXIS(GENERIC_4),
- DEFINE_AXIS(GENERIC_5),
- DEFINE_AXIS(GENERIC_6),
- DEFINE_AXIS(GENERIC_7),
- DEFINE_AXIS(GENERIC_8),
- DEFINE_AXIS(GENERIC_9),
- DEFINE_AXIS(GENERIC_10),
- DEFINE_AXIS(GENERIC_11),
- DEFINE_AXIS(GENERIC_12),
- DEFINE_AXIS(GENERIC_13),
- DEFINE_AXIS(GENERIC_14),
- DEFINE_AXIS(GENERIC_15),
- DEFINE_AXIS(GENERIC_16),
-
- // NOTE: If you add a new axis here you must also add it to several other files.
- // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
- { nullptr, 0 }
+static const std::vector<InputEventLabel> KEY_NAMES = {
+ KEYCODES_SEQUENCE
};
-static const InputEventLabel LEDS[] = {
- DEFINE_LED(NUM_LOCK),
- DEFINE_LED(CAPS_LOCK),
- DEFINE_LED(SCROLL_LOCK),
- DEFINE_LED(COMPOSE),
- DEFINE_LED(KANA),
- DEFINE_LED(SLEEP),
- DEFINE_LED(SUSPEND),
- DEFINE_LED(MUTE),
- DEFINE_LED(MISC),
- DEFINE_LED(MAIL),
- DEFINE_LED(CHARGING),
- DEFINE_LED(CONTROLLER_1),
- DEFINE_LED(CONTROLLER_2),
- DEFINE_LED(CONTROLLER_3),
- DEFINE_LED(CONTROLLER_4),
+// NOTE: If you add a new axis here you must also add it to several other files.
+// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
+#define AXES_SEQUENCE \
+ DEFINE_AXIS(X), \
+ DEFINE_AXIS(Y), \
+ DEFINE_AXIS(PRESSURE), \
+ DEFINE_AXIS(SIZE), \
+ DEFINE_AXIS(TOUCH_MAJOR), \
+ DEFINE_AXIS(TOUCH_MINOR), \
+ DEFINE_AXIS(TOOL_MAJOR), \
+ DEFINE_AXIS(TOOL_MINOR), \
+ DEFINE_AXIS(ORIENTATION), \
+ DEFINE_AXIS(VSCROLL), \
+ DEFINE_AXIS(HSCROLL), \
+ DEFINE_AXIS(Z), \
+ DEFINE_AXIS(RX), \
+ DEFINE_AXIS(RY), \
+ DEFINE_AXIS(RZ), \
+ DEFINE_AXIS(HAT_X), \
+ DEFINE_AXIS(HAT_Y), \
+ DEFINE_AXIS(LTRIGGER), \
+ DEFINE_AXIS(RTRIGGER), \
+ DEFINE_AXIS(THROTTLE), \
+ DEFINE_AXIS(RUDDER), \
+ DEFINE_AXIS(WHEEL), \
+ DEFINE_AXIS(GAS), \
+ DEFINE_AXIS(BRAKE), \
+ DEFINE_AXIS(DISTANCE), \
+ DEFINE_AXIS(TILT), \
+ DEFINE_AXIS(SCROLL), \
+ DEFINE_AXIS(RELATIVE_X), \
+ DEFINE_AXIS(RELATIVE_Y), \
+ DEFINE_AXIS(GENERIC_1), \
+ DEFINE_AXIS(GENERIC_2), \
+ DEFINE_AXIS(GENERIC_3), \
+ DEFINE_AXIS(GENERIC_4), \
+ DEFINE_AXIS(GENERIC_5), \
+ DEFINE_AXIS(GENERIC_6), \
+ DEFINE_AXIS(GENERIC_7), \
+ DEFINE_AXIS(GENERIC_8), \
+ DEFINE_AXIS(GENERIC_9), \
+ DEFINE_AXIS(GENERIC_10), \
+ DEFINE_AXIS(GENERIC_11), \
+ DEFINE_AXIS(GENERIC_12), \
+ DEFINE_AXIS(GENERIC_13), \
+ DEFINE_AXIS(GENERIC_14), \
+ DEFINE_AXIS(GENERIC_15), \
+ DEFINE_AXIS(GENERIC_16)
- // NOTE: If you add new LEDs here, you must also add them to Input.h
- { nullptr, 0 }
+static const std::unordered_map<std::string, int> AXES = {
+ AXES_SEQUENCE
};
-static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL),
- DEFINE_FLAG(FUNCTION),
- DEFINE_FLAG(GESTURE),
- DEFINE_FLAG(WAKE),
+static const std::vector<InputEventLabel> AXES_NAMES = {
+ AXES_SEQUENCE
+};
- {nullptr, 0}};
+// NOTE: If you add new LEDs here, you must also add them to Input.h
+#define LEDS_SEQUENCE \
+ DEFINE_LED(NUM_LOCK), \
+ DEFINE_LED(CAPS_LOCK), \
+ DEFINE_LED(SCROLL_LOCK), \
+ DEFINE_LED(COMPOSE), \
+ DEFINE_LED(KANA), \
+ DEFINE_LED(SLEEP), \
+ DEFINE_LED(SUSPEND), \
+ DEFINE_LED(MUTE), \
+ DEFINE_LED(MISC), \
+ DEFINE_LED(MAIL), \
+ DEFINE_LED(CHARGING), \
+ DEFINE_LED(CONTROLLER_1), \
+ DEFINE_LED(CONTROLLER_2), \
+ DEFINE_LED(CONTROLLER_3), \
+ DEFINE_LED(CONTROLLER_4)
-static int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
- while (list->literal) {
- if (strcmp(literal, list->literal) == 0) {
- return list->value;
- }
- list++;
- }
- return list->value;
+static const std::unordered_map<std::string, int> LEDS = {
+ LEDS_SEQUENCE
+};
+
+#define FLAGS_SEQUENCE \
+ DEFINE_FLAG(VIRTUAL), \
+ DEFINE_FLAG(FUNCTION), \
+ DEFINE_FLAG(GESTURE), \
+ DEFINE_FLAG(WAKE)
+
+static const std::unordered_map<std::string, int> FLAGS = {
+ FLAGS_SEQUENCE
+};
+
+static int lookupValueByLabel(const std::unordered_map<std::string, int> &map,
+ const char* literal) {
+ std::string str(literal);
+ auto it = map.find(str);
+ return it != map.end() ? it->second : 0;
}
-static const char* lookupLabelByValue(int value, const InputEventLabel* list) {
- while (list->literal) {
- if (list->value == value) {
- return list->literal;
- }
- list++;
+static const char* lookupLabelByValue(const std::vector<InputEventLabel> &vec, int value) {
+ if (static_cast<size_t>(value) < vec.size()) {
+ return vec[value].literal;
}
return nullptr;
}
static inline int32_t getKeyCodeByLabel(const char* label) {
- return int32_t(lookupValueByLabel(label, KEYCODES));
+ return int32_t(lookupValueByLabel(KEYCODES, label));
}
static inline const char* getLabelByKeyCode(int32_t keyCode) {
- if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) {
- return KEYCODES[keyCode].literal;
+ if (keyCode >= 0 && static_cast<size_t>(keyCode) < KEYCODES.size()) {
+ return lookupLabelByValue(KEY_NAMES, keyCode);
}
return nullptr;
}
static inline uint32_t getKeyFlagByLabel(const char* label) {
- return uint32_t(lookupValueByLabel(label, FLAGS));
+ return uint32_t(lookupValueByLabel(FLAGS, label));
}
static inline int32_t getAxisByLabel(const char* label) {
- return int32_t(lookupValueByLabel(label, AXES));
+ return int32_t(lookupValueByLabel(AXES, label));
}
static inline const char* getAxisLabel(int32_t axisId) {
- return lookupLabelByValue(axisId, AXES);
+ return lookupLabelByValue(AXES_NAMES, axisId);
}
static inline int32_t getLedByLabel(const char* label) {
- return int32_t(lookupValueByLabel(label, LEDS));
+ return int32_t(lookupValueByLabel(LEDS, label));
}
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 258a4e3..80aa891 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -22,6 +22,8 @@
cc_library_headers {
name: "libarect_headers",
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
export_include_dirs: ["include"],
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 8fd59ba..14ab60f 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -41,7 +41,6 @@
#include <binder/TextOutput.h>
#include <cutils/ashmem.h>
-#include <utils/Debug.h>
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/misc.h>
@@ -1526,7 +1525,7 @@
template<class T>
status_t Parcel::readAligned(T *pArg) const {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+ static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(T)) <= mDataSize) {
if (mObjectsSize > 0) {
@@ -1559,7 +1558,7 @@
template<class T>
status_t Parcel::writeAligned(T val) {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+ static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 33ee680..b72c854 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -255,8 +255,6 @@
"android.media.IAudioTrack",
"android.media.IDataSource",
"android.media.IDrmClient",
- "android.media.IEffect",
- "android.media.IEffectClient",
"android.media.IMediaCodecList",
"android.media.IMediaDrmService",
"android.media.IMediaExtractor",
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index edada3d..52bd5de 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -71,14 +71,6 @@
/* this closes this heap -- use carefully */
void dispose();
- /* this is only needed as a workaround, use only if you know
- * what you are doing */
- status_t setDevice(const char* device) {
- if (mDevice == nullptr)
- mDevice = device;
- return mDevice ? NO_ERROR : ALREADY_EXISTS;
- }
-
protected:
MemoryHeapBase();
// init() takes ownership of fd
diff --git a/libs/binder/include/binder/Nullable.h b/libs/binder/include/binder/Nullable.h
deleted file mode 100644
index a98583d..0000000
--- a/libs/binder/include/binder/Nullable.h
+++ /dev/null
@@ -1,44 +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 <optional>
-#include <utility>
-
-namespace android {
-
-namespace aidl {
-
-// nullable/make_nullable provide source-level compatibility between std::opional and std::unique_ptr
-// usage:
-// nullable<Foo> a;
-// nullable<Foo> b = make_nullable<Foo>(...);
-// auto c = make_nullable<Foo>(...);
-// c.reset();
-// c = make_nullable<Foo>(...);
-// c = std::move(a);
-
-template <typename T>
-using nullable = std::optional<T>;
-
-template <typename T, typename... Args>
-inline nullable<T> make_nullable(Args&&... args) {
- return std::make_optional<T>(std::forward<Args>(args)...);
-}
-
-} // namespace aidl
-
-} // namespace android
\ No newline at end of file
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index d287290..7d9fd51 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -168,7 +168,7 @@
binder_status_t status = getClass()->onTransact(this, code, &in, &out);
return PruneStatusT(status);
- } else if (code == SHELL_COMMAND_TRANSACTION) {
+ } else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) {
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
int err = data.readFileDescriptor();
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 5779427..902fe79 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -110,13 +110,13 @@
const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; }
// required to be non-null, implemented for every class
- const AIBinder_Class_onCreate onCreate;
- const AIBinder_Class_onDestroy onDestroy;
- const AIBinder_Class_onTransact onTransact;
+ const AIBinder_Class_onCreate onCreate = nullptr;
+ const AIBinder_Class_onDestroy onDestroy = nullptr;
+ const AIBinder_Class_onTransact onTransact = nullptr;
// optional methods for a class
- AIBinder_onDump onDump;
- AIBinder_handleShellCommand handleShellCommand;
+ AIBinder_onDump onDump = nullptr;
+ AIBinder_handleShellCommand handleShellCommand = nullptr;
private:
// This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 33e4586..f44ce0c 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -88,13 +88,21 @@
static void operator delete(void* p) { std::free(p); }
+ // Once minSdkVersion is 30, we are guaranteed to be building with the
+ // Android 11 AIDL compiler which supports the SharedRefBase::make API.
+ //
+ // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
+ // ownership. Making this operator private to avoid double-ownership.
+#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30
+ private:
+#else
+ [[deprecated("Prefer SharedRefBase::make<T>(...) if possible.")]]
+#endif
+ static void* operator new(size_t s) { return std::malloc(s); }
+
private:
std::once_flag mFlagThis;
std::weak_ptr<SharedRefBase> mThis;
-
- // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
- // ownership. Making this operator private to avoid double-ownership.
- static void* operator new(size_t s) { return std::malloc(s); }
};
/**
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 055c79b..52bcd20 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -18,6 +18,7 @@
#include <android/binder_ibinder.h>
#include <android/binder_status.h>
+#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -50,4 +51,47 @@
*/
__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
+/**
+ * Registers a lazy service with the default service manager under the 'instance' name.
+ * Does not take ownership of binder.
+ * The service must be configured statically with init so it can be restarted with
+ * ctl.interface.* messages from servicemanager.
+ * AServiceManager_registerLazyService cannot safely be used with AServiceManager_addService
+ * in the same process. If one service is registered with AServiceManager_registerLazyService,
+ * the entire process will have its lifetime controlled by servicemanager.
+ * Instead, all services in the process should be registered using
+ * AServiceManager_registerLazyService.
+ *
+ * \param binder object to register globally with the service manager.
+ * \param instance identifier of the service. This will be used to lookup the service.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char* instance)
+ __INTRODUCED_IN(31);
+
+/**
+ * Gets a binder object with this specific instance name. Efficiently waits for the service.
+ * If the service is not declared, it will wait indefinitely. Requires the threadpool
+ * to be started in the service.
+ * This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
+ * for calling AIBinder_decStrong).
+ *
+ * \param instance identifier of the service used to lookup the service.
+ *
+ * \return service if registered, null if not.
+ */
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_waitForService(const char* instance)
+ __INTRODUCED_IN(31);
+
+/**
+ * Check if a service is declared (e.g. VINTF manifest).
+ *
+ * \param instance identifier of the service.
+ *
+ * \return true on success, meaning AServiceManager_waitForService should always
+ * be able to return the service.
+ */
+bool AServiceManager_isDeclared(const char* instance) __INTRODUCED_IN(31);
+
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index d435382..1701fb5 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -117,6 +117,9 @@
ABinderProcess_setupPolling; # apex
AIBinder_getCallingSid; # apex
AIBinder_setRequestingSid; # apex
+ AServiceManager_isDeclared; # apex llndk
+ AServiceManager_registerLazyService; # llndk
+ AServiceManager_waitForService; # apex llndk
};
LIBBINDER_NDK_PLATFORM {
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index d0b166d..6b2184e 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -20,6 +20,7 @@
#include "status_internal.h"
#include <binder/IServiceManager.h>
+#include <binder/LazyServiceRegistrar.h>
using ::android::defaultServiceManager;
using ::android::IBinder;
@@ -61,3 +62,33 @@
AIBinder_incStrong(ret.get());
return ret.get();
}
+binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char* instance) {
+ if (binder == nullptr || instance == nullptr) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+
+ auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
+ status_t status = serviceRegistrar.registerService(binder->getBinder(), instance);
+
+ return PruneStatusT(status);
+}
+AIBinder* AServiceManager_waitForService(const char* instance) {
+ if (instance == nullptr) {
+ return nullptr;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->waitForService(String16(instance));
+
+ sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder);
+ AIBinder_incStrong(ret.get());
+ return ret.get();
+}
+bool AServiceManager_isDeclared(const char* instance) {
+ if (instance == nullptr) {
+ return false;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ return sm->isDeclared(String16(instance));
+}
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 9b2fcf0..1424b6c 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -43,6 +43,7 @@
constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
+constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest";
class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) {
@@ -143,11 +144,31 @@
return 1;
}
-// This is too slow
-// TEST(NdkBinder, GetServiceThatDoesntExist) {
-// sp<IFoo> foo = IFoo::getService("asdfghkl;");
-// EXPECT_EQ(nullptr, foo.get());
-// }
+int lazyService(const char* instance) {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ // Wait to register this service to make sure the main test process will
+ // actually wait for the service to be available. Tested with sleep(60),
+ // and reduced for sake of time.
+ sleep(1);
+ // Strong reference to MyBinderNdkUnitTest kept by service manager.
+ // This is just for testing, it has no corresponding init behavior.
+ auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+ auto binder = service->asBinder();
+
+ binder_status_t status = AServiceManager_registerLazyService(binder.get(), instance);
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << instance;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
+
+TEST(NdkBinder, GetServiceThatDoesntExist) {
+ sp<IFoo> foo = IFoo::getService("asdfghkl;");
+ EXPECT_EQ(nullptr, foo.get());
+}
TEST(NdkBinder, CheckServiceThatDoesntExist) {
AIBinder* binder = AServiceManager_checkService("asdfghkl;");
@@ -162,6 +183,26 @@
AIBinder_decStrong(binder);
}
+TEST(NdkBinder, UnimplementedDump) {
+ sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
+ ASSERT_NE(foo, nullptr);
+ AIBinder* binder = foo->getBinder();
+ EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0));
+ AIBinder_decStrong(binder);
+}
+
+TEST(NdkBinder, UnimplementedShell) {
+ // libbinder_ndk doesn't support calling shell, so we are calling from the
+ // libbinder across processes to the NDK service which doesn't implement
+ // shell
+ static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+ sp<IBinder> testService = sm->getService(String16(IFoo::kSomeInstanceName));
+
+ Vector<String16> argsVec;
+ EXPECT_EQ(OK, IBinder::shellCommand(testService, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO,
+ argsVec, nullptr, nullptr));
+}
+
TEST(NdkBinder, DoubleNumber) {
sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
ASSERT_NE(foo, nullptr);
@@ -171,6 +212,33 @@
EXPECT_EQ(2, out);
}
+TEST(NdkBinder, GetLazyService) {
+ // Not declared in the vintf manifest
+ ASSERT_FALSE(AServiceManager_isDeclared(kLazyBinderNdkUnitTestService));
+ ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+ ASSERT_NE(service, nullptr);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+}
+
+// This is too slow
+TEST(NdkBinder, CheckLazyServiceShutDown) {
+ ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+ ASSERT_NE(service, nullptr);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+ binder = nullptr;
+ service = nullptr;
+ IPCThreadState::self()->flushCommands();
+ // Make sure the service is dead after some time of no use
+ sleep(10);
+ ASSERT_EQ(nullptr, AServiceManager_checkService(kLazyBinderNdkUnitTestService));
+}
+
void LambdaOnDeath(void* cookie) {
auto onDeath = static_cast<std::function<void(void)>*>(cookie);
(*onDeath)();
@@ -477,6 +545,10 @@
}
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return lazyService(kLazyBinderNdkUnitTestService);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
return generatedService();
}
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
new file mode 100644
index 0000000..16811ee
--- /dev/null
+++ b/libs/binder/rust/Android.bp
@@ -0,0 +1,74 @@
+rust_library {
+ name: "libbinder_rs",
+ crate_name: "binder",
+ srcs: ["src/lib.rs"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libutils",
+ ],
+ rustlibs: [
+ "liblibc",
+ "libbinder_ndk_bindgen",
+ ],
+ host_supported: true,
+}
+
+rust_bindgen {
+ name: "libbinder_ndk_bindgen",
+ crate_name: "binder_ndk_bindgen",
+ wrapper_src: "BinderBindings.h",
+ source_stem: "ndk_bindings",
+ cflags: [
+ "-x c++",
+ ],
+ bindgen_flags: [
+ // Unfortunately the only way to specify the rust_non_exhaustive enum
+ // style for a type is to make it the default
+ "--default-enum-style", "rust_non_exhaustive",
+ // and then specify constified enums for the enums we don't want
+ // rustified
+ "--constified-enum", "android::c_interface::consts::.*",
+
+ "--whitelist-type", "android::c_interface::.*",
+ "--whitelist-type", "AStatus",
+ "--whitelist-type", "AIBinder_Class",
+ "--whitelist-type", "AIBinder",
+ "--whitelist-type", "AIBinder_Weak",
+ "--whitelist-type", "AIBinder_DeathRecipient",
+ "--whitelist-type", "AParcel",
+ "--whitelist-type", "binder_status_t",
+ "--whitelist-function", ".*",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ host_supported: true,
+
+ // Currently necessary for host builds
+ // TODO(b/31559095): bionic on host should define this
+ target: {
+ host: {
+ cflags: [
+ "-D__INTRODUCED_IN(n)=",
+ "-D__assert(a,b,c)=",
+ // We want all the APIs to be available on the host.
+ "-D__ANDROID_API__=10000",
+ ],
+ },
+ },
+}
+
+rust_test {
+ name: "libbinder_rs-internal_test",
+ crate_name: "binder",
+ srcs: ["src/lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ rustlibs: [
+ "liblibc",
+ "libbinder_ndk_bindgen",
+ ],
+}
diff --git a/libs/binder/rust/BinderBindings.h b/libs/binder/rust/BinderBindings.h
new file mode 100644
index 0000000..c7a06d9
--- /dev/null
+++ b/libs/binder/rust/BinderBindings.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
+#include <android/binder_parcel.h>
+#include <android/binder_process.h>
+#include <android/binder_shell.h>
+#include <android/binder_status.h>
+
+namespace android {
+
+namespace c_interface {
+
+// Expose error codes from anonymous enum in binder_status.h
+enum StatusCode {
+ OK = STATUS_OK,
+ UNKNOWN_ERROR = STATUS_UNKNOWN_ERROR,
+ NO_MEMORY = STATUS_NO_MEMORY,
+ INVALID_OPERATION = STATUS_INVALID_OPERATION,
+ BAD_VALUE = STATUS_BAD_VALUE,
+ BAD_TYPE = STATUS_BAD_TYPE,
+ NAME_NOT_FOUND = STATUS_NAME_NOT_FOUND,
+ PERMISSION_DENIED = STATUS_PERMISSION_DENIED,
+ NO_INIT = STATUS_NO_INIT,
+ ALREADY_EXISTS = STATUS_ALREADY_EXISTS,
+ DEAD_OBJECT = STATUS_DEAD_OBJECT,
+ FAILED_TRANSACTION = STATUS_FAILED_TRANSACTION,
+ BAD_INDEX = STATUS_BAD_INDEX,
+ NOT_ENOUGH_DATA = STATUS_NOT_ENOUGH_DATA,
+ WOULD_BLOCK = STATUS_WOULD_BLOCK,
+ TIMED_OUT = STATUS_TIMED_OUT,
+ UNKNOWN_TRANSACTION = STATUS_UNKNOWN_TRANSACTION,
+ FDS_NOT_ALLOWED = STATUS_FDS_NOT_ALLOWED,
+ UNEXPECTED_NULL = STATUS_UNEXPECTED_NULL,
+};
+
+// Expose exception codes from anonymous enum in binder_status.h
+enum ExceptionCode {
+ NONE = EX_NONE,
+ SECURITY = EX_SECURITY,
+ BAD_PARCELABLE = EX_BAD_PARCELABLE,
+ ILLEGAL_ARGUMENT = EX_ILLEGAL_ARGUMENT,
+ NULL_POINTER = EX_NULL_POINTER,
+ ILLEGAL_STATE = EX_ILLEGAL_STATE,
+ NETWORK_MAIN_THREAD = EX_NETWORK_MAIN_THREAD,
+ UNSUPPORTED_OPERATION = EX_UNSUPPORTED_OPERATION,
+ SERVICE_SPECIFIC = EX_SERVICE_SPECIFIC,
+ PARCELABLE = EX_PARCELABLE,
+
+ /**
+ * This is special, and indicates to native binder proxies that the
+ * transaction has failed at a low level.
+ */
+ TRANSACTION_FAILED = EX_TRANSACTION_FAILED,
+};
+
+namespace consts {
+
+enum {
+ FIRST_CALL_TRANSACTION = FIRST_CALL_TRANSACTION,
+ LAST_CALL_TRANSACTION = LAST_CALL_TRANSACTION,
+};
+
+enum {
+ FLAG_ONEWAY = FLAG_ONEWAY,
+};
+
+} // namespace consts
+
+} // namespace c_interface
+
+} // namespace android
diff --git a/libs/binder/rust/TEST_MAPPING b/libs/binder/rust/TEST_MAPPING
new file mode 100644
index 0000000..4470e17
--- /dev/null
+++ b/libs/binder/rust/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "libbinder_rs-internal_test"
+ },
+ {
+ "name": "rustBinderTest"
+ }
+ ]
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
new file mode 100644
index 0000000..f5e7509
--- /dev/null
+++ b/libs/binder/rust/src/binder.rs
@@ -0,0 +1,585 @@
+/*
+ * 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.
+ */
+
+//! Trait definitions for binder objects
+
+use crate::error::{status_t, Result};
+use crate::parcel::Parcel;
+use crate::proxy::{DeathRecipient, SpIBinder};
+use crate::sys;
+
+use std::ffi::{c_void, CString};
+use std::os::unix::io::AsRawFd;
+use std::ptr;
+
+/// Binder action to perform.
+///
+/// This must be a number between [`IBinder::FIRST_CALL_TRANSACTION`] and
+/// [`IBinder::LAST_CALL_TRANSACTION`].
+pub type TransactionCode = u32;
+
+/// Additional operation flags.
+///
+/// Can be either 0 for a normal RPC, or [`IBinder::FLAG_ONEWAY`] for a
+/// one-way RPC.
+pub type TransactionFlags = u32;
+
+/// Super-trait for Binder interfaces.
+///
+/// This trait allows conversion of a Binder interface trait object into an
+/// IBinder object for IPC calls. All Binder remotable interface (i.e. AIDL
+/// interfaces) must implement this trait.
+///
+/// This is equivalent `IInterface` in C++.
+pub trait Interface {
+ /// Convert this binder object into a generic [`SpIBinder`] reference.
+ fn as_binder(&self) -> SpIBinder {
+ panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
+ }
+}
+
+/// A local service that can be remotable via Binder.
+///
+/// An object that implement this interface made be made into a Binder service
+/// via `Binder::new(object)`.
+///
+/// This is a low-level interface that should normally be automatically
+/// generated from AIDL via the [`declare_binder_interface!`] macro. When using
+/// the AIDL backend, users need only implement the high-level AIDL-defined
+/// interface. The AIDL compiler then generates a container struct that wraps
+/// the user-defined service and implements `Remotable`.
+pub trait Remotable: Sync {
+ /// The Binder interface descriptor string.
+ ///
+ /// This string is a unique identifier for a Binder interface, and should be
+ /// the same between all implementations of that interface.
+ fn get_descriptor() -> &'static str;
+
+ /// Handle and reply to a request to invoke a transaction on this object.
+ ///
+ /// `reply` may be [`None`] if the sender does not expect a reply.
+ fn on_transact(&self, code: TransactionCode, data: &Parcel, reply: &mut Parcel) -> Result<()>;
+
+ /// Retrieve the class of this remote object.
+ ///
+ /// This method should always return the same InterfaceClass for the same
+ /// type.
+ fn get_class() -> InterfaceClass;
+}
+
+/// Interface of binder local or remote objects.
+///
+/// This trait corresponds to the interface of the C++ `IBinder` class.
+pub trait IBinder {
+ /// First transaction code available for user commands (inclusive)
+ const FIRST_CALL_TRANSACTION: TransactionCode = sys::FIRST_CALL_TRANSACTION;
+ /// Last transaction code available for user commands (inclusive)
+ const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION;
+
+ /// Corresponds to TF_ONE_WAY -- an asynchronous call.
+ const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
+
+ /// Is this object still alive?
+ fn is_binder_alive(&self) -> bool;
+
+ /// Send a ping transaction to this object
+ fn ping_binder(&mut self) -> Result<()>;
+
+ /// Dump this object to the given file handle
+ fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()>;
+
+ /// Get a new interface that exposes additional extension functionality, if
+ /// available.
+ fn get_extension(&mut self) -> Result<Option<SpIBinder>>;
+
+ /// Perform a generic operation with the object.
+ ///
+ /// # Arguments
+ /// * `code` - Transaction code for the operation
+ /// * `data` - [`Parcel`] with input data
+ /// * `reply` - Optional [`Parcel`] for reply data
+ /// * `flags` - Transaction flags, e.g. marking the transaction as
+ /// asynchronous ([`FLAG_ONEWAY`](IBinder::FLAG_ONEWAY))
+ fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
+ &self,
+ code: TransactionCode,
+ flags: TransactionFlags,
+ input_callback: F,
+ ) -> Result<Parcel>;
+
+ /// Register the recipient for a notification if this binder
+ /// goes away. If this binder object unexpectedly goes away
+ /// (typically because its hosting process has been killed),
+ /// then DeathRecipient::binder_died() will be called with a reference
+ /// to this.
+ ///
+ /// You will only receive death notifications for remote binders,
+ /// as local binders by definition can't die without you dying as well.
+ /// Trying to use this function on a local binder will result in an
+ /// INVALID_OPERATION code being returned and nothing happening.
+ ///
+ /// This link always holds a weak reference to its recipient.
+ ///
+ /// You will only receive a weak reference to the dead
+ /// binder. You should not try to promote this to a strong reference.
+ /// (Nor should you need to, as there is nothing useful you can
+ /// directly do with it now that it has passed on.)
+ fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
+
+ /// Remove a previously registered death notification.
+ /// The recipient will no longer be called if this object
+ /// dies.
+ fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
+}
+
+/// Opaque reference to the type of a Binder interface.
+///
+/// This object encapsulates the Binder interface descriptor string, along with
+/// the binder transaction callback, if the class describes a local service.
+///
+/// A Binder remotable object may only have a single interface class, and any
+/// given object can only be associated with one class. Two objects with
+/// different classes are incompatible, even if both classes have the same
+/// interface descriptor.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct InterfaceClass(*const sys::AIBinder_Class);
+
+impl InterfaceClass {
+ /// Get a Binder NDK `AIBinder_Class` pointer for this object type.
+ ///
+ /// Note: the returned pointer will not be constant. Calling this method
+ /// multiple times for the same type will result in distinct class
+ /// pointers. A static getter for this value is implemented in
+ /// [`declare_binder_interface!`].
+ pub fn new<I: InterfaceClassMethods>() -> InterfaceClass {
+ let descriptor = CString::new(I::get_descriptor()).unwrap();
+ let ptr = unsafe {
+ // Safety: `AIBinder_Class_define` expects a valid C string, and
+ // three valid callback functions, all non-null pointers. The C
+ // string is copied and need not be valid for longer than the call,
+ // so we can drop it after the call. We can safely assign null to
+ // the onDump and handleShellCommand callbacks as long as the class
+ // pointer was non-null. Rust None for a Option<fn> is guaranteed to
+ // be a NULL pointer. Rust retains ownership of the pointer after it
+ // is defined.
+ let class = sys::AIBinder_Class_define(
+ descriptor.as_ptr(),
+ Some(I::on_create),
+ Some(I::on_destroy),
+ Some(I::on_transact),
+ );
+ if class.is_null() {
+ panic!("Expected non-null class pointer from AIBinder_Class_define!");
+ }
+ sys::AIBinder_Class_setOnDump(class, None);
+ sys::AIBinder_Class_setHandleShellCommand(class, None);
+ class
+ };
+ InterfaceClass(ptr)
+ }
+
+ /// Construct an `InterfaceClass` out of a raw, non-null `AIBinder_Class`
+ /// pointer.
+ ///
+ /// # Safety
+ ///
+ /// This function is safe iff `ptr` is a valid, non-null pointer to an
+ /// `AIBinder_Class`.
+ pub(crate) unsafe fn from_ptr(ptr: *const sys::AIBinder_Class) -> InterfaceClass {
+ InterfaceClass(ptr)
+ }
+}
+
+impl From<InterfaceClass> for *const sys::AIBinder_Class {
+ fn from(class: InterfaceClass) -> *const sys::AIBinder_Class {
+ class.0
+ }
+}
+
+/// Create a function implementing a static getter for an interface class.
+///
+/// Each binder interface (i.e. local [`Remotable`] service or remote proxy
+/// [`Interface`]) must have global, static class that uniquely identifies
+/// it. This macro implements an [`InterfaceClass`] getter to simplify these
+/// implementations.
+///
+/// The type of a structure that implements [`InterfaceClassMethods`] must be
+/// passed to this macro. For local services, this should be `Binder<Self>`
+/// since [`Binder`] implements [`InterfaceClassMethods`].
+///
+/// # Examples
+///
+/// When implementing a local [`Remotable`] service `ExampleService`, the
+/// `get_class` method is required in the [`Remotable`] impl block. This macro
+/// should be used as follows to implement this functionality:
+///
+/// ```rust
+/// impl Remotable for ExampleService {
+/// fn get_descriptor() -> &'static str {
+/// "android.os.IExampleInterface"
+/// }
+///
+/// fn on_transact(
+/// &self,
+/// code: TransactionCode,
+/// data: &Parcel,
+/// reply: &mut Parcel,
+/// ) -> Result<()> {
+/// // ...
+/// }
+///
+/// binder_fn_get_class!(Binder<Self>);
+/// }
+/// ```
+macro_rules! binder_fn_get_class {
+ ($class:ty) => {
+ binder_fn_get_class!($crate::InterfaceClass::new::<$class>());
+ };
+
+ ($constructor:expr) => {
+ fn get_class() -> $crate::InterfaceClass {
+ static CLASS_INIT: std::sync::Once = std::sync::Once::new();
+ static mut CLASS: Option<$crate::InterfaceClass> = None;
+
+ CLASS_INIT.call_once(|| unsafe {
+ // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
+ // variable, and therefore is thread-safe, as it can only occur
+ // once.
+ CLASS = Some($constructor);
+ });
+ unsafe {
+ // Safety: The `CLASS` variable can only be mutated once, above,
+ // and is subsequently safe to read from any thread.
+ CLASS.unwrap()
+ }
+ }
+ };
+}
+
+pub trait InterfaceClassMethods {
+ /// Get the interface descriptor string for this object type.
+ fn get_descriptor() -> &'static str
+ where
+ Self: Sized;
+
+ /// Called during construction of a new `AIBinder` object of this interface
+ /// class.
+ ///
+ /// The opaque pointer parameter will be the parameter provided to
+ /// `AIBinder_new`. Returns an opaque userdata to be associated with the new
+ /// `AIBinder` object.
+ ///
+ /// # Safety
+ ///
+ /// Callback called from C++. The parameter argument provided to
+ /// `AIBinder_new` must match the type expected here. The `AIBinder` object
+ /// will take ownership of the returned pointer, which it will free via
+ /// `on_destroy`.
+ unsafe extern "C" fn on_create(args: *mut c_void) -> *mut c_void;
+
+ /// Called when a transaction needs to be processed by the local service
+ /// implementation.
+ ///
+ /// # Safety
+ ///
+ /// Callback called from C++. The `binder` parameter must be a valid pointer
+ /// to a binder object of this class with userdata initialized via this
+ /// class's `on_create`. The parcel parameters must be valid pointers to
+ /// parcel objects.
+ unsafe extern "C" fn on_transact(
+ binder: *mut sys::AIBinder,
+ code: u32,
+ data: *const sys::AParcel,
+ reply: *mut sys::AParcel,
+ ) -> status_t;
+
+ /// Called whenever an `AIBinder` object is no longer referenced and needs
+ /// to be destroyed.
+ ///
+ /// # Safety
+ ///
+ /// Callback called from C++. The opaque pointer parameter must be the value
+ /// returned by `on_create` for this class. This function takes ownership of
+ /// the provided pointer and destroys it.
+ unsafe extern "C" fn on_destroy(object: *mut c_void);
+}
+
+/// Interface for transforming a generic SpIBinder into a specific remote
+/// interface trait.
+///
+/// # Example
+///
+/// For Binder interface `IFoo`, the following implementation should be made:
+/// ```no_run
+/// # use binder::{FromIBinder, SpIBinder, Result};
+/// # trait IFoo {}
+/// impl FromIBinder for dyn IFoo {
+/// fn try_from(ibinder: SpIBinder) -> Result<Box<Self>> {
+/// // ...
+/// # Err(binder::StatusCode::OK)
+/// }
+/// }
+/// ```
+pub trait FromIBinder {
+ /// Try to interpret a generic Binder object as this interface.
+ ///
+ /// Returns a trait object for the `Self` interface if this object
+ /// implements that interface.
+ fn try_from(ibinder: SpIBinder) -> Result<Box<Self>>;
+}
+
+/// Trait for transparent Rust wrappers around android C++ native types.
+///
+/// The pointer return by this trait's methods should be immediately passed to
+/// C++ and not stored by Rust. The pointer is valid only as long as the
+/// underlying C++ object is alive, so users must be careful to take this into
+/// account, as Rust cannot enforce this.
+///
+/// # Safety
+///
+/// For this trait to be a correct implementation, `T` must be a valid android
+/// C++ type. Since we cannot constrain this via the type system, this trait is
+/// marked as unsafe.
+pub unsafe trait AsNative<T> {
+ /// Return a pointer to the native version of `self`
+ fn as_native(&self) -> *const T;
+
+ /// Return a mutable pointer to the native version of `self`
+ fn as_native_mut(&mut self) -> *mut T;
+}
+
+unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> {
+ fn as_native(&self) -> *const T {
+ self.as_ref().map_or(ptr::null(), |v| v.as_native())
+ }
+
+ fn as_native_mut(&mut self) -> *mut T {
+ self.as_mut().map_or(ptr::null_mut(), |v| v.as_native_mut())
+ }
+}
+
+/// Declare typed interfaces for a binder object.
+///
+/// Given an interface trait and descriptor string, create a native and remote
+/// proxy wrapper for this interface. The native service object (`$native`)
+/// implements `Remotable` and will dispatch to the function `$on_transact` to
+/// handle transactions. The typed proxy object (`$proxy`) wraps remote binder
+/// objects for this interface and can optionally contain additional fields.
+///
+/// Assuming the interface trait is `Interface`, `$on_transact` function must
+/// have the following type:
+///
+/// ```
+/// # use binder::{Interface, TransactionCode, Parcel};
+/// # trait Placeholder {
+/// fn on_transact(
+/// service: &dyn Interface,
+/// code: TransactionCode,
+/// data: &Parcel,
+/// reply: &mut Parcel,
+/// ) -> binder::Result<()>;
+/// # }
+/// ```
+///
+/// # Examples
+///
+/// The following example declares the local service type `BnServiceManager` and
+/// a remote proxy type `BpServiceManager` (the `n` and `p` stand for native and
+/// proxy respectively) for the `IServiceManager` Binder interface. The
+/// interfaces will be identified by the descriptor string
+/// "android.os.IServiceManager". The local service will dispatch transactions
+/// using the provided function, `on_transact`.
+///
+/// ```
+/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, Parcel};
+///
+/// pub trait IServiceManager: Interface {
+/// // remote methods...
+/// }
+///
+/// declare_binder_interface! {
+/// IServiceManager["android.os.IServiceManager"] {
+/// native: BnServiceManager(on_transact),
+/// proxy: BpServiceManager,
+/// }
+/// }
+///
+/// fn on_transact(
+/// service: &dyn IServiceManager,
+/// code: TransactionCode,
+/// data: &Parcel,
+/// reply: &mut Parcel,
+/// ) -> binder::Result<()> {
+/// // ...
+/// Ok(())
+/// }
+///
+/// impl IServiceManager for BpServiceManager {
+/// // parceling/unparceling code for the IServiceManager emitted here
+/// }
+///
+/// impl IServiceManager for Binder<BnServiceManager> {
+/// // Forward calls to local implementation
+/// }
+/// ```
+#[macro_export]
+macro_rules! declare_binder_interface {
+ {
+ $interface:path[$descriptor:expr] {
+ native: $native:ident($on_transact:path),
+ proxy: $proxy:ident,
+ }
+ } => {
+ $crate::declare_binder_interface! {
+ $interface[$descriptor] {
+ native: $native($on_transact),
+ proxy: $proxy {},
+ }
+ }
+ };
+
+ {
+ $interface:path[$descriptor:expr] {
+ native: $native:ident($on_transact:path),
+ proxy: $proxy:ident {
+ $($fname:ident: $fty:ty = $finit:expr),*
+ },
+ }
+ } => {
+ $crate::declare_binder_interface! {
+ $interface[$descriptor] {
+ @doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")]
+ native: $native($on_transact),
+ @doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
+ proxy: $proxy {
+ $($fname: $fty = $finit),*
+ },
+ }
+ }
+ };
+
+ {
+ $interface:path[$descriptor:expr] {
+ @doc[$native_doc:expr]
+ native: $native:ident($on_transact:path),
+
+ @doc[$proxy_doc:expr]
+ proxy: $proxy:ident {
+ $($fname:ident: $fty:ty = $finit:expr),*
+ },
+ }
+ } => {
+ #[doc = $proxy_doc]
+ pub struct $proxy {
+ binder: $crate::SpIBinder,
+ $($fname: $fty,)*
+ }
+
+ impl $crate::Interface for $proxy {
+ fn as_binder(&self) -> $crate::SpIBinder {
+ self.binder.clone()
+ }
+ }
+
+ impl $crate::Proxy for $proxy
+ where
+ $proxy: $interface,
+ {
+ fn get_descriptor() -> &'static str {
+ $descriptor
+ }
+
+ fn from_binder(mut binder: $crate::SpIBinder) -> $crate::Result<Self> {
+ use $crate::AssociateClass;
+ if binder.associate_class(<$native as $crate::Remotable>::get_class()) {
+ Ok(Self { binder, $($fname: $finit),* })
+ } else {
+ Err($crate::StatusCode::BAD_TYPE)
+ }
+ }
+ }
+
+ #[doc = $native_doc]
+ #[repr(transparent)]
+ pub struct $native(Box<dyn $interface + Sync + Send + 'static>);
+
+ impl $native {
+ /// Create a new binder service.
+ pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> impl $interface {
+ $crate::Binder::new($native(Box::new(inner)))
+ }
+ }
+
+ impl $crate::Remotable for $native {
+ fn get_descriptor() -> &'static str {
+ $descriptor
+ }
+
+ fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::Parcel, reply: &mut $crate::Parcel) -> $crate::Result<()> {
+ $on_transact(&*self.0, code, data, reply)
+ }
+
+ fn get_class() -> $crate::InterfaceClass {
+ static CLASS_INIT: std::sync::Once = std::sync::Once::new();
+ static mut CLASS: Option<$crate::InterfaceClass> = None;
+
+ CLASS_INIT.call_once(|| unsafe {
+ // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
+ // variable, and therefore is thread-safe, as it can only occur
+ // once.
+ CLASS = Some($crate::InterfaceClass::new::<$crate::Binder<$native>>());
+ });
+ unsafe {
+ // Safety: The `CLASS` variable can only be mutated once, above,
+ // and is subsequently safe to read from any thread.
+ CLASS.unwrap()
+ }
+ }
+ }
+
+ impl $crate::FromIBinder for dyn $interface {
+ fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<Box<dyn $interface>> {
+ use $crate::AssociateClass;
+ if !ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
+ return Err($crate::StatusCode::BAD_TYPE.into());
+ }
+
+ let service: $crate::Result<$crate::Binder<$native>> = std::convert::TryFrom::try_from(ibinder.clone());
+ if let Ok(service) = service {
+ Ok(Box::new(service))
+ } else {
+ Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?))
+ }
+ }
+ }
+
+ impl $crate::parcel::Serialize for dyn $interface + '_
+ where
+ $interface: $crate::Interface
+ {
+ fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ let binder = $crate::Interface::as_binder(self);
+ parcel.write(&binder)
+ }
+ }
+
+ impl $crate::parcel::SerializeOption for dyn $interface + '_ {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ parcel.write(&this.map($crate::Interface::as_binder))
+ }
+ }
+ };
+}
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
new file mode 100644
index 0000000..289b157
--- /dev/null
+++ b/libs/binder/rust/src/error.rs
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ */
+
+use crate::binder::AsNative;
+use crate::sys;
+
+use std::error;
+use std::ffi::CStr;
+use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
+use std::result;
+
+pub use sys::binder_status_t as status_t;
+
+/// Low-level status codes from Android `libutils`.
+// All error codes are negative integer values. Derived from the anonymous enum
+// in utils/Errors.h
+pub use sys::android_c_interface_StatusCode as StatusCode;
+
+/// A specialized [`Result`](result::Result) for binder operations.
+pub type Result<T> = result::Result<T, StatusCode>;
+
+/// Convert a low-level status code into an empty result.
+///
+/// An OK status is converted into an `Ok` result, any other status is converted
+/// into an `Err` result holding the status code.
+pub fn status_result(status: status_t) -> Result<()> {
+ match parse_status_code(status) {
+ StatusCode::OK => Ok(()),
+ e => Err(e),
+ }
+}
+
+// impl Display for StatusCode {
+// fn fmt(&self, f: &mut Formatter) -> FmtResult {
+// write!(f, "StatusCode::{:?}", self)
+// }
+// }
+
+// impl error::Error for StatusCode {}
+
+fn parse_status_code(code: i32) -> StatusCode {
+ match code {
+ e if e == StatusCode::OK as i32 => StatusCode::OK,
+ e if e == StatusCode::NO_MEMORY as i32 => StatusCode::NO_MEMORY,
+ e if e == StatusCode::INVALID_OPERATION as i32 => StatusCode::INVALID_OPERATION,
+ e if e == StatusCode::BAD_VALUE as i32 => StatusCode::BAD_VALUE,
+ e if e == StatusCode::BAD_TYPE as i32 => StatusCode::BAD_TYPE,
+ e if e == StatusCode::NAME_NOT_FOUND as i32 => StatusCode::NAME_NOT_FOUND,
+ e if e == StatusCode::PERMISSION_DENIED as i32 => StatusCode::PERMISSION_DENIED,
+ e if e == StatusCode::NO_INIT as i32 => StatusCode::NO_INIT,
+ e if e == StatusCode::ALREADY_EXISTS as i32 => StatusCode::ALREADY_EXISTS,
+ e if e == StatusCode::DEAD_OBJECT as i32 => StatusCode::DEAD_OBJECT,
+ e if e == StatusCode::FAILED_TRANSACTION as i32 => StatusCode::FAILED_TRANSACTION,
+ e if e == StatusCode::BAD_INDEX as i32 => StatusCode::BAD_INDEX,
+ e if e == StatusCode::NOT_ENOUGH_DATA as i32 => StatusCode::NOT_ENOUGH_DATA,
+ e if e == StatusCode::WOULD_BLOCK as i32 => StatusCode::WOULD_BLOCK,
+ e if e == StatusCode::TIMED_OUT as i32 => StatusCode::TIMED_OUT,
+ e if e == StatusCode::UNKNOWN_TRANSACTION as i32 => StatusCode::UNKNOWN_TRANSACTION,
+ e if e == StatusCode::FDS_NOT_ALLOWED as i32 => StatusCode::FDS_NOT_ALLOWED,
+ e if e == StatusCode::UNEXPECTED_NULL as i32 => StatusCode::UNEXPECTED_NULL,
+ _ => StatusCode::UNKNOWN_ERROR,
+ }
+}
+
+pub use sys::android_c_interface_ExceptionCode as ExceptionCode;
+
+fn parse_exception_code(code: i32) -> ExceptionCode {
+ match code {
+ e if e == ExceptionCode::NONE as i32 => ExceptionCode::NONE,
+ e if e == ExceptionCode::SECURITY as i32 => ExceptionCode::SECURITY,
+ e if e == ExceptionCode::BAD_PARCELABLE as i32 => ExceptionCode::BAD_PARCELABLE,
+ e if e == ExceptionCode::ILLEGAL_ARGUMENT as i32 => ExceptionCode::ILLEGAL_ARGUMENT,
+ e if e == ExceptionCode::NULL_POINTER as i32 => ExceptionCode::NULL_POINTER,
+ e if e == ExceptionCode::ILLEGAL_STATE as i32 => ExceptionCode::ILLEGAL_STATE,
+ e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => {
+ ExceptionCode::NETWORK_MAIN_THREAD
+ }
+ e if e == ExceptionCode::UNSUPPORTED_OPERATION as i32 => {
+ ExceptionCode::UNSUPPORTED_OPERATION
+ }
+ e if e == ExceptionCode::SERVICE_SPECIFIC as i32 => ExceptionCode::SERVICE_SPECIFIC,
+ _ => ExceptionCode::TRANSACTION_FAILED,
+ }
+}
+
+// Safety: `Status` always contains a owning pointer to a valid `AStatus`. The
+// lifetime of the contained pointer is the same as the `Status` object.
+/// High-level binder status object that encapsulates a standard way to keep
+/// track of and chain binder errors along with service specific errors.
+///
+/// Used in AIDL transactions to represent failed transactions.
+pub struct Status(*mut sys::AStatus);
+
+impl Status {
+ /// Create a status object representing a successful transaction.
+ pub fn ok() -> Self {
+ let ptr = unsafe {
+ // Safety: `AStatus_newOk` always returns a new, heap allocated
+ // pointer to an `ASTatus` object, so we know this pointer will be
+ // valid.
+ //
+ // Rust takes ownership of the returned pointer.
+ sys::AStatus_newOk()
+ };
+ Self(ptr)
+ }
+
+ /// Create a status object from a service specific error
+ pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status {
+ let ptr = if let Some(message) = message {
+ unsafe {
+ // Safety: Any i32 is a valid service specific error for the
+ // error code parameter. We construct a valid, null-terminated
+ // `CString` from the message, which must be a valid C-style
+ // string to pass as the message. This function always returns a
+ // new, heap allocated pointer to an `AStatus` object, so we
+ // know the returned pointer will be valid.
+ //
+ // Rust takes ownership of the returned pointer.
+ sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr())
+ }
+ } else {
+ unsafe {
+ // Safety: Any i32 is a valid service specific error for the
+ // error code parameter. This function always returns a new,
+ // heap allocated pointer to an `AStatus` object, so we know the
+ // returned pointer will be valid.
+ //
+ // Rust takes ownership of the returned pointer.
+ sys::AStatus_fromServiceSpecificError(err)
+ }
+ };
+ Self(ptr)
+ }
+
+ /// Create a status object from an exception code
+ pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status {
+ if let Some(message) = message {
+ let ptr = unsafe {
+ sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr())
+ };
+ Self(ptr)
+ } else {
+ exception.into()
+ }
+ }
+
+ /// Create a status object from a raw `AStatus` pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`.
+ pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self {
+ Self(ptr)
+ }
+
+ /// Returns `true` if this status represents a successful transaction.
+ pub fn is_ok(&self) -> bool {
+ unsafe {
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_isOk` here.
+ sys::AStatus_isOk(self.as_native())
+ }
+ }
+
+ /// Returns a description of the status.
+ pub fn get_description(&self) -> String {
+ let description_ptr = unsafe {
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_getDescription`
+ // here.
+ //
+ // `AStatus_getDescription` always returns a valid pointer to a null
+ // terminated C string. Rust is responsible for freeing this pointer
+ // via `AStatus_deleteDescription`.
+ sys::AStatus_getDescription(self.as_native())
+ };
+ let description = unsafe {
+ // Safety: `AStatus_getDescription` always returns a valid C string,
+ // which can be safely converted to a `CStr`.
+ CStr::from_ptr(description_ptr)
+ };
+ let description = description.to_string_lossy().to_string();
+ unsafe {
+ // Safety: `description_ptr` was returned from
+ // `AStatus_getDescription` above, and must be freed via
+ // `AStatus_deleteDescription`. We must not access the pointer after
+ // this call, so we copy it into an owned string above and return
+ // that string.
+ sys::AStatus_deleteDescription(description_ptr);
+ }
+ description
+ }
+
+ /// Returns the exception code of the status.
+ pub fn exception_code(&self) -> ExceptionCode {
+ let code = unsafe {
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_getExceptionCode`
+ // here.
+ sys::AStatus_getExceptionCode(self.as_native())
+ };
+ parse_exception_code(code)
+ }
+
+ /// Return a status code representing a transaction failure, or
+ /// `StatusCode::OK` if there was no transaction failure.
+ ///
+ /// If this method returns `OK`, the status may still represent a different
+ /// exception or a service specific error. To find out if this transaction
+ /// as a whole is okay, use [`is_ok`](Self::is_ok) instead.
+ pub fn transaction_error(&self) -> StatusCode {
+ let code = unsafe {
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to `AStatus_getStatus` here.
+ sys::AStatus_getStatus(self.as_native())
+ };
+ parse_status_code(code)
+ }
+
+ /// Return a service specific error if this status represents one.
+ ///
+ /// This function will only ever return a non-zero result if
+ /// [`exception_code`](Self::exception_code) returns
+ /// `ExceptionCode::SERVICE_SPECIFIC`. If this function returns 0, the
+ /// status object may still represent a different exception or status. To
+ /// find out if this transaction as a whole is okay, use
+ /// [`is_ok`](Self::is_ok) instead.
+ pub fn service_specific_error(&self) -> i32 {
+ unsafe {
+ // Safety: `Status` always contains a valid `AStatus` pointer, so we
+ // are always passing a valid pointer to
+ // `AStatus_getServiceSpecificError` here.
+ sys::AStatus_getServiceSpecificError(self.as_native())
+ }
+ }
+
+ /// Calls `op` if the status was ok, otherwise returns an `Err` value of
+ /// `self`.
+ pub fn and_then<T, F>(self, op: F) -> result::Result<T, Status>
+ where
+ F: FnOnce() -> result::Result<T, Status>,
+ {
+ <result::Result<(), Status>>::from(self)?;
+ op()
+ }
+}
+
+impl error::Error for Status {}
+
+impl Display for Status {
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ f.write_str(&self.get_description())
+ }
+}
+
+impl Debug for Status {
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ f.write_str(&self.get_description())
+ }
+}
+
+impl PartialEq for Status {
+ fn eq(&self, other: &Status) -> bool {
+ let self_code = self.exception_code();
+ let other_code = other.exception_code();
+
+ match (self_code, other_code) {
+ (ExceptionCode::NONE, ExceptionCode::NONE) => true,
+ (ExceptionCode::TRANSACTION_FAILED, ExceptionCode::TRANSACTION_FAILED) => {
+ self.transaction_error() == other.transaction_error()
+ && self.get_description() == other.get_description()
+ }
+ (ExceptionCode::SERVICE_SPECIFIC, ExceptionCode::SERVICE_SPECIFIC) => {
+ self.service_specific_error() == other.service_specific_error()
+ && self.get_description() == other.get_description()
+ }
+ (e1, e2) => e1 == e2 && self.get_description() == other.get_description(),
+ }
+ }
+}
+
+impl Eq for Status {}
+
+impl From<StatusCode> for Status {
+ fn from(status: StatusCode) -> Status {
+ (status as status_t).into()
+ }
+}
+
+impl From<status_t> for Status {
+ fn from(status: status_t) -> Status {
+ let ptr = unsafe {
+ // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
+ // this is a safe FFI call. Unknown values will be coerced into
+ // UNKNOWN_ERROR.
+ sys::AStatus_fromStatus(status)
+ };
+ Self(ptr)
+ }
+}
+
+impl From<ExceptionCode> for Status {
+ fn from(code: ExceptionCode) -> Status {
+ let ptr = unsafe {
+ // Safety: `AStatus_fromExceptionCode` expects any
+ // `binder_exception_t` (i32) integer, so this is a safe FFI call.
+ // Unknown values will be coerced into EX_TRANSACTION_FAILED.
+ sys::AStatus_fromExceptionCode(code as i32)
+ };
+ Self(ptr)
+ }
+}
+
+// TODO: impl Try for Status when try_trait is stabilized
+// https://github.com/rust-lang/rust/issues/42327
+impl From<Status> for result::Result<(), Status> {
+ fn from(status: Status) -> result::Result<(), Status> {
+ if status.is_ok() {
+ Ok(())
+ } else {
+ Err(status)
+ }
+ }
+}
+
+impl From<Status> for status_t {
+ fn from(status: Status) -> status_t {
+ status.transaction_error() as status_t
+ }
+}
+
+impl Drop for Status {
+ fn drop(&mut self) {
+ unsafe {
+ // Safety: `Status` manages the lifetime of its inner `AStatus`
+ // pointee, so we need to delete it here. We know that the pointer
+ // will be valid here since `Status` always contains a valid pointer
+ // while it is alive.
+ sys::AStatus_delete(self.0);
+ }
+ }
+}
+
+/// # Safety
+///
+/// `Status` always contains a valid pointer to an `AStatus` object, so we can
+/// trivially convert it to a correctly-typed raw pointer.
+///
+/// Care must be taken that the returned pointer is only dereferenced while the
+/// `Status` object is still alive.
+unsafe impl AsNative<sys::AStatus> for Status {
+ fn as_native(&self) -> *const sys::AStatus {
+ self.0
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AStatus {
+ self.0
+ }
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
new file mode 100644
index 0000000..4b9cccf
--- /dev/null
+++ b/libs/binder/rust/src/lib.rs
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+//! Safe Rust interface to Android `libbinder`.
+//!
+//! This crate is primarily designed as an target for a Rust AIDL compiler
+//! backend, and should generally not be used directly by users. It is built on
+//! top of the binder NDK library to be usable by APEX modules, and therefore
+//! only exposes functionality available in the NDK interface.
+//!
+//! # Example
+//!
+//! The following example illustrates how the AIDL backend will use this crate.
+//!
+//! ```
+//! use binder::{
+//! declare_binder_interface, Binder, IBinder, Interface, Remotable, Parcel, SpIBinder,
+//! StatusCode, TransactionCode,
+//! };
+//!
+//! // Generated by AIDL compiler
+//! pub trait ITest: Interface {
+//! fn test(&self) -> binder::Result<String>;
+//! }
+//!
+//! // Creates a new local (native) service object, BnTest, and a remote proxy
+//! // object, BpTest, that are the typed interfaces for their respective ends
+//! // of the binder transaction. Generated by AIDL compiler.
+//! declare_binder_interface! {
+//! ITest["android.os.ITest"] {
+//! native: BnTest(on_transact),
+//! proxy: BpTest,
+//! }
+//! }
+//!
+//! // Generated by AIDL compiler
+//! fn on_transact(
+//! service: &dyn ITest,
+//! code: TransactionCode,
+//! _data: &Parcel,
+//! reply: &mut Parcel,
+//! ) -> binder::Result<()> {
+//! match code {
+//! SpIBinder::FIRST_CALL_TRANSACTION => {
+//! reply.write(&service.test()?)?;
+//! Ok(())
+//! }
+//! _ => Err(StatusCode::UNKNOWN_TRANSACTION),
+//! }
+//! }
+//!
+//! // Generated by AIDL compiler
+//! impl ITest for Binder<BnTest> {
+//! fn test(&self) -> binder::Result<String> {
+//! self.0.test()
+//! }
+//! }
+//!
+//! // Generated by AIDL compiler
+//! impl ITest for BpTest {
+//! fn test(&self) -> binder::Result<String> {
+//! let reply = self
+//! .as_binder()
+//! .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?;
+//! reply.read()
+//! }
+//! }
+//!
+//! // User implemented:
+//!
+//! // Local implementation of the ITest remotable interface.
+//! struct TestService;
+//!
+//! impl Interface for TestService {}
+//!
+//! impl ITest for TestService {
+//! fn test(&self) -> binder::Result<String> {
+//! Ok("testing service".to_string())
+//! }
+//! }
+//! ```
+
+#[macro_use]
+mod proxy;
+
+#[macro_use]
+mod binder;
+mod error;
+mod native;
+mod state;
+
+use binder_ndk_bindgen as sys;
+
+pub mod parcel;
+
+pub use crate::binder::{
+ FromIBinder, IBinder, Interface, InterfaceClass, Remotable, TransactionCode, TransactionFlags,
+};
+pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
+pub use native::add_service;
+pub use native::Binder;
+pub use parcel::Parcel;
+pub use proxy::{get_interface, get_service};
+pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
+pub use state::{ProcessState, ThreadState};
+
+/// The public API usable outside AIDL-generated interface crates.
+pub mod public_api {
+ pub use super::parcel::ParcelFileDescriptor;
+ pub use super::{add_service, get_interface};
+ pub use super::{
+ ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode,
+ };
+
+ /// Binder result containing a [`Status`] on error.
+ pub type Result<T> = std::result::Result<T, Status>;
+}
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
new file mode 100644
index 0000000..798fed8
--- /dev/null
+++ b/libs/binder/rust/src/native.rs
@@ -0,0 +1,358 @@
+/*
+ * 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.
+ */
+
+use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode};
+use crate::error::{status_result, status_t, Result, StatusCode};
+use crate::parcel::{Parcel, Serialize};
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::convert::TryFrom;
+use std::ffi::{c_void, CString};
+use std::mem::ManuallyDrop;
+use std::ops::Deref;
+use std::ptr;
+
+/// Rust wrapper around Binder remotable objects.
+///
+/// Implements the C++ `BBinder` class, and therefore implements the C++
+/// `IBinder` interface.
+#[repr(C)]
+pub struct Binder<T: Remotable> {
+ ibinder: *mut sys::AIBinder,
+ rust_object: *mut T,
+}
+
+impl<T: Remotable> Binder<T> {
+ /// Create a new Binder remotable object.
+ ///
+ /// This moves the `rust_object` into an owned [`Box`] and Binder will
+ /// manage its lifetime.
+ pub fn new(rust_object: T) -> Binder<T> {
+ let class = T::get_class();
+ let rust_object = Box::into_raw(Box::new(rust_object));
+ let ibinder = unsafe {
+ // Safety: `AIBinder_new` expects a valid class pointer (which we
+ // initialize via `get_class`), and an arbitrary pointer
+ // argument. The caller owns the returned `AIBinder` pointer, which
+ // is a strong reference to a `BBinder`. This reference should be
+ // decremented via `AIBinder_decStrong` when the reference lifetime
+ // ends.
+ sys::AIBinder_new(class.into(), rust_object as *mut c_void)
+ };
+ Binder {
+ ibinder,
+ rust_object,
+ }
+ }
+
+ /// Set the extension of a binder interface. This allows a downstream
+ /// developer to add an extension to an interface without modifying its
+ /// interface file. This should be called immediately when the object is
+ /// created before it is passed to another thread.
+ ///
+ /// # Examples
+ ///
+ /// For instance, imagine if we have this Binder AIDL interface definition:
+ /// interface IFoo { void doFoo(); }
+ ///
+ /// If an unrelated owner (perhaps in a downstream codebase) wants to make a
+ /// change to the interface, they have two options:
+ ///
+ /// 1) Historical option that has proven to be BAD! Only the original
+ /// author of an interface should change an interface. If someone
+ /// downstream wants additional functionality, they should not ever
+ /// change the interface or use this method.
+ /// ```AIDL
+ /// BAD TO DO: interface IFoo { BAD TO DO
+ /// BAD TO DO: void doFoo(); BAD TO DO
+ /// BAD TO DO: + void doBar(); // adding a method BAD TO DO
+ /// BAD TO DO: } BAD TO DO
+ /// ```
+ ///
+ /// 2) Option that this method enables!
+ /// Leave the original interface unchanged (do not change IFoo!).
+ /// Instead, create a new AIDL interface in a downstream package:
+ /// ```AIDL
+ /// package com.<name>; // new functionality in a new package
+ /// interface IBar { void doBar(); }
+ /// ```
+ ///
+ /// When registering the interface, add:
+ ///
+ /// # use binder::{Binder, Interface};
+ /// # type MyFoo = ();
+ /// # type MyBar = ();
+ /// # let my_foo = ();
+ /// # let my_bar = ();
+ /// let mut foo: Binder<MyFoo> = Binder::new(my_foo); // class in AOSP codebase
+ /// let bar: Binder<MyBar> = Binder::new(my_bar); // custom extension class
+ /// foo.set_extension(&mut bar.as_binder()); // use method in Binder
+ ///
+ /// Then, clients of `IFoo` can get this extension:
+ ///
+ /// # use binder::{declare_binder_interface, Binder, TransactionCode, Parcel};
+ /// # trait IBar {}
+ /// # declare_binder_interface! {
+ /// # IBar["test"] {
+ /// # native: BnBar(on_transact),
+ /// # proxy: BpBar,
+ /// # }
+ /// # }
+ /// # fn on_transact(
+ /// # service: &dyn IBar,
+ /// # code: TransactionCode,
+ /// # data: &Parcel,
+ /// # reply: &mut Parcel,
+ /// # ) -> binder::Result<()> {
+ /// # Ok(())
+ /// # }
+ /// # impl IBar for BpBar {}
+ /// # impl IBar for Binder<BnBar> {}
+ /// # fn main() -> binder::Result<()> {
+ /// # let binder = Binder::new(());
+ /// if let Some(barBinder) = binder.get_extension()? {
+ /// let bar = BpBar::new(barBinder)
+ /// .expect("Extension was not of type IBar");
+ /// } else {
+ /// // There was no extension
+ /// }
+ /// # }
+ pub fn set_extension(&mut self, extension: &mut SpIBinder) -> Result<()> {
+ let status = unsafe {
+ // Safety: `AIBinder_setExtension` expects two valid, mutable
+ // `AIBinder` pointers. We are guaranteed that both `self` and
+ // `extension` contain valid `AIBinder` pointers, because they
+ // cannot be initialized without a valid
+ // pointer. `AIBinder_setExtension` does not take ownership of
+ // either parameter.
+ sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut())
+ };
+ status_result(status)
+ }
+
+ /// Retrieve the interface descriptor string for this object's Binder
+ /// interface.
+ pub fn get_descriptor() -> &'static str {
+ T::get_descriptor()
+ }
+}
+
+impl<T: Remotable> Interface for Binder<T> {
+ /// Converts the local remotable object into a generic `SpIBinder`
+ /// reference.
+ ///
+ /// The resulting `SpIBinder` will hold its own strong reference to this
+ /// remotable object, which will prevent the object from being dropped while
+ /// the `SpIBinder` is alive.
+ fn as_binder(&self) -> SpIBinder {
+ unsafe {
+ // Safety: `self.ibinder` is guaranteed to always be a valid pointer
+ // to an `AIBinder` by the `Binder` constructor. We are creating a
+ // copy of the `self.ibinder` strong reference, but
+ // `SpIBinder::from_raw` assumes it receives an owned pointer with
+ // its own strong reference. We first increment the reference count,
+ // so that the new `SpIBinder` will be tracked as a new reference.
+ sys::AIBinder_incStrong(self.ibinder);
+ SpIBinder::from_raw(self.ibinder).unwrap()
+ }
+ }
+}
+
+impl<T: Remotable> InterfaceClassMethods for Binder<T> {
+ fn get_descriptor() -> &'static str {
+ <T as Remotable>::get_descriptor()
+ }
+
+ /// Called whenever a transaction needs to be processed by a local
+ /// implementation.
+ ///
+ /// # Safety
+ ///
+ /// Must be called with a non-null, valid pointer to a local `AIBinder` that
+ /// contains a `T` pointer in its user data. The `data` and `reply` parcel
+ /// parameters must be valid pointers to `AParcel` objects. This method does
+ /// not take ownership of any of its parameters.
+ ///
+ /// These conditions hold when invoked by `ABBinder::onTransact`.
+ unsafe extern "C" fn on_transact(
+ binder: *mut sys::AIBinder,
+ code: u32,
+ data: *const sys::AParcel,
+ reply: *mut sys::AParcel,
+ ) -> status_t {
+ let res = {
+ let mut reply = Parcel::borrowed(reply).unwrap();
+ let data = Parcel::borrowed(data as *mut sys::AParcel).unwrap();
+ let object = sys::AIBinder_getUserData(binder);
+ let binder: &T = &*(object as *const T);
+ binder.on_transact(code, &data, &mut reply)
+ };
+ match res {
+ Ok(()) => 0i32,
+ Err(e) => e as i32,
+ }
+ }
+
+ /// Called whenever an `AIBinder` object is no longer referenced and needs
+ /// destroyed.
+ ///
+ /// # Safety
+ ///
+ /// Must be called with a valid pointer to a `T` object. After this call,
+ /// the pointer will be invalid and should not be dereferenced.
+ unsafe extern "C" fn on_destroy(object: *mut c_void) {
+ ptr::drop_in_place(object as *mut T)
+ }
+
+ /// Called whenever a new, local `AIBinder` object is needed of a specific
+ /// class.
+ ///
+ /// Constructs the user data pointer that will be stored in the object,
+ /// which will be a heap-allocated `T` object.
+ ///
+ /// # Safety
+ ///
+ /// Must be called with a valid pointer to a `T` object allocated via `Box`.
+ unsafe extern "C" fn on_create(args: *mut c_void) -> *mut c_void {
+ // We just return the argument, as it is already a pointer to the rust
+ // object created by Box.
+ args
+ }
+}
+
+impl<T: Remotable> Drop for Binder<T> {
+ // This causes C++ to decrease the strong ref count of the `AIBinder`
+ // object. We specifically do not drop the `rust_object` here. When C++
+ // actually destroys the object, it calls `on_destroy` and we can drop the
+ // `rust_object` then.
+ fn drop(&mut self) {
+ unsafe {
+ // Safety: When `self` is dropped, we can no longer access the
+ // reference, so can decrement the reference count. `self.ibinder`
+ // is always a valid `AIBinder` pointer, so is valid to pass to
+ // `AIBinder_decStrong`.
+ sys::AIBinder_decStrong(self.ibinder);
+ }
+ }
+}
+
+impl<T: Remotable> Deref for Binder<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ unsafe {
+ // Safety: While `self` is alive, the reference count of the
+ // underlying object is > 0 and therefore `on_destroy` cannot be
+ // called. Therefore while `self` is alive, we know that
+ // `rust_object` is still a valid pointer to a heap allocated object
+ // of type `T`.
+ &*self.rust_object
+ }
+ }
+}
+
+impl<B: Remotable> Serialize for Binder<B> {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ parcel.write_binder(Some(&self.as_binder()))
+ }
+}
+
+// This implementation is an idiomatic implementation of the C++
+// `IBinder::localBinder` interface if the binder object is a Rust binder
+// service.
+impl<B: Remotable> TryFrom<SpIBinder> for Binder<B> {
+ type Error = StatusCode;
+
+ fn try_from(mut ibinder: SpIBinder) -> Result<Self> {
+ let class = B::get_class();
+ if Some(class) != ibinder.get_class() {
+ return Err(StatusCode::BAD_TYPE);
+ }
+ let userdata = unsafe {
+ // Safety: `SpIBinder` always holds a valid pointer pointer to an
+ // `AIBinder`, which we can safely pass to
+ // `AIBinder_getUserData`. `ibinder` retains ownership of the
+ // returned pointer.
+ sys::AIBinder_getUserData(ibinder.as_native_mut())
+ };
+ if userdata.is_null() {
+ return Err(StatusCode::UNEXPECTED_NULL);
+ }
+ // We are transferring the ownership of the AIBinder into the new Binder
+ // object.
+ let mut ibinder = ManuallyDrop::new(ibinder);
+ Ok(Binder {
+ ibinder: ibinder.as_native_mut(),
+ rust_object: userdata as *mut B,
+ })
+ }
+}
+
+/// # Safety
+///
+/// The constructor for `Binder` guarantees that `self.ibinder` will contain a
+/// valid, non-null pointer to an `AIBinder`, so this implementation is type
+/// safe. `self.ibinder` will remain valid for the entire lifetime of `self`
+/// because we hold a strong reference to the `AIBinder` until `self` is
+/// dropped.
+unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> {
+ fn as_native(&self) -> *const sys::AIBinder {
+ self.ibinder
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AIBinder {
+ self.ibinder
+ }
+}
+
+/// Register a new service with the default service manager.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier.
+pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
+ let instance = CString::new(identifier).unwrap();
+ let status = unsafe {
+ // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both
+ // pointers. `AServiceManager_addService` creates a new strong reference
+ // and copies the string, so both pointers need only be valid until the
+ // call returns.
+ sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr())
+ };
+ status_result(status)
+}
+
+/// Tests often create a base BBinder instance; so allowing the unit
+/// type to be remotable translates nicely to Binder::new(()).
+impl Remotable for () {
+ fn get_descriptor() -> &'static str {
+ ""
+ }
+
+ fn on_transact(
+ &self,
+ _code: TransactionCode,
+ _data: &Parcel,
+ _reply: &mut Parcel,
+ ) -> Result<()> {
+ Ok(())
+ }
+
+ binder_fn_get_class!(Binder::<Self>);
+}
+
+impl Interface for () {}
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
new file mode 100644
index 0000000..43850fe
--- /dev/null
+++ b/libs/binder/rust/src/parcel.rs
@@ -0,0 +1,447 @@
+/*
+ * 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.
+ */
+
+//! Container for messages that are sent via binder.
+
+use crate::binder::AsNative;
+use crate::error::{status_result, Result, StatusCode};
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::convert::TryInto;
+use std::mem::ManuallyDrop;
+use std::ptr;
+
+mod file_descriptor;
+mod parcelable;
+
+pub use self::file_descriptor::ParcelFileDescriptor;
+pub use self::parcelable::{
+ Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption,
+};
+
+/// Container for a message (data and object references) that can be sent
+/// through Binder.
+///
+/// A Parcel can contain both serialized data that will be deserialized on the
+/// other side of the IPC, and references to live Binder objects that will
+/// result in the other side receiving a proxy Binder connected with the
+/// original Binder in the Parcel.
+pub enum Parcel {
+ /// Owned parcel pointer
+ Owned(*mut sys::AParcel),
+ /// Borrowed parcel pointer (will not be destroyed on drop)
+ Borrowed(*mut sys::AParcel),
+}
+
+/// # Safety
+///
+/// The `Parcel` constructors guarantee that a `Parcel` object will always
+/// contain a valid pointer to an `AParcel`.
+unsafe impl AsNative<sys::AParcel> for Parcel {
+ fn as_native(&self) -> *const sys::AParcel {
+ match *self {
+ Self::Owned(x) | Self::Borrowed(x) => x,
+ }
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AParcel {
+ match *self {
+ Self::Owned(x) | Self::Borrowed(x) => x,
+ }
+ }
+}
+
+impl Parcel {
+ /// Create a borrowed reference to a parcel object from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe if the raw pointer parameter is either null
+ /// (resulting in `None`), or a valid pointer to an `AParcel` object.
+ pub(crate) unsafe fn borrowed(ptr: *mut sys::AParcel) -> Option<Parcel> {
+ ptr.as_mut().map(|ptr| Self::Borrowed(ptr))
+ }
+
+ /// Create an owned reference to a parcel object from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe if the raw pointer parameter is either null
+ /// (resulting in `None`), or a valid pointer to an `AParcel` object. The
+ /// parcel object must be owned by the caller prior to this call, as this
+ /// constructor takes ownership of the parcel and will destroy it on drop.
+ pub(crate) unsafe fn owned(ptr: *mut sys::AParcel) -> Option<Parcel> {
+ ptr.as_mut().map(|ptr| Self::Owned(ptr))
+ }
+
+ /// Consume the parcel, transferring ownership to the caller if the parcel
+ /// was owned.
+ pub(crate) fn into_raw(mut self) -> *mut sys::AParcel {
+ let ptr = self.as_native_mut();
+ let _ = ManuallyDrop::new(self);
+ ptr
+ }
+}
+
+// Data serialization methods
+impl Parcel {
+ /// Write a type that implements [`Serialize`] to the `Parcel`.
+ pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
+ parcelable.serialize(self)
+ }
+
+ /// Writes the length of a slice to the `Parcel`.
+ ///
+ /// This is used in AIDL-generated client side code to indicate the
+ /// allocated space for an output array parameter.
+ pub fn write_slice_size<T>(&mut self, slice: Option<&[T]>) -> Result<()> {
+ if let Some(slice) = slice {
+ let len: i32 = slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?;
+ self.write(&len)
+ } else {
+ self.write(&-1i32)
+ }
+ }
+
+ /// Returns the current position in the parcel data.
+ pub fn get_data_position(&self) -> i32 {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an `AParcel`,
+ // and this call is otherwise safe.
+ sys::AParcel_getDataPosition(self.as_native())
+ }
+ }
+
+ /// Move the current read/write position in the parcel.
+ ///
+ /// The new position must be a position previously returned by
+ /// `self.get_data_position()`.
+ ///
+ /// # Safety
+ ///
+ /// This method is safe if `pos` is less than the current size of the parcel
+ /// data buffer. Otherwise, we are relying on correct bounds checking in the
+ /// Parcel C++ code on every subsequent read or write to this parcel. If all
+ /// accesses are bounds checked, this call is still safe, but we can't rely
+ /// on that.
+ pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
+ status_result(sys::AParcel_setDataPosition(self.as_native(), pos))
+ }
+}
+
+// Data deserialization methods
+impl Parcel {
+ /// Attempt to read a type that implements [`Deserialize`] from this
+ /// `Parcel`.
+ pub fn read<D: Deserialize>(&self) -> Result<D> {
+ D::deserialize(self)
+ }
+
+ /// Read a vector size from the `Parcel` and resize the given output vector
+ /// to be correctly sized for that amount of data.
+ ///
+ /// This method is used in AIDL-generated server side code for methods that
+ /// take a mutable slice reference parameter.
+ pub fn resize_out_vec<D: Default + Deserialize>(&self, out_vec: &mut Vec<D>) -> Result<()> {
+ let len: i32 = self.read()?;
+
+ if len < 0 {
+ return Err(StatusCode::UNEXPECTED_NULL);
+ }
+
+ // usize in Rust may be 16-bit, so i32 may not fit
+ let len = len.try_into().unwrap();
+ out_vec.resize_with(len, Default::default);
+
+ Ok(())
+ }
+
+ /// Read a vector size from the `Parcel` and either create a correctly sized
+ /// vector for that amount of data or set the output parameter to None if
+ /// the vector should be null.
+ ///
+ /// This method is used in AIDL-generated server side code for methods that
+ /// take a mutable slice reference parameter.
+ pub fn resize_nullable_out_vec<D: Default + Deserialize>(
+ &self,
+ out_vec: &mut Option<Vec<D>>,
+ ) -> Result<()> {
+ let len: i32 = self.read()?;
+
+ if len < 0 {
+ *out_vec = None;
+ } else {
+ // usize in Rust may be 16-bit, so i32 may not fit
+ let len = len.try_into().unwrap();
+ let mut vec = Vec::with_capacity(len);
+ vec.resize_with(len, Default::default);
+ *out_vec = Some(vec);
+ }
+
+ Ok(())
+ }
+}
+
+// Internal APIs
+impl Parcel {
+ pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return
+ // null or a valid pointer to an `AIBinder`, both of which are
+ // valid, safe inputs to `AParcel_writeStrongBinder`.
+ //
+ // This call does not take ownership of the binder. However, it does
+ // require a mutable pointer, which we cannot extract from an
+ // immutable reference, so we clone the binder, incrementing the
+ // refcount before the call. The refcount will be immediately
+ // decremented when this temporary is dropped.
+ status_result(sys::AParcel_writeStrongBinder(
+ self.as_native_mut(),
+ binder.cloned().as_native_mut(),
+ ))
+ }
+ }
+
+ pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> {
+ let mut binder = ptr::null_mut();
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a valid, mutable out pointer to the `binder`
+ // parameter. After this call, `binder` will be either null or a
+ // valid pointer to an `AIBinder` owned by the caller.
+ sys::AParcel_readStrongBinder(self.as_native(), &mut binder)
+ };
+
+ status_result(status)?;
+
+ Ok(unsafe {
+ // Safety: `binder` is either null or a valid, owned pointer at this
+ // point, so can be safely passed to `SpIBinder::from_raw`.
+ SpIBinder::from_raw(binder)
+ })
+ }
+}
+
+impl Drop for Parcel {
+ fn drop(&mut self) {
+ // Run the C++ Parcel complete object destructor
+ if let Self::Owned(ptr) = *self {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If we own the parcel, we can safely delete it
+ // here.
+ sys::AParcel_delete(ptr)
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+impl Parcel {
+ /// Create a new parcel tied to a bogus binder. TESTING ONLY!
+ ///
+ /// This can only be used for testing! All real parcel operations must be
+ /// done in the callback to [`IBinder::transact`] or in
+ /// [`Remotable::on_transact`] using the parcels provided to these methods.
+ pub(crate) fn new_for_test(binder: &mut SpIBinder) -> Result<Self> {
+ let mut input = ptr::null_mut();
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `binder` always contains a
+ // valid pointer to an `AIBinder`. We pass a valid, mutable out
+ // pointer to receive a newly constructed parcel. When successful
+ // this function assigns a new pointer to an `AParcel` to `input`
+ // and transfers ownership of this pointer to the caller. Thus,
+ // after this call, `input` will either be null or point to a valid,
+ // owned `AParcel`.
+ sys::AIBinder_prepareTransaction(binder.as_native_mut(), &mut input)
+ };
+ status_result(status)?;
+ unsafe {
+ // Safety: `input` is either null or a valid, owned pointer to an
+ // `AParcel`, so is valid to safe to
+ // `Parcel::owned`. `Parcel::owned` takes ownership of the parcel
+ // pointer.
+ Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)
+ }
+ }
+}
+
+#[test]
+fn test_read_write() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+ use std::ffi::CString;
+
+ let mut service = Binder::new(()).as_binder();
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ assert_eq!(parcel.read::<bool>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<i8>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<u16>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<i32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<u32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<i64>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<u64>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<f32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<f64>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ assert_eq!(parcel.read::<Option<CString>>(), Ok(None));
+ assert_eq!(parcel.read::<String>(), Err(StatusCode::UNEXPECTED_NULL));
+
+ assert_eq!(parcel.read_binder().err(), Some(StatusCode::BAD_TYPE));
+
+ parcel.write(&1i32).unwrap();
+
+ unsafe {
+ parcel.set_data_position(start).unwrap();
+ }
+
+ let i: i32 = parcel.read().unwrap();
+ assert_eq!(i, 1i32);
+}
+
+#[test]
+#[allow(clippy::float_cmp)]
+fn test_read_data() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+
+ let mut service = Binder::new(()).as_binder();
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let str_start = parcel.get_data_position();
+
+ parcel.write(&b"Hello, Binder!\0"[..]).unwrap();
+ // Skip over string length
+ unsafe {
+ assert!(parcel.set_data_position(str_start).is_ok());
+ }
+ assert_eq!(parcel.read::<i32>().unwrap(), 15);
+ let start = parcel.get_data_position();
+
+ assert_eq!(parcel.read::<bool>().unwrap(), true);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<i8>().unwrap(), 72i8);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u16>().unwrap(), 25928);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<i32>().unwrap(), 1819043144);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 1819043144);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<i64>().unwrap(), 4764857262830019912);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u64>().unwrap(), 4764857262830019912);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(
+ parcel.read::<f32>().unwrap(),
+ 1143139100000000000000000000.0
+ );
+ assert_eq!(parcel.read::<f32>().unwrap(), 40.043392);
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<f64>().unwrap(), 34732488246.197815);
+
+ // Skip back to before the string length
+ unsafe {
+ assert!(parcel.set_data_position(str_start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<Vec<u8>>().unwrap(), b"Hello, Binder!\0");
+}
+
+#[test]
+fn test_utf8_utf16_conversions() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+
+ let mut service = Binder::new(()).as_binder();
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ assert!(parcel.write("Hello, Binder!").is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert_eq!(
+ parcel.read::<Option<String>>().unwrap().unwrap(),
+ "Hello, Binder!"
+ );
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(parcel.write(&["str1", "str2", "str3"][..]).is_ok());
+ assert!(parcel
+ .write(
+ &[
+ String::from("str4"),
+ String::from("str5"),
+ String::from("str6"),
+ ][..]
+ )
+ .is_ok());
+
+ let s1 = "Hello, Binder!";
+ let s2 = "This is a utf8 string.";
+ let s3 = "Some more text here.";
+
+ assert!(parcel.write(&[s1, s2, s3][..]).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(
+ parcel.read::<Vec<String>>().unwrap(),
+ ["str1", "str2", "str3"]
+ );
+ assert_eq!(
+ parcel.read::<Vec<String>>().unwrap(),
+ ["str4", "str5", "str6"]
+ );
+ assert_eq!(parcel.read::<Vec<String>>().unwrap(), [s1, s2, s3]);
+}
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
new file mode 100644
index 0000000..8a89ab0
--- /dev/null
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+use super::{
+ Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray,
+ SerializeOption,
+};
+use crate::binder::AsNative;
+use crate::error::{status_result, Result, StatusCode};
+use crate::sys;
+
+use std::fs::File;
+use std::os::unix::io::{AsRawFd, FromRawFd};
+
+/// Rust version of the Java class android.os.ParcelFileDescriptor
+pub struct ParcelFileDescriptor(File);
+
+impl ParcelFileDescriptor {
+ /// Create a new `ParcelFileDescriptor`
+ pub fn new(file: File) -> Self {
+ Self(file)
+ }
+}
+
+impl AsRef<File> for ParcelFileDescriptor {
+ fn as_ref(&self) -> &File {
+ &self.0
+ }
+}
+
+impl From<ParcelFileDescriptor> for File {
+ fn from(file: ParcelFileDescriptor) -> File {
+ file.0
+ }
+}
+
+impl Serialize for ParcelFileDescriptor {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ let fd = self.0.as_raw_fd();
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a
+ // valid file, so we can borrow a valid file
+ // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take
+ // ownership of the fd, so we need not duplicate it first.
+ sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd)
+ };
+ status_result(status)
+ }
+}
+
+impl SerializeArray for ParcelFileDescriptor {}
+
+impl SerializeOption for ParcelFileDescriptor {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ if let Some(f) = this {
+ f.serialize(parcel)
+ } else {
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the
+ // value `-1` as the file descriptor to signify serializing a
+ // null file descriptor.
+ sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32)
+ };
+ status_result(status)
+ }
+ }
+}
+
+impl SerializeArray for Option<ParcelFileDescriptor> {}
+
+impl DeserializeOption for ParcelFileDescriptor {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ let mut fd = -1i32;
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a valid mutable pointer to an i32, which
+ // `AParcel_readParcelFileDescriptor` assigns the valid file
+ // descriptor into, or `-1` if deserializing a null file
+ // descriptor. The read function passes ownership of the file
+ // descriptor to its caller if it was non-null, so we must take
+ // ownership of the file and ensure that it is eventually closed.
+ status_result(sys::AParcel_readParcelFileDescriptor(
+ parcel.as_native(),
+ &mut fd,
+ ))?;
+ }
+ if fd < 0 {
+ Ok(None)
+ } else {
+ let file = unsafe {
+ // Safety: At this point, we know that the file descriptor was
+ // not -1, so must be a valid, owned file descriptor which we
+ // can safely turn into a `File`.
+ File::from_raw_fd(fd)
+ };
+ Ok(Some(ParcelFileDescriptor::new(file)))
+ }
+ }
+}
+
+impl DeserializeArray for Option<ParcelFileDescriptor> {}
+
+impl Deserialize for ParcelFileDescriptor {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ Deserialize::deserialize(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+ }
+}
+
+impl DeserializeArray for ParcelFileDescriptor {}
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
new file mode 100644
index 0000000..78b3d2c
--- /dev/null
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -0,0 +1,922 @@
+/*
+ * 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.
+ */
+
+use crate::binder::{AsNative, FromIBinder};
+use crate::error::{status_result, Result, Status, StatusCode};
+use crate::parcel::Parcel;
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::convert::TryInto;
+use std::ffi::{c_void, CStr, CString};
+use std::ptr;
+
+/// A struct whose instances can be written to a [`Parcel`].
+// Might be able to hook this up as a serde backend in the future?
+pub trait Serialize {
+ /// Serialize this instance into the given [`Parcel`].
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()>;
+}
+
+/// A struct whose instances can be restored from a [`Parcel`].
+// Might be able to hook this up as a serde backend in the future?
+pub trait Deserialize: Sized {
+ /// Deserialize an instance from the given [`Parcel`].
+ fn deserialize(parcel: &Parcel) -> Result<Self>;
+}
+
+/// Helper trait for types that can be serialized as arrays.
+/// Defaults to calling Serialize::serialize() manually for every element,
+/// but can be overridden for custom implementations like `writeByteArray`.
+// Until specialization is stabilized in Rust, we need this to be a separate
+// trait because it's the only way to have a default implementation for a method.
+// We want the default implementation for most types, but an override for
+// a few special ones like `readByteArray` for `u8`.
+pub trait SerializeArray: Serialize + Sized {
+ /// Serialize an array of this type into the given [`Parcel`].
+ fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ parcel.write_slice_size(Some(slice))?;
+
+ for item in slice {
+ parcel.write(item)?;
+ }
+
+ Ok(())
+ }
+}
+
+/// Helper trait for types that can be deserialized as arrays.
+/// Defaults to calling Deserialize::deserialize() manually for every element,
+/// but can be overridden for custom implementations like `readByteArray`.
+pub trait DeserializeArray: Deserialize {
+ /// Deserialize an array of type from the given [`Parcel`].
+ fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
+ let len: i32 = parcel.read()?;
+ if len < 0 {
+ return Ok(None);
+ }
+
+ // TODO: Assumes that usize is at least 32 bits
+ let mut vec = Vec::with_capacity(len as usize);
+
+ for _ in 0..len {
+ vec.push(parcel.read()?);
+ }
+
+ Ok(Some(vec))
+ }
+}
+
+/// Helper trait for types that can be nullable when serialized.
+// We really need this trait instead of implementing `Serialize for Option<T>`
+// because of the Rust orphan rule which prevents us from doing
+// `impl Serialize for Option<&dyn IFoo>` for AIDL interfaces.
+// Instead we emit `impl SerializeOption for dyn IFoo` which is allowed.
+// We also use it to provide a default implementation for AIDL-generated
+// parcelables.
+pub trait SerializeOption: Serialize {
+ /// Serialize an Option of this type into the given [`Parcel`].
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ if let Some(inner) = this {
+ parcel.write(&1i32)?;
+ parcel.write(inner)
+ } else {
+ parcel.write(&0i32)
+ }
+ }
+}
+
+/// Helper trait for types that can be nullable when deserialized.
+pub trait DeserializeOption: Deserialize {
+ /// Deserialize an Option of this type from the given [`Parcel`].
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ let null: i32 = parcel.read()?;
+ if null == 0 {
+ Ok(None)
+ } else {
+ parcel.read().map(Some)
+ }
+ }
+}
+
+/// Callback to allocate a vector for parcel array read functions.
+///
+/// # Safety
+///
+/// The opaque data pointer passed to the array read function must be a mutable
+/// pointer to an `Option<Vec<T>>`. `buffer` will be assigned a mutable pointer
+/// to the allocated vector data if this function returns true.
+unsafe extern "C" fn allocate_vec<T: Clone + Default>(
+ data: *mut c_void,
+ len: i32,
+ buffer: *mut *mut T,
+) -> bool {
+ let vec = &mut *(data as *mut Option<Vec<T>>);
+ if len < 0 {
+ *vec = None;
+ return true;
+ }
+ let mut new_vec: Vec<T> = Vec::with_capacity(len as usize);
+ new_vec.resize_with(len as usize, Default::default);
+ *buffer = new_vec.as_mut_ptr();
+ *vec = Some(new_vec);
+ true
+}
+
+macro_rules! parcelable_primitives {
+ {
+ $(
+ impl $trait:ident for $ty:ty = $fn:path;
+ )*
+ } => {
+ $(impl_parcelable!{$trait, $ty, $fn})*
+ };
+}
+
+macro_rules! impl_parcelable {
+ {Serialize, $ty:ty, $write_fn:path} => {
+ impl Serialize for $ty {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`, and any `$ty` literal value is safe to pass to
+ // `$write_fn`.
+ status_result($write_fn(parcel.as_native_mut(), *self))
+ }
+ }
+ }
+ };
+
+ {Deserialize, $ty:ty, $read_fn:path} => {
+ impl Deserialize for $ty {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ let mut val = Self::default();
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a valid, mutable pointer to `val`, a
+ // literal of type `$ty`, and `$read_fn` will write the
+ // value read into `val` if successful
+ status_result($read_fn(parcel.as_native(), &mut val))?
+ };
+ Ok(val)
+ }
+ }
+ };
+
+ {SerializeArray, $ty:ty, $write_array_fn:path} => {
+ impl SerializeArray for $ty {
+ fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the slice is > 0 length, `slice.as_ptr()`
+ // will be a valid pointer to an array of elements of type
+ // `$ty`. If the slice length is 0, `slice.as_ptr()` may be
+ // dangling, but this is safe since the pointer is not
+ // dereferenced if the length parameter is 0.
+ $write_array_fn(
+ parcel.as_native_mut(),
+ slice.as_ptr(),
+ slice
+ .len()
+ .try_into()
+ .or(Err(StatusCode::BAD_VALUE))?,
+ )
+ };
+ status_result(status)
+ }
+ }
+ };
+
+ {DeserializeArray, $ty:ty, $read_array_fn:path} => {
+ impl DeserializeArray for $ty {
+ fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
+ let mut vec: Option<Vec<Self>> = None;
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `allocate_vec<T>` expects the opaque pointer to
+ // be of type `*mut Option<Vec<T>>`, so `&mut vec` is
+ // correct for it.
+ $read_array_fn(
+ parcel.as_native(),
+ &mut vec as *mut _ as *mut c_void,
+ Some(allocate_vec),
+ )
+ };
+ status_result(status)?;
+ Ok(vec)
+ }
+ }
+ };
+}
+
+parcelable_primitives! {
+ impl Serialize for bool = sys::AParcel_writeBool;
+ impl Deserialize for bool = sys::AParcel_readBool;
+
+ // This is only safe because `Option<Vec<u8>>` is interchangeable with
+ // `Option<Vec<i8>>` (what the allocator function actually allocates.
+ impl DeserializeArray for u8 = sys::AParcel_readByteArray;
+
+ impl Serialize for i8 = sys::AParcel_writeByte;
+ impl Deserialize for i8 = sys::AParcel_readByte;
+ impl SerializeArray for i8 = sys::AParcel_writeByteArray;
+ impl DeserializeArray for i8 = sys::AParcel_readByteArray;
+
+ impl Serialize for u16 = sys::AParcel_writeChar;
+ impl Deserialize for u16 = sys::AParcel_readChar;
+ impl SerializeArray for u16 = sys::AParcel_writeCharArray;
+ impl DeserializeArray for u16 = sys::AParcel_readCharArray;
+
+ // This is only safe because `Option<Vec<i16>>` is interchangeable with
+ // `Option<Vec<u16>>` (what the allocator function actually allocates.
+ impl DeserializeArray for i16 = sys::AParcel_readCharArray;
+
+ impl Serialize for u32 = sys::AParcel_writeUint32;
+ impl Deserialize for u32 = sys::AParcel_readUint32;
+ impl SerializeArray for u32 = sys::AParcel_writeUint32Array;
+ impl DeserializeArray for u32 = sys::AParcel_readUint32Array;
+
+ impl Serialize for i32 = sys::AParcel_writeInt32;
+ impl Deserialize for i32 = sys::AParcel_readInt32;
+ impl SerializeArray for i32 = sys::AParcel_writeInt32Array;
+ impl DeserializeArray for i32 = sys::AParcel_readInt32Array;
+
+ impl Serialize for u64 = sys::AParcel_writeUint64;
+ impl Deserialize for u64 = sys::AParcel_readUint64;
+ impl SerializeArray for u64 = sys::AParcel_writeUint64Array;
+ impl DeserializeArray for u64 = sys::AParcel_readUint64Array;
+
+ impl Serialize for i64 = sys::AParcel_writeInt64;
+ impl Deserialize for i64 = sys::AParcel_readInt64;
+ impl SerializeArray for i64 = sys::AParcel_writeInt64Array;
+ impl DeserializeArray for i64 = sys::AParcel_readInt64Array;
+
+ impl Serialize for f32 = sys::AParcel_writeFloat;
+ impl Deserialize for f32 = sys::AParcel_readFloat;
+ impl SerializeArray for f32 = sys::AParcel_writeFloatArray;
+ impl DeserializeArray for f32 = sys::AParcel_readFloatArray;
+
+ impl Serialize for f64 = sys::AParcel_writeDouble;
+ impl Deserialize for f64 = sys::AParcel_readDouble;
+ impl SerializeArray for f64 = sys::AParcel_writeDoubleArray;
+ impl DeserializeArray for f64 = sys::AParcel_readDoubleArray;
+}
+
+impl SerializeArray for bool {}
+impl DeserializeArray for bool {}
+
+impl Serialize for u8 {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ (*self as i8).serialize(parcel)
+ }
+}
+
+impl Deserialize for u8 {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ i8::deserialize(parcel).map(|v| v as u8)
+ }
+}
+
+impl SerializeArray for u8 {
+ fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
+ // valid pointer to an array of elements of type `$ty`. If the slice
+ // length is 0, `slice.as_ptr()` may be dangling, but this is safe
+ // since the pointer is not dereferenced if the length parameter is
+ // 0.
+ sys::AParcel_writeByteArray(
+ parcel.as_native_mut(),
+ slice.as_ptr() as *const i8,
+ slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?,
+ )
+ };
+ status_result(status)
+ }
+}
+
+impl Serialize for i16 {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ (*self as u16).serialize(parcel)
+ }
+}
+
+impl Deserialize for i16 {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ u16::deserialize(parcel).map(|v| v as i16)
+ }
+}
+
+impl SerializeArray for i16 {
+ fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
+ // valid pointer to an array of elements of type `$ty`. If the slice
+ // length is 0, `slice.as_ptr()` may be dangling, but this is safe
+ // since the pointer is not dereferenced if the length parameter is
+ // 0.
+ sys::AParcel_writeCharArray(
+ parcel.as_native_mut(),
+ slice.as_ptr() as *const u16,
+ slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?,
+ )
+ };
+ status_result(status)
+ }
+}
+
+impl SerializeOption for CStr {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ match this {
+ None => unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. If the string pointer is null,
+ // `AParcel_writeString` requires that the length is -1 to
+ // indicate that we want to serialize a null string.
+ status_result(sys::AParcel_writeString(
+ parcel.as_native_mut(),
+ ptr::null(),
+ -1,
+ ))
+ },
+ Some(s) => unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. `AParcel_writeString` assumes that we pass a
+ // null-terminated C string pointer with no nulls in the middle
+ // of the string. Rust guarantees exactly that for a valid CStr
+ // instance.
+ status_result(sys::AParcel_writeString(
+ parcel.as_native_mut(),
+ s.as_ptr(),
+ s.to_bytes()
+ .len()
+ .try_into()
+ .or(Err(StatusCode::BAD_VALUE))?,
+ ))
+ },
+ }
+ }
+}
+
+impl SerializeArray for Option<&CStr> {}
+
+impl Serialize for CStr {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Some(self).serialize(parcel)
+ }
+}
+
+impl Serialize for CString {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Some(self.as_c_str()).serialize(parcel)
+ }
+}
+
+impl SerializeArray for CString {}
+
+impl SerializeOption for CString {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.map(CString::as_c_str), parcel)
+ }
+}
+
+impl SerializeArray for Option<CString> {}
+
+impl Serialize for String {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Some(self.as_str()).serialize(parcel)
+ }
+}
+
+impl SerializeArray for String {}
+
+impl SerializeOption for String {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.map(String::as_str), parcel)
+ }
+}
+
+impl SerializeArray for Option<String> {}
+
+impl Deserialize for Option<CString> {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ let mut vec: Option<Vec<u8>> = None;
+ let status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an `AParcel`.
+ // `Option<Vec<u8>>` is equivalent to the expected `Option<Vec<i8>>`
+ // for `allocate_vec`, so `vec` is safe to pass as the opaque data
+ // pointer on platforms where char is signed.
+ sys::AParcel_readString(
+ parcel.as_native(),
+ &mut vec as *mut _ as *mut c_void,
+ Some(allocate_vec),
+ )
+ };
+
+ status_result(status)?;
+ vec.map(|mut s| {
+ // The vector includes a null-terminator and CString::new requires
+ // no nulls, including terminating.
+ s.pop();
+ CString::new(s).or(Err(StatusCode::BAD_VALUE))
+ })
+ .transpose()
+ }
+}
+
+impl DeserializeArray for Option<CString> {}
+
+impl DeserializeOption for String {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ let c_str = <Option<CString>>::deserialize(parcel)?;
+ c_str
+ .map(|s| s.into_string().or(Err(StatusCode::BAD_VALUE)))
+ .transpose()
+ }
+}
+
+impl DeserializeArray for Option<String> {}
+
+impl Deserialize for String {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ Deserialize::deserialize(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+ }
+}
+
+impl DeserializeArray for String {}
+
+impl SerializeOption for str {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ match this {
+ None => parcel.write(&-1i32),
+ Some(s) => {
+ let c_str = CString::new(s).or(Err(StatusCode::BAD_VALUE))?;
+ parcel.write(&c_str)
+ }
+ }
+ }
+}
+
+impl Serialize for str {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Some(self).serialize(parcel)
+ }
+}
+
+impl SerializeArray for &str {}
+
+impl SerializeArray for Option<&str> {}
+
+impl<T: SerializeArray> Serialize for [T] {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ SerializeArray::serialize_array(self, parcel)
+ }
+}
+
+impl<T: SerializeArray> Serialize for Vec<T> {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ SerializeArray::serialize_array(&self[..], parcel)
+ }
+}
+
+impl<T: SerializeArray> SerializeOption for [T] {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ if let Some(v) = this {
+ SerializeArray::serialize_array(v, parcel)
+ } else {
+ parcel.write(&-1i32)
+ }
+ }
+}
+
+impl<T: SerializeArray> SerializeOption for Vec<T> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.map(Vec::as_slice), parcel)
+ }
+}
+
+impl<T: DeserializeArray> Deserialize for Vec<T> {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ DeserializeArray::deserialize_array(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+ }
+}
+
+impl<T: DeserializeArray> DeserializeOption for Vec<T> {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ DeserializeArray::deserialize_array(parcel)
+ }
+}
+
+impl Serialize for Status {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an `AParcel`
+ // and `Status` always contains a valid pointer to an `AStatus`, so
+ // both parameters are valid and safe. This call does not take
+ // ownership of either of its parameters.
+ status_result(sys::AParcel_writeStatusHeader(
+ parcel.as_native_mut(),
+ self.as_native(),
+ ))
+ }
+ }
+}
+
+impl Deserialize for Status {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ let mut status_ptr = ptr::null_mut();
+ let ret_status = unsafe {
+ // Safety: `Parcel` always contains a valid pointer to an
+ // `AParcel`. We pass a mutable out pointer which will be
+ // assigned a valid `AStatus` pointer if the function returns
+ // status OK. This function passes ownership of the status
+ // pointer to the caller, if it was assigned.
+ sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr)
+ };
+ status_result(ret_status)?;
+ Ok(unsafe {
+ // Safety: At this point, the return status of the read call was ok,
+ // so we know that `status_ptr` is a valid, owned pointer to an
+ // `AStatus`, from which we can safely construct a `Status` object.
+ Status::from_ptr(status_ptr)
+ })
+ }
+}
+
+impl<T: Serialize + ?Sized> Serialize for Box<T> {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Serialize::serialize(&**self, parcel)
+ }
+}
+
+impl<T: SerializeOption + ?Sized> SerializeOption for Box<T> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.map(|b| &**b), parcel)
+ }
+}
+
+impl<T: FromIBinder + ?Sized> Deserialize for Box<T> {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ let ibinder: SpIBinder = parcel.read()?;
+ FromIBinder::try_from(ibinder)
+ }
+}
+
+impl<T: FromIBinder + ?Sized> DeserializeOption for Box<T> {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ let ibinder: Option<SpIBinder> = parcel.read()?;
+ ibinder.map(FromIBinder::try_from).transpose()
+ }
+}
+
+// We need these to support Option<&T> for all T
+impl<T: Serialize + ?Sized> Serialize for &T {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ Serialize::serialize(*self, parcel)
+ }
+}
+
+impl<T: SerializeOption + ?Sized> SerializeOption for &T {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(this.copied(), parcel)
+ }
+}
+
+impl<T: SerializeOption> Serialize for Option<T> {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ SerializeOption::serialize_option(self.as_ref(), parcel)
+ }
+}
+
+impl<T: DeserializeOption> Deserialize for Option<T> {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ DeserializeOption::deserialize_option(parcel)
+ }
+}
+
+#[test]
+fn test_custom_parcelable() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+ let mut service = Binder::new(()).as_binder();
+
+ struct Custom(u32, bool, String, Vec<String>);
+
+ impl Serialize for Custom {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ self.0.serialize(parcel)?;
+ self.1.serialize(parcel)?;
+ self.2.serialize(parcel)?;
+ self.3.serialize(parcel)
+ }
+ }
+
+ impl Deserialize for Custom {
+ fn deserialize(parcel: &Parcel) -> Result<Self> {
+ Ok(Custom(
+ parcel.read()?,
+ parcel.read()?,
+ parcel.read()?,
+ parcel.read::<Option<Vec<String>>>()?.unwrap(),
+ ))
+ }
+ }
+
+ let string8 = "Custom Parcelable".to_string();
+
+ let s1 = "str1".to_string();
+ let s2 = "str2".to_string();
+ let s3 = "str3".to_string();
+
+ let strs = vec![s1, s2, s3];
+
+ let custom = Custom(123_456_789, true, string8, strs);
+
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ assert!(custom.serialize(&mut parcel).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let custom2 = Custom::deserialize(&parcel).unwrap();
+
+ assert_eq!(custom2.0, 123_456_789);
+ assert!(custom2.1);
+ assert_eq!(custom2.2, custom.2);
+ assert_eq!(custom2.3, custom.3);
+}
+
+#[test]
+#[allow(clippy::excessive_precision)]
+fn test_slice_parcelables() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+ let mut service = Binder::new(()).as_binder();
+
+ let bools = [true, false, false, true];
+
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ assert!(bools.serialize(&mut parcel).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4);
+ assert_eq!(parcel.read::<u32>().unwrap(), 1);
+ assert_eq!(parcel.read::<u32>().unwrap(), 0);
+ assert_eq!(parcel.read::<u32>().unwrap(), 0);
+ assert_eq!(parcel.read::<u32>().unwrap(), 1);
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<bool>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [true, false, false, true]);
+
+ let u8s = [101u8, 255, 42, 117];
+
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ assert!(parcel.write(&u8s[..]).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u8>::deserialize(&parcel).unwrap();
+ assert_eq!(vec, [101, 255, 42, 117]);
+
+ let i8s = [-128i8, 127, 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert!(parcel.write(&i8s[..]).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u8>::deserialize(&parcel).unwrap();
+ assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]);
+
+ let u16s = [u16::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u16s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u16>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]);
+
+ let i16s = [i16::max_value(), i16::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i16s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i16>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]);
+
+ let u32s = [u32::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u32s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u32>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]);
+
+ let i32s = [i32::max_value(), i32::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i32s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i32>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]);
+
+ let u64s = [u64::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u64s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u64>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]);
+
+ let i64s = [i64::max_value(), i64::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i64s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i64>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]);
+
+ let f32s = [
+ std::f32::NAN,
+ std::f32::INFINITY,
+ 1.23456789,
+ std::f32::EPSILON,
+ ];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(f32s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<f32>::deserialize(&parcel).unwrap();
+
+ // NAN != NAN so we can't use it in the assert_eq:
+ assert!(vec[0].is_nan());
+ assert_eq!(vec[1..], f32s[1..]);
+
+ let f64s = [
+ std::f64::NAN,
+ std::f64::INFINITY,
+ 1.234567890123456789,
+ std::f64::EPSILON,
+ ];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(f64s.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<f64>::deserialize(&parcel).unwrap();
+
+ // NAN != NAN so we can't use it in the assert_eq:
+ assert!(vec[0].is_nan());
+ assert_eq!(vec[1..], f64s[1..]);
+
+ let s1 = "Hello, Binder!";
+ let s2 = "This is a utf8 string.";
+ let s3 = "Some more text here.";
+
+ let strs = [s1, s2, s3];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(strs.serialize(&mut parcel).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<String>::deserialize(&parcel).unwrap();
+
+ assert_eq!(vec, strs);
+}
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
new file mode 100644
index 0000000..f9519b4
--- /dev/null
+++ b/libs/binder/rust/src/proxy.rs
@@ -0,0 +1,512 @@
+/*
+ * 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.
+ */
+
+//! Rust API for interacting with a remote binder service.
+
+use crate::binder::{
+ AsNative, FromIBinder, IBinder, Interface, InterfaceClass, TransactionCode, TransactionFlags,
+};
+use crate::error::{status_result, Result, StatusCode};
+use crate::parcel::{
+ Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray,
+ SerializeOption,
+};
+use crate::sys;
+
+use std::convert::TryInto;
+use std::ffi::{c_void, CString};
+use std::os::unix::io::AsRawFd;
+use std::ptr;
+
+/// A strong reference to a Binder remote object.
+///
+/// This struct encapsulates the generic C++ `sp<IBinder>` class. This wrapper
+/// is untyped; typed interface access is implemented by the AIDL compiler.
+pub struct SpIBinder(*mut sys::AIBinder);
+
+/// # Safety
+///
+/// An `SpIBinder` is a handle to a C++ IBinder, which is thread-safe
+unsafe impl Send for SpIBinder {}
+
+impl SpIBinder {
+ /// Create an `SpIBinder` wrapper object from a raw `AIBinder` pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe iff `ptr` is a null pointer or a valid pointer
+ /// to an `AIBinder`.
+ ///
+ /// In the non-null case, this method conceptually takes ownership of a strong
+ /// reference to the object, so `AIBinder_incStrong` must have been called
+ /// on the pointer before passing it to this constructor. This is generally
+ /// done by Binder NDK methods that return an `AIBinder`, but care should be
+ /// taken to ensure this invariant.
+ ///
+ /// All `SpIBinder` objects that are constructed will hold a valid pointer
+ /// to an `AIBinder`, which will remain valid for the entire lifetime of the
+ /// `SpIBinder` (we keep a strong reference, and only decrement on drop).
+ pub(crate) unsafe fn from_raw(ptr: *mut sys::AIBinder) -> Option<Self> {
+ ptr.as_mut().map(|p| Self(p))
+ }
+
+ /// Return true if this binder object is hosted in a different process than
+ /// the current one.
+ pub fn is_remote(&self) -> bool {
+ unsafe {
+ // Safety: `SpIBinder` guarantees that it always contains a valid
+ // `AIBinder` pointer.
+ sys::AIBinder_isRemote(self.as_native())
+ }
+ }
+
+ /// Try to convert this Binder object into a trait object for the given
+ /// Binder interface.
+ ///
+ /// If this object does not implement the expected interface, the error
+ /// `StatusCode::BAD_TYPE` is returned.
+ pub fn into_interface<I: FromIBinder + ?Sized>(self) -> Result<Box<I>> {
+ FromIBinder::try_from(self)
+ }
+
+ /// Return the interface class of this binder object, if associated with
+ /// one.
+ pub(crate) fn get_class(&mut self) -> Option<InterfaceClass> {
+ unsafe {
+ // Safety: `SpIBinder` guarantees that it always contains a valid
+ // `AIBinder` pointer. `AIBinder_getClass` returns either a null
+ // pointer or a valid pointer to an `AIBinder_Class`. After mapping
+ // null to None, we can safely construct an `InterfaceClass` if the
+ // pointer was non-null.
+ let class = sys::AIBinder_getClass(self.as_native_mut());
+ class.as_ref().map(|p| InterfaceClass::from_ptr(p))
+ }
+ }
+}
+
+/// An object that can be associate with an [`InterfaceClass`].
+pub trait AssociateClass {
+ /// Check if this object is a valid object for the given interface class
+ /// `I`.
+ ///
+ /// Returns `Some(self)` if this is a valid instance of the interface, and
+ /// `None` otherwise.
+ ///
+ /// Classes constructed by `InterfaceClass` are unique per type, so
+ /// repeatedly calling this method for the same `InterfaceClass` is allowed.
+ fn associate_class(&mut self, class: InterfaceClass) -> bool;
+}
+
+impl AssociateClass for SpIBinder {
+ fn associate_class(&mut self, class: InterfaceClass) -> bool {
+ unsafe {
+ // Safety: `SpIBinder` guarantees that it always contains a valid
+ // `AIBinder` pointer. An `InterfaceClass` can always be converted
+ // into a valid `AIBinder_Class` pointer, so these parameters are
+ // always safe.
+ sys::AIBinder_associateClass(self.as_native_mut(), class.into())
+ }
+ }
+}
+
+impl Clone for SpIBinder {
+ fn clone(&self) -> Self {
+ unsafe {
+ // Safety: Cloning a strong reference must increment the reference
+ // count. We are guaranteed by the `SpIBinder` constructor
+ // invariants that `self.0` is always a valid `AIBinder` pointer.
+ sys::AIBinder_incStrong(self.0);
+ }
+ Self(self.0)
+ }
+}
+
+impl Drop for SpIBinder {
+ // We hold a strong reference to the IBinder in SpIBinder and need to give up
+ // this reference on drop.
+ fn drop(&mut self) {
+ unsafe {
+ // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we
+ // know this pointer is safe to pass to `AIBinder_decStrong` here.
+ sys::AIBinder_decStrong(self.as_native_mut());
+ }
+ }
+}
+
+impl<T: AsNative<sys::AIBinder>> IBinder for T {
+ /// Perform a binder transaction
+ fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
+ &self,
+ code: TransactionCode,
+ flags: TransactionFlags,
+ input_callback: F,
+ ) -> Result<Parcel> {
+ let mut input = ptr::null_mut();
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. It is safe to cast from an
+ // immutable pointer to a mutable pointer here, because
+ // `AIBinder_prepareTransaction` only calls immutable `AIBinder`
+ // methods but the parameter is unfortunately not marked as const.
+ //
+ // After the call, input will be either a valid, owned `AParcel`
+ // pointer, or null.
+ sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input)
+ };
+ status_result(status)?;
+ let mut input = unsafe {
+ // Safety: At this point, `input` is either a valid, owned `AParcel`
+ // pointer, or null. `Parcel::owned` safely handles both cases,
+ // taking ownership of the parcel.
+ Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)?
+ };
+ input_callback(&mut input)?;
+ let mut reply = ptr::null_mut();
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. Although `IBinder::transact` is
+ // not a const method, it is still safe to cast our immutable
+ // pointer to mutable for the call. First, `IBinder::transact` is
+ // thread-safe, so concurrency is not an issue. The only way that
+ // `transact` can affect any visible, mutable state in the current
+ // process is by calling `onTransact` for a local service. However,
+ // in order for transactions to be thread-safe, this method must
+ // dynamically lock its data before modifying it. We enforce this
+ // property in Rust by requiring `Sync` for remotable objects and
+ // only providing `on_transact` with an immutable reference to
+ // `self`.
+ //
+ // This call takes ownership of the `input` parcel pointer, and
+ // passes ownership of the `reply` out parameter to its caller. It
+ // does not affect ownership of the `binder` parameter.
+ sys::AIBinder_transact(
+ self.as_native() as *mut sys::AIBinder,
+ code,
+ &mut input.into_raw(),
+ &mut reply,
+ flags,
+ )
+ };
+ status_result(status)?;
+
+ unsafe {
+ // Safety: `reply` is either a valid `AParcel` pointer or null
+ // after the call to `AIBinder_transact` above, so we can
+ // construct a `Parcel` out of it. `AIBinder_transact` passes
+ // ownership of the `reply` parcel to Rust, so we need to
+ // construct an owned variant. `Parcel::owned` takes ownership
+ // of the parcel pointer.
+ Parcel::owned(reply).ok_or(StatusCode::UNEXPECTED_NULL)
+ }
+ }
+
+ fn is_binder_alive(&self) -> bool {
+ unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`.
+ //
+ // This call does not affect ownership of its pointer parameter.
+ sys::AIBinder_isAlive(self.as_native())
+ }
+ }
+
+ fn ping_binder(&mut self) -> Result<()> {
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`.
+ //
+ // This call does not affect ownership of its pointer parameter.
+ sys::AIBinder_ping(self.as_native_mut())
+ };
+ status_result(status)
+ }
+
+ fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> {
+ let args: Vec<_> = args.iter().map(|a| CString::new(*a).unwrap()).collect();
+ let mut arg_ptrs: Vec<_> = args.iter().map(|a| a.as_ptr()).collect();
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the
+ // file descriptor parameter is always be a valid open file. The
+ // `args` pointer parameter is a valid pointer to an array of C
+ // strings that will outlive the call since `args` lives for the
+ // whole function scope.
+ //
+ // This call does not affect ownership of its binder pointer
+ // parameter and does not take ownership of the file or args array
+ // parameters.
+ sys::AIBinder_dump(
+ self.as_native_mut(),
+ fp.as_raw_fd(),
+ arg_ptrs.as_mut_ptr(),
+ arg_ptrs.len().try_into().unwrap(),
+ )
+ };
+ status_result(status)
+ }
+
+ fn get_extension(&mut self) -> Result<Option<SpIBinder>> {
+ let mut out = ptr::null_mut();
+ let status = unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. After this call, the `out`
+ // parameter will be either null, or a valid pointer to an
+ // `AIBinder`.
+ //
+ // This call passes ownership of the out pointer to its caller
+ // (assuming it is set to a non-null value).
+ sys::AIBinder_getExtension(self.as_native_mut(), &mut out)
+ };
+ let ibinder = unsafe {
+ // Safety: The call above guarantees that `out` is either null or a
+ // valid, owned pointer to an `AIBinder`, both of which are safe to
+ // pass to `SpIBinder::from_raw`.
+ SpIBinder::from_raw(out)
+ };
+
+ status_result(status)?;
+ Ok(ibinder)
+ }
+
+ fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
+ status_result(unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. `recipient` can always be
+ // converted into a valid pointer to an
+ // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+ // cookie, although we depend on this value being set by
+ // `get_cookie` when the death recipient callback is called.
+ sys::AIBinder_linkToDeath(
+ self.as_native_mut(),
+ recipient.as_native_mut(),
+ recipient.get_cookie(),
+ )
+ })
+ }
+
+ fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
+ status_result(unsafe {
+ // Safety: `SpIBinder` guarantees that `self` always contains a
+ // valid pointer to an `AIBinder`. `recipient` can always be
+ // converted into a valid pointer to an
+ // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+ // cookie, although we depend on this value being set by
+ // `get_cookie` when the death recipient callback is called.
+ sys::AIBinder_unlinkToDeath(
+ self.as_native_mut(),
+ recipient.as_native_mut(),
+ recipient.get_cookie(),
+ )
+ })
+ }
+}
+
+impl Serialize for SpIBinder {
+ fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ parcel.write_binder(Some(self))
+ }
+}
+
+impl SerializeOption for SpIBinder {
+ fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ parcel.write_binder(this)
+ }
+}
+
+impl SerializeArray for SpIBinder {}
+impl SerializeArray for Option<&SpIBinder> {}
+
+impl Deserialize for SpIBinder {
+ fn deserialize(parcel: &Parcel) -> Result<SpIBinder> {
+ parcel.read_binder().transpose().unwrap()
+ }
+}
+
+impl DeserializeOption for SpIBinder {
+ fn deserialize_option(parcel: &Parcel) -> Result<Option<SpIBinder>> {
+ parcel.read_binder()
+ }
+}
+
+impl DeserializeArray for SpIBinder {}
+impl DeserializeArray for Option<SpIBinder> {}
+
+/// A weak reference to a Binder remote object.
+///
+/// This struct encapsulates the C++ `wp<IBinder>` class. However, this wrapper
+/// is untyped, so properly typed versions implementing a particular binder
+/// interface should be crated with [`declare_binder_interface!`].
+pub struct WpIBinder(*mut sys::AIBinder_Weak);
+
+impl WpIBinder {
+ /// Create a new weak reference from an object that can be converted into a
+ /// raw `AIBinder` pointer.
+ pub fn new<B: AsNative<sys::AIBinder>>(binder: &mut B) -> WpIBinder {
+ let ptr = unsafe {
+ // Safety: `SpIBinder` guarantees that `binder` always contains a
+ // valid pointer to an `AIBinder`.
+ sys::AIBinder_Weak_new(binder.as_native_mut())
+ };
+ assert!(!ptr.is_null());
+ Self(ptr)
+ }
+}
+
+/// Rust wrapper around DeathRecipient objects.
+#[repr(C)]
+pub struct DeathRecipient {
+ recipient: *mut sys::AIBinder_DeathRecipient,
+ callback: Box<dyn Fn() + Send + 'static>,
+}
+
+impl DeathRecipient {
+ /// Create a new death recipient that will call the given callback when its
+ /// associated object dies.
+ pub fn new<F>(callback: F) -> DeathRecipient
+ where
+ F: Fn() + Send + 'static,
+ {
+ let callback = Box::new(callback);
+ let recipient = unsafe {
+ // Safety: The function pointer is a valid death recipient callback.
+ //
+ // This call returns an owned `AIBinder_DeathRecipient` pointer
+ // which must be destroyed via `AIBinder_DeathRecipient_delete` when
+ // no longer needed.
+ sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>))
+ };
+ DeathRecipient {
+ recipient,
+ callback,
+ }
+ }
+
+ /// Get the opaque cookie that identifies this death recipient.
+ ///
+ /// This cookie will be used to link and unlink this death recipient to a
+ /// binder object and will be passed to the `binder_died` callback as an
+ /// opaque userdata pointer.
+ fn get_cookie(&self) -> *mut c_void {
+ &*self.callback as *const _ as *mut c_void
+ }
+
+ /// Callback invoked from C++ when the binder object dies.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must have been created with the `get_cookie`
+ /// method of this object.
+ unsafe extern "C" fn binder_died<F>(cookie: *mut c_void)
+ where
+ F: Fn() + Send + 'static,
+ {
+ let callback = (cookie as *mut F).as_ref().unwrap();
+ callback();
+ }
+}
+
+/// # Safety
+///
+/// A `DeathRecipient` is always constructed with a valid raw pointer to an
+/// `AIBinder_DeathRecipient`, so it is always type-safe to extract this
+/// pointer.
+unsafe impl AsNative<sys::AIBinder_DeathRecipient> for DeathRecipient {
+ fn as_native(&self) -> *const sys::AIBinder_DeathRecipient {
+ self.recipient
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AIBinder_DeathRecipient {
+ self.recipient
+ }
+}
+
+impl Drop for DeathRecipient {
+ fn drop(&mut self) {
+ unsafe {
+ // Safety: `self.recipient` is always a valid, owned
+ // `AIBinder_DeathRecipient` pointer returned by
+ // `AIBinder_DeathRecipient_new` when `self` was created. This
+ // delete method can only be called once when `self` is dropped.
+ sys::AIBinder_DeathRecipient_delete(self.recipient);
+ }
+ }
+}
+
+/// Generic interface to remote binder objects.
+///
+/// Corresponds to the C++ `BpInterface` class.
+pub trait Proxy: Sized + Interface {
+ /// The Binder interface descriptor string.
+ ///
+ /// This string is a unique identifier for a Binder interface, and should be
+ /// the same between all implementations of that interface.
+ fn get_descriptor() -> &'static str;
+
+ /// Create a new interface from the given proxy, if it matches the expected
+ /// type of this interface.
+ fn from_binder(binder: SpIBinder) -> Result<Self>;
+}
+
+/// # Safety
+///
+/// This is a convenience method that wraps `AsNative` for `SpIBinder` to allow
+/// invocation of `IBinder` methods directly from `Interface` objects. It shares
+/// the same safety as the implementation for `SpIBinder`.
+unsafe impl<T: Proxy> AsNative<sys::AIBinder> for T {
+ fn as_native(&self) -> *const sys::AIBinder {
+ self.as_binder().as_native()
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AIBinder {
+ self.as_binder().as_native_mut()
+ }
+}
+
+/// Retrieve an existing service, blocking for a few seconds if it doesn't yet
+/// exist.
+pub fn get_service(name: &str) -> Option<SpIBinder> {
+ let name = CString::new(name).ok()?;
+ unsafe {
+ // Safety: `AServiceManager_getService` returns either a null pointer or
+ // a valid pointer to an owned `AIBinder`. Either of these values is
+ // safe to pass to `SpIBinder::from_raw`.
+ SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr()))
+ }
+}
+
+/// Retrieve an existing service for a particular interface, blocking for a few
+/// seconds if it doesn't yet exist.
+pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Box<T>> {
+ let service = get_service(name);
+ match service {
+ Some(service) => FromIBinder::try_from(service),
+ None => Err(StatusCode::NAME_NOT_FOUND),
+ }
+}
+
+/// # Safety
+///
+/// `SpIBinder` guarantees that `binder` always contains a valid pointer to an
+/// `AIBinder`, so we can trivially extract this pointer here.
+unsafe impl AsNative<sys::AIBinder> for SpIBinder {
+ fn as_native(&self) -> *const sys::AIBinder {
+ self.0
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AIBinder {
+ self.0
+ }
+}
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
new file mode 100644
index 0000000..992f074
--- /dev/null
+++ b/libs/binder/rust/src/state.rs
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::sys;
+
+use libc::{pid_t, uid_t};
+
+/// Static utility functions to manage Binder process state.
+pub struct ProcessState;
+
+impl ProcessState {
+ /// Start the Binder IPC thread pool
+ pub fn start_thread_pool() {
+ unsafe {
+ // Safety: Safe FFI
+ sys::ABinderProcess_startThreadPool();
+ }
+ }
+
+ /// Set the maximum number of threads that can be started in the threadpool.
+ ///
+ /// By default, after startThreadPool is called, this is 15. If it is called
+ /// additional times, it will only prevent the kernel from starting new
+ /// threads and will not delete already existing threads.
+ pub fn set_thread_pool_max_thread_count(num_threads: u32) {
+ unsafe {
+ // Safety: Safe FFI
+ sys::ABinderProcess_setThreadPoolMaxThreadCount(num_threads);
+ }
+ }
+
+ /// Block on the Binder IPC thread pool
+ pub fn join_thread_pool() {
+ unsafe {
+ // Safety: Safe FFI
+ sys::ABinderProcess_joinThreadPool();
+ }
+ }
+}
+
+/// Static utility functions to manage Binder thread state.
+pub struct ThreadState;
+
+impl ThreadState {
+ /// This returns the calling UID assuming that this thread is called from a
+ /// thread that is processing a binder transaction (for instance, in the
+ /// implementation of
+ /// [`Remotable::on_transact`](crate::Remotable::on_transact)).
+ ///
+ /// This can be used with higher-level system services to determine the
+ /// caller's identity and check permissions.
+ ///
+ /// Available since API level 29.
+ ///
+ /// \return calling uid or the current process's UID if this thread isn't
+ /// processing a transaction.
+ pub fn get_calling_uid() -> uid_t {
+ unsafe {
+ // Safety: Safe FFI
+ sys::AIBinder_getCallingUid()
+ }
+ }
+
+ /// This returns the calling PID assuming that this thread is called from a
+ /// thread that is processing a binder transaction (for instance, in the
+ /// implementation of
+ /// [`Remotable::on_transact`](crate::Remotable::on_transact)).
+ ///
+ /// This can be used with higher-level system services to determine the
+ /// caller's identity and check permissions. However, when doing this, one
+ /// should be aware of possible TOCTOU problems when the calling process
+ /// dies and is replaced with another process with elevated permissions and
+ /// the same PID.
+ ///
+ /// Available since API level 29.
+ ///
+ /// \return calling pid or the current process's PID if this thread isn't
+ /// processing a transaction.
+ ///
+ /// If the transaction being processed is a oneway transaction, then this
+ /// method will return 0.
+ pub fn get_calling_pid() -> pid_t {
+ unsafe {
+ // Safety: Safe FFI
+ sys::AIBinder_getCallingPid()
+ }
+ }
+}
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
new file mode 100644
index 0000000..622604f
--- /dev/null
+++ b/libs/binder/rust/tests/Android.bp
@@ -0,0 +1,28 @@
+rust_test {
+ name: "rustBinderTest",
+ srcs: ["integration.rs"],
+ rustlibs: [
+ "libbinder_rs",
+ ],
+ // For the binaries to be pushed properly as specified in AndroidTest.xml,
+ // this cannot be the same as the module name.
+ stem: "rustBinderTestClientBinary",
+ test_suites: ["general-tests"],
+}
+
+rust_test {
+ name: "rustBinderTestService",
+ srcs: ["integration.rs"],
+ rustlibs: [
+ "libbinder_rs",
+ "liblibc",
+ ],
+ // For the binaries to be pushed properly as specified in AndroidTest.xml,
+ // this cannot be the same as the module name.
+ stem: "rustBinderTestServiceBinary",
+ test_harness: false,
+ // TODO(b/164473602): Remove this setting and add the module to `data`
+ // attribute of rustBinderTest.
+ auto_gen_config: false,
+ test_suites: ["general-tests"],
+}
diff --git a/libs/binder/rust/tests/AndroidTest.xml b/libs/binder/rust/tests/AndroidTest.xml
new file mode 100644
index 0000000..d8d735d
--- /dev/null
+++ b/libs/binder/rust/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Binder Rust integration tests.">
+ <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="rustBinderTestClientBinary->/data/local/tmp/rustBinderTest" />
+ <option name="push" value="rustBinderTestServiceBinary->/data/local/tmp/rustBinderTestService" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+ <option name="test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="rustBinderTest" />
+ </test>
+</configuration>
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
new file mode 100644
index 0000000..fe59416
--- /dev/null
+++ b/libs/binder/rust/tests/integration.rs
@@ -0,0 +1,371 @@
+/*
+ * 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.
+ */
+
+//! Rust Binder crate integration tests
+
+use binder::declare_binder_interface;
+use binder::parcel::Parcel;
+use binder::{Binder, IBinder, Interface, SpIBinder, TransactionCode};
+
+/// Name of service runner.
+///
+/// Must match the binary name in Android.bp
+const RUST_SERVICE_BINARY: &str = "rustBinderTestService";
+
+/// Binary to run a test service.
+///
+/// This needs to be in a separate process from the tests, so we spawn this
+/// binary as a child, providing the service name as an argument.
+fn main() -> Result<(), &'static str> {
+ // Ensure that we can handle all transactions on the main thread.
+ binder::ProcessState::set_thread_pool_max_thread_count(0);
+ binder::ProcessState::start_thread_pool();
+
+ let mut args = std::env::args().skip(1);
+ if args.len() < 1 || args.len() > 2 {
+ print_usage();
+ return Err("");
+ }
+ let service_name = args.next().ok_or_else(|| {
+ print_usage();
+ "Missing SERVICE_NAME argument"
+ })?;
+ let extension_name = args.next();
+
+ {
+ let mut service = Binder::new(BnTest(Box::new(TestService {
+ s: service_name.clone(),
+ })));
+ if let Some(extension_name) = extension_name {
+ let extension = BnTest::new_binder(TestService { s: extension_name });
+ service
+ .set_extension(&mut extension.as_binder())
+ .expect("Could not add extension");
+ }
+ binder::add_service(&service_name, service.as_binder())
+ .expect("Could not register service");
+ }
+
+ binder::ProcessState::join_thread_pool();
+ Err("Unexpected exit after join_thread_pool")
+}
+
+fn print_usage() {
+ eprintln!(
+ "Usage: {} SERVICE_NAME [EXTENSION_NAME]",
+ RUST_SERVICE_BINARY
+ );
+ eprintln!(concat!(
+ "Spawn a Binder test service identified by SERVICE_NAME,",
+ " optionally with an extesion named EXTENSION_NAME",
+ ));
+}
+
+#[derive(Clone)]
+struct TestService {
+ s: String,
+}
+
+impl Interface for TestService {}
+
+impl ITest for TestService {
+ fn test(&self) -> binder::Result<String> {
+ Ok(self.s.clone())
+ }
+}
+
+/// Trivial testing binder interface
+pub trait ITest: Interface {
+ /// Returns a test string
+ fn test(&self) -> binder::Result<String>;
+}
+
+declare_binder_interface! {
+ ITest["android.os.ITest"] {
+ native: BnTest(on_transact),
+ proxy: BpTest {
+ x: i32 = 100
+ },
+ }
+}
+
+fn on_transact(
+ service: &dyn ITest,
+ _code: TransactionCode,
+ _data: &Parcel,
+ reply: &mut Parcel,
+) -> binder::Result<()> {
+ reply.write(&service.test()?)?;
+ Ok(())
+}
+
+impl ITest for BpTest {
+ fn test(&self) -> binder::Result<String> {
+ let reply = self
+ .binder
+ .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?;
+ reply.read()
+ }
+}
+
+impl ITest for Binder<BnTest> {
+ fn test(&self) -> binder::Result<String> {
+ self.0.test()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::fs::File;
+ use std::process::{Child, Command};
+ use std::sync::atomic::{AtomicBool, Ordering};
+ use std::sync::Arc;
+ use std::thread;
+ use std::time::Duration;
+
+ use binder::{DeathRecipient, FromIBinder, IBinder, SpIBinder, StatusCode};
+
+ use super::{ITest, RUST_SERVICE_BINARY};
+
+ pub struct ScopedServiceProcess(Child);
+
+ impl ScopedServiceProcess {
+ pub fn new(identifier: &str) -> Self {
+ Self::new_internal(identifier, None)
+ }
+
+ pub fn new_with_extension(identifier: &str, extension: &str) -> Self {
+ Self::new_internal(identifier, Some(extension))
+ }
+
+ fn new_internal(identifier: &str, extension: Option<&str>) -> Self {
+ let mut binary_path =
+ std::env::current_exe().expect("Could not retrieve current executable path");
+ binary_path.pop();
+ binary_path.push(RUST_SERVICE_BINARY);
+ let mut command = Command::new(&binary_path);
+ command.arg(identifier);
+ if let Some(ext) = extension {
+ command.arg(ext);
+ }
+ let child = command.spawn().expect("Could not start service");
+ Self(child)
+ }
+ }
+
+ impl Drop for ScopedServiceProcess {
+ fn drop(&mut self) {
+ self.0.kill().expect("Could not kill child process");
+ self.0
+ .wait()
+ .expect("Could not wait for child process to die");
+ }
+ }
+
+ #[test]
+ fn check_services() {
+ let mut sm = binder::get_service("manager").expect("Did not get manager binder service");
+ assert!(sm.is_binder_alive());
+ assert!(sm.ping_binder().is_ok());
+
+ assert!(binder::get_service("this_service_does_not_exist").is_none());
+ assert_eq!(
+ binder::get_interface::<dyn ITest>("this_service_does_not_exist").err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
+
+ // The service manager service isn't an ITest, so this must fail.
+ assert_eq!(
+ binder::get_interface::<dyn ITest>("manager").err(),
+ Some(StatusCode::BAD_TYPE)
+ );
+ }
+
+ #[test]
+ fn trivial_client() {
+ let service_name = "trivial_client_test";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Box<dyn ITest> =
+ binder::get_interface(service_name).expect("Did not get manager binder service");
+ assert_eq!(test_client.test().unwrap(), "trivial_client_test");
+ }
+
+ fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) {
+ let binder_died = Arc::new(AtomicBool::new(false));
+
+ let mut death_recipient = {
+ let flag = binder_died.clone();
+ DeathRecipient::new(move || {
+ flag.store(true, Ordering::Relaxed);
+ })
+ };
+
+ binder
+ .link_to_death(&mut death_recipient)
+ .expect("link_to_death failed");
+
+ (binder_died, death_recipient)
+ }
+
+ /// Killing a remote service should unregister the service and trigger
+ /// death notifications.
+ #[test]
+ fn test_death_notifications() {
+ binder::ProcessState::start_thread_pool();
+
+ let service_name = "test_death_notifications";
+ let service_process = ScopedServiceProcess::new(service_name);
+ let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
+
+ let (binder_died, _recipient) = register_death_notification(&mut remote);
+
+ drop(service_process);
+ remote
+ .ping_binder()
+ .expect_err("Service should have died already");
+
+ // Pause to ensure any death notifications get delivered
+ thread::sleep(Duration::from_secs(1));
+
+ assert!(
+ binder_died.load(Ordering::Relaxed),
+ "Did not receive death notification"
+ );
+ }
+
+ /// Test unregistering death notifications.
+ #[test]
+ fn test_unregister_death_notifications() {
+ binder::ProcessState::start_thread_pool();
+
+ let service_name = "test_unregister_death_notifications";
+ let service_process = ScopedServiceProcess::new(service_name);
+ let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
+
+ let (binder_died, mut recipient) = register_death_notification(&mut remote);
+
+ remote
+ .unlink_to_death(&mut recipient)
+ .expect("Could not unlink death notifications");
+
+ drop(service_process);
+ remote
+ .ping_binder()
+ .expect_err("Service should have died already");
+
+ // Pause to ensure any death notifications get delivered
+ thread::sleep(Duration::from_secs(1));
+
+ assert!(
+ !binder_died.load(Ordering::Relaxed),
+ "Received unexpected death notification after unlinking",
+ );
+ }
+
+ /// Dropping a remote handle should unregister any death notifications.
+ #[test]
+ fn test_death_notification_registration_lifetime() {
+ binder::ProcessState::start_thread_pool();
+
+ let service_name = "test_death_notification_registration_lifetime";
+ let service_process = ScopedServiceProcess::new(service_name);
+ let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
+
+ let (binder_died, _recipient) = register_death_notification(&mut remote);
+
+ // This should automatically unregister our death notification.
+ drop(remote);
+
+ drop(service_process);
+
+ // Pause to ensure any death notifications get delivered
+ thread::sleep(Duration::from_secs(1));
+
+ // We dropped the remote handle, so we should not receive the death
+ // notification when the remote process dies here.
+ assert!(
+ !binder_died.load(Ordering::Relaxed),
+ "Received unexpected death notification after dropping remote handle"
+ );
+ }
+
+ /// Test IBinder interface methods not exercised elsewhere.
+ #[test]
+ fn test_misc_ibinder() {
+ let service_name = "rust_test_ibinder";
+
+ {
+ let _process = ScopedServiceProcess::new(service_name);
+
+ let mut remote = binder::get_service(service_name);
+ assert!(remote.is_binder_alive());
+ remote.ping_binder().expect("Could not ping remote service");
+
+ // We're not testing the output of dump here, as that's really a
+ // property of the C++ implementation. There is the risk that the
+ // method just does nothing, but we don't want to depend on any
+ // particular output from the underlying library.
+ let null_out = File::open("/dev/null").expect("Could not open /dev/null");
+ remote
+ .dump(&null_out, &[])
+ .expect("Could not dump remote service");
+ }
+
+ // get/set_extensions is tested in test_extensions()
+
+ // transact is tested everywhere else, and we can't make raw
+ // transactions outside the [FIRST_CALL_TRANSACTION,
+ // LAST_CALL_TRANSACTION] range from the NDK anyway.
+
+ // link_to_death is tested in test_*_death_notification* tests.
+ }
+
+ #[test]
+ fn test_extensions() {
+ let service_name = "rust_test_extensions";
+ let extension_name = "rust_test_extensions_ext";
+
+ {
+ let _process = ScopedServiceProcess::new(service_name);
+
+ let mut remote = binder::get_service(service_name);
+ assert!(remote.is_binder_alive());
+
+ let extension = remote
+ .get_extension()
+ .expect("Could not check for an extension");
+ assert!(extension.is_none());
+ }
+
+ {
+ let _process = ScopedServiceProcess::new_with_extension(service_name, extension_name);
+
+ let mut remote = binder::get_service(service_name);
+ assert!(remote.is_binder_alive());
+
+ let maybe_extension = remote
+ .get_extension()
+ .expect("Could not check for an extension");
+
+ let extension = maybe_extension.expect("Remote binder did not have an extension");
+
+ let extension: Box<dyn ITest> = FromIBinder::try_from(extension)
+ .expect("Extension could not be converted to the expected interface");
+
+ assert_eq!(extension.test().unwrap(), extension_name);
+ }
+ }
+}
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 50f6289..5e785b6 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -251,7 +251,7 @@
for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) {
key.bucket = i;
if (findMapEntry(gTisMapFd, &key, vals.data())) {
- if (errno != ENOENT) return {};
+ if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {};
continue;
}
@@ -362,7 +362,7 @@
time_key_t key = {.uid = uid};
for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
if (findMapEntry(gConcurrentMapFd, &key, vals.data())) {
- if (errno != ENOENT) return {};
+ if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {};
continue;
}
auto offset = key.bucket * CPUS_PER_ENTRY;
diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp
index ef7a6f5..351af65 100644
--- a/libs/gui/BitTube.cpp
+++ b/libs/gui/BitTube.cpp
@@ -86,6 +86,10 @@
mReceiveFd = std::move(receiveFd);
}
+void BitTube::setSendFd(base::unique_fd&& sendFd) {
+ mSendFd = std::move(sendFd);
+}
+
ssize_t BitTube::write(void const* vaddr, size_t size) {
ssize_t err, len;
do {
@@ -115,6 +119,11 @@
status_t result = reply->writeDupFileDescriptor(mReceiveFd);
mReceiveFd.reset();
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->writeDupFileDescriptor(mSendFd);
+ mSendFd.reset();
return result;
}
@@ -126,6 +135,13 @@
ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error));
return -error;
}
+ mSendFd.reset(dup(parcel->readFileDescriptor()));
+ if (mSendFd < 0) {
+ mSendFd.reset();
+ int error = errno;
+ ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error));
+ return -error;
+ }
return NO_ERROR;
}
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 51fbb97..2cc7c34 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -89,12 +89,8 @@
return OK;
}
-void DisplayEventDispatcher::requestLatestConfig() {
- status_t status = mReceiver.requestLatestConfig();
- if (status) {
- ALOGW("Failed enable config events, status=%d", status);
- return;
- }
+void DisplayEventDispatcher::injectEvent(const DisplayEventReceiver::Event& event) {
+ mReceiver.sendEvents(&event, 1);
}
int DisplayEventDispatcher::getFd() const {
@@ -156,6 +152,9 @@
dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
ev.config.configId, ev.config.vsyncPeriod);
break;
+ case DisplayEventReceiver::DISPLAY_EVENT_NULL:
+ dispatchNullEvent(ev.header.timestamp, ev.header.displayId);
+ break;
default:
ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
break;
@@ -167,4 +166,5 @@
}
return gotVsync;
}
+
} // namespace android
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 1fed509..f2b0962 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -79,14 +79,6 @@
return NO_INIT;
}
-status_t DisplayEventReceiver::requestLatestConfig() {
- if (mEventConnection != nullptr) {
- mEventConnection->requestLatestConfig();
- return NO_ERROR;
- }
- return NO_INIT;
-}
-
ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
size_t count) {
return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count);
@@ -98,6 +90,10 @@
return gui::BitTube::recvObjects(dataChannel, events, count);
}
+ssize_t DisplayEventReceiver::sendEvents(Event const* events, size_t count) {
+ return DisplayEventReceiver::sendEvents(mDataChannel.get(), events, count);
+}
+
ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,
Event const* events, size_t count)
{
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index aa74bfd..c0e246f 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -26,8 +26,7 @@
STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
SET_VSYNC_RATE,
REQUEST_NEXT_VSYNC,
- REQUEST_LATEST_CONFIG,
- LAST = REQUEST_LATEST_CONFIG,
+ LAST = REQUEST_NEXT_VSYNC,
};
} // Anonymous namespace
@@ -54,11 +53,6 @@
callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
Tag::REQUEST_NEXT_VSYNC);
}
-
- void requestLatestConfig() override {
- callRemoteAsync<decltype(&IDisplayEventConnection::requestLatestConfig)>(
- Tag::REQUEST_LATEST_CONFIG);
- }
};
// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
@@ -80,8 +74,6 @@
return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
case Tag::REQUEST_NEXT_VSYNC:
return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
- case Tag::REQUEST_LATEST_CONFIG:
- return callLocalAsync(data, reply, &IDisplayEventConnection::requestLatestConfig);
}
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index b215756..725d3cd 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -61,7 +61,7 @@
output.writeUint32(transform);
output.writeBool(transformToDisplayInverse);
output.write(crop);
- output.write(frame);
+ output.write(orientedDisplaySpaceRect);
if (buffer) {
output.writeBool(true);
output.write(*buffer);
@@ -159,7 +159,7 @@
transform = input.readUint32();
transformToDisplayInverse = input.readBool();
input.read(crop);
- input.read(frame);
+ input.read(orientedDisplaySpaceRect);
buffer = new GraphicBuffer();
if (input.readBool()) {
input.read(*buffer);
@@ -216,15 +216,13 @@
return state.read(input);
}
-
-DisplayState::DisplayState() :
- what(0),
- layerStack(0),
- viewport(Rect::EMPTY_RECT),
- frame(Rect::EMPTY_RECT),
- width(0),
- height(0) {
-}
+DisplayState::DisplayState()
+ : what(0),
+ layerStack(0),
+ layerStackSpaceRect(Rect::EMPTY_RECT),
+ orientedDisplaySpaceRect(Rect::EMPTY_RECT),
+ width(0),
+ height(0) {}
status_t DisplayState::write(Parcel& output) const {
output.writeStrongBinder(token);
@@ -232,8 +230,8 @@
output.writeUint32(what);
output.writeUint32(layerStack);
output.writeUint32(toRotationInt(orientation));
- output.write(viewport);
- output.write(frame);
+ output.write(layerStackSpaceRect);
+ output.write(orientedDisplaySpaceRect);
output.writeUint32(width);
output.writeUint32(height);
return NO_ERROR;
@@ -245,8 +243,8 @@
what = input.readUint32();
layerStack = input.readUint32();
orientation = ui::toRotation(input.readUint32());
- input.read(viewport);
- input.read(frame);
+ input.read(layerStackSpaceRect);
+ input.read(orientedDisplaySpaceRect);
width = input.readUint32();
height = input.readUint32();
return NO_ERROR;
@@ -264,8 +262,8 @@
if (other.what & eDisplayProjectionChanged) {
what |= eDisplayProjectionChanged;
orientation = other.orientation;
- viewport = other.viewport;
- frame = other.frame;
+ layerStackSpaceRect = other.layerStackSpaceRect;
+ orientedDisplaySpaceRect = other.orientedDisplaySpaceRect;
}
if (other.what & eDisplaySizeChanged) {
what |= eDisplaySizeChanged;
@@ -368,7 +366,7 @@
}
if (other.what & eFrameChanged) {
what |= eFrameChanged;
- frame = other.frame;
+ orientedDisplaySpaceRect = other.orientedDisplaySpaceRect;
}
if (other.what & eBufferChanged) {
what |= eBufferChanged;
@@ -509,7 +507,8 @@
status_t status = output.writeInt32(static_cast<int32_t>(pixelFormat)) ?:
output.write(sourceCrop) ?:
output.writeFloat(frameScale) ?:
- output.writeBool(captureSecureLayers);
+ output.writeBool(captureSecureLayers) ?:
+ output.writeInt32(uid);
return status;
}
@@ -518,7 +517,8 @@
status_t status = input.readInt32(&format) ?:
input.read(sourceCrop) ?:
input.readFloat(&frameScale) ?:
- input.readBool(&captureSecureLayers);
+ input.readBool(&captureSecureLayers) ?:
+ input.readInt32(&uid);
pixelFormat = static_cast<ui::PixelFormat>(format);
return status;
@@ -530,23 +530,17 @@
status |= output.writeStrongBinder(displayToken) ?:
output.writeUint32(width) ?:
output.writeUint32(height) ?:
- output.writeBool(useIdentityTransform) ?:
- output.writeInt32(static_cast<int32_t>(rotation));
+ output.writeBool(useIdentityTransform);
return status;
}
status_t DisplayCaptureArgs::read(const Parcel& input) {
status_t status = CaptureArgs::read(input);
- int32_t rotationInt = 0;
-
status |= input.readStrongBinder(&displayToken) ?:
input.readUint32(&width) ?:
input.readUint32(&height) ?:
- input.readBool(&useIdentityTransform) ?:
- input.readInt32(&rotationInt);
-
- rotation = ui::toRotation(rotationInt);
+ input.readBool(&useIdentityTransform);
return status;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b51bf1f..62a3c45 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1137,7 +1137,7 @@
return *this;
}
s->what |= layer_state_t::eFrameChanged;
- s->frame = frame;
+ s->orientedDisplaySpaceRect = frame;
registerSurfaceControlForCallback(sc);
return *this;
@@ -1545,8 +1545,8 @@
const Rect& displayRect) {
DisplayState& s(getDisplayState(token));
s.orientation = orientation;
- s.viewport = layerStackRect;
- s.frame = displayRect;
+ s.layerStackSpaceRect = layerStackRect;
+ s.orientedDisplaySpaceRect = displayRect;
s.what |= DisplayState::eDisplayProjectionChanged;
mForceSynchronous = true; // TODO: do we actually still need this?
}
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp
index fcae05c..8df6e81 100644
--- a/libs/gui/SyncFeatures.cpp
+++ b/libs/gui/SyncFeatures.cpp
@@ -27,8 +27,6 @@
#include <private/gui/SyncFeatures.h>
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
namespace android {
ANDROID_SINGLETON_STATIC_INSTANCE(SyncFeatures);
@@ -40,8 +38,8 @@
EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
// This can only be called after EGL has been initialized; otherwise the
// check below will abort.
- const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
- LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed");
+ const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+ LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryString failed");
if (strstr(exts, "EGL_ANDROID_native_fence_sync")) {
// This makes GLConsumer use the EGL_ANDROID_native_fence_sync
// extension to create Android native fences to signal when all
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index f210c34..eb5b004 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -31,7 +31,7 @@
status_t initialize();
void dispose();
status_t scheduleVsync();
- void requestLatestConfig();
+ void injectEvent(const DisplayEventReceiver::Event& event);
int getFd() const;
virtual int handleEvent(int receiveFd, int events, void* data);
@@ -48,6 +48,9 @@
bool connected) = 0;
virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
int32_t configId, nsecs_t vsyncPeriod) = 0;
+ // AChoreographer-specific hook for processing null-events so that looper
+ // can be properly poked.
+ virtual void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) = 0;
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
uint32_t* outCount);
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 8d49184..7974a06 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -53,19 +53,25 @@
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'),
+ DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
};
struct Event {
+ // We add __attribute__((aligned(8))) for nsecs_t fields because
+ // we need to make sure all fields are aligned the same with x86
+ // and x64 (long long has different default alignment):
+ //
+ // https://en.wikipedia.org/wiki/Data_structure_alignment
struct Header {
uint32_t type;
- PhysicalDisplayId displayId;
+ PhysicalDisplayId displayId __attribute__((aligned(8)));
nsecs_t timestamp __attribute__((aligned(8)));
};
struct VSync {
uint32_t count;
- nsecs_t expectedVSyncTimestamp;
+ nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
};
struct Hotplug {
@@ -74,7 +80,7 @@
struct Config {
int32_t configId;
- nsecs_t vsyncPeriod;
+ nsecs_t vsyncPeriod __attribute__((aligned(8)));
};
Header header;
@@ -130,6 +136,7 @@
* sendEvents write events to the queue and returns how many events were
* written.
*/
+ ssize_t sendEvents(Event const* events, size_t count);
static ssize_t sendEvents(gui::BitTube* dataChannel, Event const* events, size_t count);
/*
@@ -146,12 +153,6 @@
*/
status_t requestNextVsync();
- /*
- * requestLatestConfig() force-requests the current config for the primary
- * display.
- */
- status_t requestLatestConfig();
-
private:
sp<IDisplayEventConnection> mEventConnection;
std::unique_ptr<gui::BitTube> mDataChannel;
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
index 674aafd..cff22a3 100644
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ b/libs/gui/include/gui/IDisplayEventConnection.h
@@ -51,11 +51,6 @@
* requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
*/
virtual void requestNextVsync() = 0; // Asynchronous
-
- /*
- * requestLatestConfig() requests the config for the primary display.
- */
- virtual void requestLatestConfig() = 0; // Asynchronous
};
class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index de3a9a7..187e478 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -129,7 +129,7 @@
transform(0),
transformToDisplayInverse(false),
crop(Rect::INVALID_RECT),
- frame(Rect::INVALID_RECT),
+ orientedDisplaySpaceRect(Rect::INVALID_RECT),
dataspace(ui::Dataspace::UNKNOWN),
surfaceDamageRegion(),
api(-1),
@@ -192,7 +192,7 @@
uint32_t transform;
bool transformToDisplayInverse;
Rect crop;
- Rect frame;
+ Rect orientedDisplaySpaceRect;
sp<GraphicBuffer> buffer;
sp<Fence> acquireFence;
ui::Dataspace dataspace;
@@ -265,18 +265,18 @@
// These states define how layers are projected onto the physical display.
//
- // Layers are first clipped to `viewport'. They are then translated and
- // scaled from `viewport' to `frame'. Finally, they are rotated according
- // to `orientation', `width', and `height'.
+ // Layers are first clipped to `layerStackSpaceRect'. They are then translated and
+ // scaled from `layerStackSpaceRect' to `orientedDisplaySpaceRect'. Finally, they are rotated
+ // according to `orientation', `width', and `height'.
//
- // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20,
- // 10, 420, 210), and the size of the display is WxH. When orientation is
- // 0, layers will be scaled by a factor of 2 and translated by (20, 10).
- // When orientation is 1, layers will be additionally rotated by 90
- // degrees around the origin clockwise and translated by (W, 0).
+ // For example, assume layerStackSpaceRect is Rect(0, 0, 200, 100), orientedDisplaySpaceRect is
+ // Rect(20, 10, 420, 210), and the size of the display is WxH. When orientation is 0, layers
+ // will be scaled by a factor of 2 and translated by (20, 10). When orientation is 1, layers
+ // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W,
+ // 0).
ui::Rotation orientation = ui::ROTATION_0;
- Rect viewport;
- Rect frame;
+ Rect layerStackSpaceRect;
+ Rect orientedDisplaySpaceRect;
uint32_t width, height;
@@ -314,12 +314,14 @@
bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName);
struct CaptureArgs {
+ const static int32_t UNSET_UID = -1;
virtual ~CaptureArgs() = default;
ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
Rect sourceCrop;
float frameScale{1};
bool captureSecureLayers{false};
+ int32_t uid{UNSET_UID};
virtual status_t write(Parcel& output) const;
virtual status_t read(const Parcel& input);
@@ -330,7 +332,6 @@
uint32_t width{0};
uint32_t height{0};
bool useIdentityTransform{false};
- ui::Rotation rotation{ui::ROTATION_0};
status_t write(Parcel& output) const override;
status_t read(const Parcel& input) override;
@@ -339,7 +340,7 @@
struct LayerCaptureArgs : CaptureArgs {
sp<IBinder> layerHandle;
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeHandles;
- bool childrenOnly{true};
+ bool childrenOnly{false};
status_t write(Parcel& output) const override;
status_t read(const Parcel& input) override;
diff --git a/libs/gui/include/private/gui/BitTube.h b/libs/gui/include/private/gui/BitTube.h
index 13c0162..8048518 100644
--- a/libs/gui/include/private/gui/BitTube.h
+++ b/libs/gui/include/private/gui/BitTube.h
@@ -58,6 +58,9 @@
// resets this BitTube's receive file descriptor to receiveFd
void setReceiveFd(base::unique_fd&& receiveFd);
+ // resets this BitTube's send file descriptor to sendFd
+ void setSendFd(base::unique_fd&& sendFd);
+
// send objects (sized blobs). All objects are guaranteed to be written or the call fails.
template <typename T>
static ssize_t sendObjects(BitTube* tube, T const* events, size_t count) {
@@ -85,7 +88,7 @@
// the message, excess data is silently discarded.
ssize_t read(void* vaddr, size_t size);
- base::unique_fd mSendFd;
+ mutable base::unique_fd mSendFd;
mutable base::unique_fd mReceiveFd;
static ssize_t sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize);
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index a6bcd10..53c13c8 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -14,7 +14,7 @@
srcs: [
"BLASTBufferQueue_test.cpp",
- "BufferItemConsumer_test.cpp",
+ "BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
@@ -58,6 +58,30 @@
header_libs: ["libsurfaceflinger_headers"],
}
+// Build the tests that need to run with both 32bit and 64bit.
+cc_test {
+ name: "libgui_multilib_test",
+ test_suites: ["device-tests"],
+
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: [
+ "DisplayEventStructLayout_test.cpp",
+ ],
+
+ shared_libs: [
+ "libgui",
+ ],
+
+ compile_multilib: "both",
+
+ header_libs: ["libsurfaceflinger_headers"],
+}
+
// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
// This test has a main method, and requires a separate binary to be built.
// To add move tests like this, just add additional cc_test statements,
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
new file mode 100644
index 0000000..4bcb795
--- /dev/null
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <gui/DisplayEventReceiver.h>
+
+namespace android::test {
+
+#define CHECK_OFFSET(type, member, expected_offset) \
+ static_assert((offsetof(type, member) == (expected_offset)), "")
+
+TEST(DisplayEventStructLayoutTest, TestEventAlignment) {
+ CHECK_OFFSET(DisplayEventReceiver::Event, vsync, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event, hotplug, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event, config, 24);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::Header, type, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::Header, displayId, 8);
+ CHECK_OFFSET(DisplayEventReceiver::Event::Header, timestamp, 16);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, expectedVSyncTimestamp, 8);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0);
+
+ CHECK_OFFSET(DisplayEventReceiver::Event::Config, configId, 0);
+ CHECK_OFFSET(DisplayEventReceiver::Event::Config, vsyncPeriod, 8);
+}
+
+} // namespace android::test
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index b088ee7..79e15c1 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -481,13 +481,13 @@
}
if (DEBUG_TRANSPORT_ACTIONS) {
std::string transformString;
- transform.dump(transformString, "");
+ transform.dump(transformString, "transform", " ");
ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
"displayId=%" PRId32 ", "
"action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
"metaState=0x%x, buttonState=0x%x, classification=%s,"
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
- "pointerCount=%" PRIu32 " transform=%s",
+ "pointerCount=%" PRIu32 " \n%s",
mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
flags, edgeFlags, metaState, buttonState,
motionClassificationToString(classification), xPrecision, yPrecision, downTime,
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index ebc8909..ff1b5e6 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -136,6 +136,7 @@
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
nsecs_t vsyncPeriod) override;
+ void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
void scheduleCallbacks();
@@ -170,7 +171,7 @@
Choreographer::Choreographer(const sp<Looper>& looper)
: DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp,
- ISurfaceComposer::ConfigChanged::eConfigChangedDispatch),
+ ISurfaceComposer::ConfigChanged::eConfigChangedSuppress),
mLooper(looper),
mThreadId(std::this_thread::get_id()) {
std::lock_guard<std::mutex> _l(gChoreographers.lock);
@@ -294,8 +295,14 @@
} else {
// If the looper thread is detached from Choreographer, then refresh rate
// changes will be handled in AChoreographer_handlePendingEvents, so we
- // need to redispatch a config from SF
- requestLatestConfig();
+ // need to wake up the looper thread by writing to the write-end of the
+ // socket the looper is listening on.
+ // Fortunately, these events are small so sending packets across the
+ // socket should be atomic across processes.
+ DisplayEventReceiver::Event event;
+ event.header = DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+ PhysicalDisplayId(0), systemTime()};
+ injectEvent(event);
}
}
@@ -373,27 +380,14 @@
// displays. When multi-display choreographer is properly supported, then
// PhysicalDisplayId should no longer be ignored.
void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId,
- nsecs_t vsyncPeriod) {
+ nsecs_t) {
ALOGV("choreographer %p ~ received config change event (displayId=%s, configId=%d).",
this, to_string(displayId).c_str(), configId);
+}
- const nsecs_t lastPeriod = mLatestVsyncPeriod;
- std::vector<RefreshRateCallback> callbacks{};
- {
- std::lock_guard<std::mutex> _l{mLock};
- for (auto& cb : mRefreshRateCallbacks) {
- callbacks.push_back(cb);
- cb.firstCallbackFired = true;
- }
- }
-
- for (auto& cb : callbacks) {
- if (!cb.firstCallbackFired || (vsyncPeriod > 0 && vsyncPeriod != lastPeriod)) {
- cb.callback(vsyncPeriod, cb.data);
- }
- }
-
- mLatestVsyncPeriod = vsyncPeriod;
+void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
+ ALOGV("choreographer %p ~ received null event.", this);
+ handleRefreshRateUpdates();
}
void Choreographer::handleMessage(const Message& message) {
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
index c00fbba..b44456b 100644
--- a/libs/renderengine/OWNERS
+++ b/libs/renderengine/OWNERS
@@ -1,2 +1,4 @@
+alecmouri@google.com
+jreck@google.com
lpy@google.com
stoza@google.com
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 6b18848..672ffae 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -51,8 +51,6 @@
#include "ProgramCache.h"
#include "filters/BlurFilter.h"
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
bool checkGlError(const char* op, int lineNumber) {
bool errorFound = false;
GLint error = glGetError();
@@ -208,16 +206,16 @@
LOG_ALWAYS_FATAL("failed to initialize EGL");
}
- const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION);
+ const auto eglVersion = eglQueryString(display, EGL_VERSION);
if (!eglVersion) {
checkGlError(__FUNCTION__, __LINE__);
- LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed");
+ LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
}
- const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS);
+ const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
if (!eglExtensions) {
checkGlError(__FUNCTION__, __LINE__);
- LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed");
+ LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
}
GLExtensions& extensions = GLExtensions::getInstance();
@@ -409,6 +407,23 @@
mImageManager = std::make_unique<ImageManager>(this);
mImageManager->initThread();
mDrawingBuffer = createFramebuffer();
+ sp<GraphicBuffer> buf =
+ new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "placeholder");
+
+ const status_t err = buf->initCheck();
+ if (err != OK) {
+ ALOGE("Error allocating placeholder buffer: %d", err);
+ return;
+ }
+ mPlaceholderBuffer = buf.get();
+ EGLint attributes[] = {
+ EGL_NONE,
+ };
+ mPlaceholderImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ mPlaceholderBuffer, attributes);
+ ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x",
+ eglGetError());
}
GLESRenderEngine::~GLESRenderEngine() {
@@ -423,6 +438,7 @@
eglDestroyImageKHR(mEGLDisplay, expired);
DEBUG_EGL_IMAGE_TRACKER_DESTROY();
}
+ eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
mImageCache.clear();
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mEGLDisplay);
@@ -589,6 +605,9 @@
}
void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) {
+ for (int i = 0; i < count; ++i) {
+ mTextureView.erase(names[i]);
+ }
glDeleteTextures(count, names);
}
@@ -646,6 +665,7 @@
}
bindExternalTextureImage(texName, *cachedImage->second);
+ mTextureView.insert_or_assign(texName, buffer->getId());
}
// Wait for the new buffer to be ready.
@@ -887,7 +907,7 @@
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
-bool GLESRenderEngine::cleanupPostRender() {
+bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) {
ATRACE_CALL();
if (mPriorResourcesCleaned ||
@@ -896,6 +916,30 @@
return false;
}
+ // This is a bit of a band-aid fix for FrameCaptureProcessor, as we should
+ // not need to keep memory around if we don't need to do so.
+ if (mode == CleanupMode::CLEAN_ALL) {
+ // TODO: SurfaceFlinger memory utilization may benefit from resetting
+ // texture bindings as well. Assess if it does and there's no performance regression
+ // when rebinding the same image data to the same texture, and if so then its mode
+ // behavior can be tweaked.
+ if (mPlaceholderImage != EGL_NO_IMAGE_KHR) {
+ for (auto [textureName, bufferId] : mTextureView) {
+ if (bufferId && mPlaceholderImage != EGL_NO_IMAGE_KHR) {
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
+ static_cast<GLeglImageOES>(mPlaceholderImage));
+ mTextureView[textureName] = std::nullopt;
+ checkErrors();
+ }
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mImageCache.clear();
+ }
+ }
+
// Bind the texture to placeholder so that backing image data can be freed.
GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
@@ -1620,6 +1664,16 @@
return cachedImage != mImageCache.end();
}
+bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) {
+ const auto& entry = mTextureView.find(texName);
+ return entry != mTextureView.end();
+}
+
+std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) {
+ const auto& entry = mTextureView.find(texName);
+ return entry != mTextureView.end() ? entry->second : std::nullopt;
+}
+
bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 245bfd7..2c6eae2 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -75,7 +75,7 @@
const std::vector<const LayerSettings*>& layers,
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
- bool cleanupPostRender() override;
+ bool cleanupPostRender(CleanupMode mode) override;
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
// Creates an output image for rendering to
@@ -86,6 +86,12 @@
// Test-only methods
// Returns true iff mImageCache contains an image keyed by bufferId
bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ // Returns true iff texName was previously generated by RenderEngine and was
+ // not destroyed.
+ bool isTextureNameKnownForTesting(uint32_t texName);
+ // Returns the buffer ID of the content bound to texName, or nullopt if no
+ // such mapping exists.
+ std::optional<uint64_t> getBufferIdForTextureNameForTesting(uint32_t texName);
// Returns true iff mFramebufferImageCache contains an image keyed by bufferId
bool isFramebufferImageCachedForTesting(uint64_t bufferId)
EXCLUDES(mFramebufferImageCacheMutex);
@@ -224,6 +230,8 @@
// Cache of GL images that we'll store per GraphicBuffer ID
std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
+ std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView;
+
// Mutex guarding rendering operations, so that:
// 1. GL operations aren't interleaved, and
// 2. Internal state related to rendering that is potentially modified by
@@ -237,6 +245,11 @@
// ensure that we align on a word. Allocating 16 bytes will provide a
// guarantee that we don't clobber memory.
uint32_t mPlaceholderDrawBuffer[4];
+ // Placeholder buffer and image, similar to mPlaceholderDrawBuffer, but
+ // instead these are intended for cleaning up texture memory with the
+ // GL_TEXTURE_EXTERNAL_OES target.
+ ANativeWindowBuffer* mPlaceholderBuffer = nullptr;
+ EGLImage mPlaceholderImage = EGL_NO_IMAGE_KHR;
sp<Fence> mLastDrawFence;
// Store a separate boolean checking if prior resources were cleaned up, as
// devices that don't support native sync fences can't rely on a last draw
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index b137023..09a0f65 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -120,14 +120,25 @@
// Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
+
+ enum class CleanupMode {
+ CLEAN_OUTPUT_RESOURCES,
+ CLEAN_ALL,
+ };
// Clean-up method that should be called on the main thread after the
// drawFence returned by drawLayers fires. This method will free up
// resources used by the most recently drawn frame. If the frame is still
// being drawn, then this call is silently ignored.
//
+ // If mode is CLEAN_OUTPUT_RESOURCES, then only resources related to the
+ // output framebuffer are cleaned up, including the sibling texture.
+ //
+ // If mode is CLEAN_ALL, then we also cleanup resources related to any input
+ // buffers.
+ //
// Returns true if resources were cleaned up, and false if we didn't need to
// do any work.
- virtual bool cleanupPostRender() = 0;
+ virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;
// queries
virtual size_t getMaxTextureSize() const = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index d0343ba..e03dd58 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -56,7 +56,7 @@
MOCK_CONST_METHOD0(isProtected, bool());
MOCK_CONST_METHOD0(supportsProtectedContent, bool());
MOCK_METHOD1(useProtectedContext, bool(bool));
- MOCK_METHOD0(cleanupPostRender, bool());
+ MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
MOCK_METHOD6(drawLayers,
status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 8ab2746..ba17143 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -83,6 +83,7 @@
}
for (uint32_t texName : mTexNames) {
sRE->deleteTextures(1, &texName);
+ EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
}
}
@@ -1422,10 +1423,44 @@
if (fd >= 0) {
sync_wait(fd, -1);
}
-
// Only cleanup the first time.
- EXPECT_TRUE(sRE->cleanupPostRender());
- EXPECT_FALSE(sRE->cleanupPostRender());
+ EXPECT_TRUE(sRE->cleanupPostRender(
+ renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
+ EXPECT_FALSE(sRE->cleanupPostRender(
+ renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
+}
+
+TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<const renderengine::LayerSettings*> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(&layer);
+
+ base::unique_fd fence;
+ sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+
+ const int fd = fence.get();
+ if (fd >= 0) {
+ sync_wait(fd, -1);
+ }
+
+ uint64_t bufferId = layer.source.buffer.buffer->getId();
+ uint32_t texName = layer.source.buffer.textureName;
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(bufferId, sRE->getBufferIdForTextureNameForTesting(texName));
+
+ EXPECT_TRUE(sRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
+
+ // Now check that our view of memory is good.
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+ EXPECT_EQ(std::nullopt, sRE->getBufferIdForTextureNameForTesting(bufferId));
+ EXPECT_TRUE(sRE->isTextureNameKnownForTesting(texName));
}
} // namespace android
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 69a0e19..97c7442 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -178,14 +178,20 @@
}
TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
- EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(false));
- status_t result = mThreadedRE->cleanupPostRender();
+ EXPECT_CALL(*mRenderEngine,
+ cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+ .WillOnce(Return(false));
+ status_t result =
+ mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
ASSERT_EQ(false, result);
}
TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
- EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(true));
- status_t result = mThreadedRE->cleanupPostRender();
+ EXPECT_CALL(*mRenderEngine,
+ cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
+ .WillOnce(Return(true));
+ status_t result =
+ mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
ASSERT_EQ(true, result);
}
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index ad61718..d4184fd 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -361,14 +361,14 @@
return resultFuture.get();
}
-bool RenderEngineThreaded::cleanupPostRender() {
+bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) {
std::promise<bool> resultPromise;
std::future<bool> resultFuture = resultPromise.get_future();
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::cleanupPostRender");
- bool returnValue = instance.cleanupPostRender();
+ bool returnValue = instance.cleanupPostRender(mode);
resultPromise.set_value(returnValue);
});
}
@@ -400,4 +400,4 @@
} // namespace threaded
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index ec18e1f..86a49e9 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -62,7 +62,7 @@
bool isProtected() const override;
bool supportsProtectedContent() const override;
bool useProtectedContext(bool useProtectedContext) override;
- bool cleanupPostRender() override;
+ bool cleanupPostRender(CleanupMode mode) override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
@@ -94,4 +94,4 @@
};
} // namespace threaded
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index f394635..1f006ce 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -321,10 +321,6 @@
return std::string("Unknown RenderIntent");
}
-std::string to_string(const android::Rect& rect) {
- return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom);
-}
-
std::string toString(const android::DeviceProductInfo::ManufactureOrModelDate& date) {
using ModelYear = android::DeviceProductInfo::ModelYear;
using ManufactureYear = android::DeviceProductInfo::ManufactureYear;
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index 203a739..b1317b1 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -1,6 +1,7 @@
+alecmouri@google.com
chrisforbes@google.com
+jreck@google.com
lpy@google.com
mathias@google.com
romainguy@google.com
stoza@google.com
-vhau@google.com
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index 13fed3a..a8d6285 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/stringprintf.h>
#include <system/graphics.h>
#include <ui/Rect.h>
@@ -149,4 +150,13 @@
return result;
}
+std::string to_string(const android::Rect& rect) {
+ return android::base::StringPrintf("Rect(%d, %d, %d, %d)", rect.left, rect.top, rect.right,
+ rect.bottom);
+}
+
+void PrintTo(const Rect& rect, ::std::ostream* os) {
+ *os << to_string(rect);
+}
+
}; // namespace android
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 5424a3c..6b1bb40 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -457,7 +457,43 @@
return m;
}
-void Transform::dump(std::string& out, const char* name) const {
+static std::string rotationToString(const uint32_t rotationFlags) {
+ switch (rotationFlags) {
+ case Transform::ROT_0:
+ return "ROT_0";
+ case Transform::FLIP_H:
+ return "FLIP_H";
+ case Transform::FLIP_V:
+ return "FLIP_V";
+ case Transform::ROT_90:
+ return "ROT_90";
+ case Transform::ROT_180:
+ return "ROT_180";
+ case Transform::ROT_270:
+ return "ROT_270";
+ case Transform::ROT_INVALID:
+ default:
+ return "ROT_INVALID";
+ }
+}
+
+static std::string transformToString(const uint32_t transform) {
+ if (transform == Transform::IDENTITY) {
+ return "IDENTITY";
+ }
+
+ if (transform == Transform::UNKNOWN) {
+ return "UNKNOWN";
+ }
+
+ std::string out;
+ if (transform & Transform::SCALE) out.append("SCALE ");
+ if (transform & Transform::ROTATE) out.append("ROTATE ");
+ if (transform & Transform::TRANSLATE) out.append("TRANSLATE");
+ return out;
+}
+
+void Transform::dump(std::string& out, const char* name, const char* prefix) const {
using android::base::StringAppendF;
type(); // Ensure the information in mType is up to date
@@ -465,38 +501,29 @@
const uint32_t type = mType;
const uint32_t orient = type >> 8;
- StringAppendF(&out, "%s 0x%08x (", name, orient);
+ out += prefix;
+ out += name;
+ out += " ";
if (orient & ROT_INVALID) {
- out.append("ROT_INVALID ");
- } else {
- if (orient & ROT_90) {
- out.append("ROT_90 ");
- } else {
- out.append("ROT_0 ");
- }
- if (orient & FLIP_V) out.append("FLIP_V ");
- if (orient & FLIP_H) out.append("FLIP_H ");
+ StringAppendF(&out, "0x%08x ", orient);
}
+ out += "(" + rotationToString(orient) + ") ";
- StringAppendF(&out, ") 0x%02x (", type);
-
- if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY ");
- if (type & SCALE) out.append("SCALE ");
- if (type & ROTATE) out.append("ROTATE ");
- if (type & TRANSLATE) out.append("TRANSLATE ");
-
- out.append(")\n");
+ if (type & UNKNOWN) {
+ StringAppendF(&out, "0x%02x ", type);
+ }
+ out += "(" + transformToString(type) + ")\n";
for (size_t i = 0; i < 3; i++) {
- StringAppendF(&out, " %.4f %.4f %.4f\n", static_cast<double>(mMatrix[0][i]),
+ StringAppendF(&out, "%s %.4f %.4f %.4f\n", prefix, static_cast<double>(mMatrix[0][i]),
static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i]));
}
}
-void Transform::dump(const char* name) const {
+void Transform::dump(const char* name, const char* prefix) const {
std::string out;
- dump(out, name);
+ dump(out, name, prefix);
ALOGD("%s", out.c_str());
}
diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h
index 4685575..18cd487 100644
--- a/libs/ui/include/ui/DebugUtils.h
+++ b/libs/ui/include/ui/DebugUtils.h
@@ -34,5 +34,4 @@
std::string decodeColorTransform(android_color_transform colorTransform);
std::string decodePixelFormat(android::PixelFormat format);
std::string decodeRenderIntent(android::ui::RenderIntent renderIntent);
-std::string to_string(const android::Rect& rect);
std::string toString(const android::DeviceProductInfo&);
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
index 64efc84..70a0d50 100644
--- a/libs/ui/include/ui/DisplayState.h
+++ b/libs/ui/include/ui/DisplayState.h
@@ -32,7 +32,7 @@
struct DisplayState {
LayerStack layerStack = NO_LAYER_STACK;
Rotation orientation = ROTATION_0;
- Size viewport;
+ Size layerStackSpaceRect;
};
static_assert(std::is_trivially_copyable_v<DisplayState>);
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 013505a..57be686 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -260,6 +260,9 @@
uint64_t mId;
+ // Unused, but removing this may break GSI.
+ int32_t mBufferId = -1;
+
// Stores the generation number of this buffer. If this number does not
// match the BufferQueue's internal generation number (set through
// IGBP::setGenerationNumber), attempts to attach the buffer will fail.
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 2f2229e..6670dc0 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -19,10 +19,10 @@
#include <ostream>
+#include <log/log.h>
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/TypeHelpers.h>
-#include <log/log.h>
#include <ui/FloatRect.h>
#include <ui/Point.h>
@@ -216,11 +216,10 @@
}
};
+std::string to_string(const android::Rect& rect);
+
// Defining PrintTo helps with Google Tests.
-static inline void PrintTo(const Rect& rect, ::std::ostream* os) {
- *os << "Rect(" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom
- << ")";
-}
+void PrintTo(const Rect& rect, ::std::ostream* os);
ANDROID_BASIC_TYPES_TRAITS(Rect)
diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h
index 89008f6..83d431d 100644
--- a/libs/ui/include/ui/Rotation.h
+++ b/libs/ui/include/ui/Rotation.h
@@ -41,6 +41,15 @@
return toRotation((toRotationInt(lhs) + toRotationInt(rhs)) % N);
}
+constexpr Rotation operator-(Rotation lhs, Rotation rhs) {
+ constexpr auto N = toRotationInt(ROTATION_270) + 1;
+ return toRotation((N + toRotationInt(lhs) - toRotationInt(rhs)) % N);
+}
+
+constexpr Rotation operator-(Rotation rotation) {
+ return ROTATION_0 - rotation;
+}
+
constexpr const char* toCString(Rotation rotation) {
switch (rotation) {
case ROTATION_0:
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index 2612e82..4c463bf 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -104,8 +104,8 @@
Transform inverse() const;
// for debugging
- void dump(std::string& result, const char* name) const;
- void dump(const char* name) const;
+ void dump(std::string& result, const char* name, const char* prefix = "") const;
+ void dump(const char* name, const char* prefix = "") const;
static RotationFlags toRotationFlags(Rotation);
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index a27c09f..beca7f1 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -18,11 +18,11 @@
#include "BlobCache.h"
+#include <android-base/properties.h>
#include <errno.h>
#include <inttypes.h>
-
-#include <android-base/properties.h>
#include <log/log.h>
+
#include <chrono>
namespace android {
@@ -36,8 +36,8 @@
// BlobCache::Header::mDeviceVersion value
static const uint32_t blobCacheDeviceVersion = 1;
-BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
- mMaxTotalSize(maxTotalSize),
+BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize)
+ : mMaxTotalSize(maxTotalSize),
mMaxKeySize(maxKeySize),
mMaxValueSize(maxValueSize),
mTotalSize(0) {
@@ -52,21 +52,21 @@
ALOGV("initializing random seed using %lld", (unsigned long long)now);
}
-void BlobCache::set(const void* key, size_t keySize, const void* value,
- size_t valueSize) {
+void BlobCache::set(const void* key, size_t keySize, const void* value, size_t valueSize) {
if (mMaxKeySize < keySize) {
- ALOGV("set: not caching because the key is too large: %zu (limit: %zu)",
- keySize, mMaxKeySize);
+ ALOGV("set: not caching because the key is too large: %zu (limit: %zu)", keySize,
+ mMaxKeySize);
return;
}
if (mMaxValueSize < valueSize) {
- ALOGV("set: not caching because the value is too large: %zu (limit: %zu)",
- valueSize, mMaxValueSize);
+ ALOGV("set: not caching because the value is too large: %zu (limit: %zu)", valueSize,
+ mMaxValueSize);
return;
}
if (mMaxTotalSize < keySize + valueSize) {
ALOGV("set: not caching because the combined key/value size is too "
- "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize);
+ "large: %zu (limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
return;
}
if (keySize == 0) {
@@ -95,16 +95,16 @@
continue;
} else {
ALOGV("set: not caching new key/value pair because the "
- "total cache size limit would be exceeded: %zu "
- "(limit: %zu)",
- keySize + valueSize, mMaxTotalSize);
+ "total cache size limit would be exceeded: %zu "
+ "(limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
break;
}
}
mCacheEntries.insert(index, CacheEntry(keyBlob, valueBlob));
mTotalSize = newTotalSize;
- ALOGV("set: created new cache entry with %zu byte key and %zu byte value",
- keySize, valueSize);
+ ALOGV("set: created new cache entry with %zu byte key and %zu byte value", keySize,
+ valueSize);
} else {
// Update the existing cache entry.
std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true));
@@ -117,25 +117,25 @@
continue;
} else {
ALOGV("set: not caching new value because the total cache "
- "size limit would be exceeded: %zu (limit: %zu)",
- keySize + valueSize, mMaxTotalSize);
+ "size limit would be exceeded: %zu (limit: %zu)",
+ keySize + valueSize, mMaxTotalSize);
break;
}
}
index->setValue(valueBlob);
mTotalSize = newTotalSize;
ALOGV("set: updated existing cache entry with %zu byte key and %zu byte "
- "value", keySize, valueSize);
+ "value",
+ keySize, valueSize);
}
break;
}
}
-size_t BlobCache::get(const void* key, size_t keySize, void* value,
- size_t valueSize) {
+size_t BlobCache::get(const void* key, size_t keySize, void* value, size_t valueSize) {
if (mMaxKeySize < keySize) {
- ALOGV("get: not searching because the key is too large: %zu (limit %zu)",
- keySize, mMaxKeySize);
+ ALOGV("get: not searching because the key is too large: %zu (limit %zu)", keySize,
+ mMaxKeySize);
return 0;
}
std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false));
@@ -154,8 +154,8 @@
ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize);
memcpy(value, valueBlob->getData(), valueBlobSize);
} else {
- ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)",
- valueSize, valueBlobSize);
+ ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)", valueSize,
+ valueBlobSize);
}
return valueBlobSize;
}
@@ -167,7 +167,7 @@
size_t BlobCache::getFlattenedSize() const {
auto buildId = base::GetProperty("ro.build.id", "");
size_t size = align4(sizeof(Header) + buildId.size());
- for (const CacheEntry& e : mCacheEntries) {
+ for (const CacheEntry& e : mCacheEntries) {
std::shared_ptr<Blob> const& keyBlob = e.getKey();
std::shared_ptr<Blob> const& valueBlob = e.getValue();
size += align4(sizeof(EntryHeader) + keyBlob->getSize() + valueBlob->getSize());
@@ -193,7 +193,7 @@
// Write cache entries
uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
- for (const CacheEntry& e : mCacheEntries) {
+ for (const CacheEntry& e : mCacheEntries) {
std::shared_ptr<Blob> const& keyBlob = e.getKey();
std::shared_ptr<Blob> const& valueBlob = e.getValue();
size_t keySize = keyBlob->getSize();
@@ -259,8 +259,7 @@
return -EINVAL;
}
- const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
- &byteBuffer[byteOffset]);
+ const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(&byteBuffer[byteOffset]);
size_t keySize = eheader->mKeySize;
size_t valueSize = eheader->mValueSize;
size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
@@ -304,10 +303,8 @@
return mTotalSize > mMaxTotalSize / 2;
}
-BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) :
- mData(copyData ? malloc(size) : data),
- mSize(size),
- mOwnsData(copyData) {
+BlobCache::Blob::Blob(const void* data, size_t size, bool copyData)
+ : mData(copyData ? malloc(size) : data), mSize(size), mOwnsData(copyData) {
if (data != nullptr && copyData) {
memcpy(const_cast<void*>(mData), data, size);
}
@@ -335,19 +332,13 @@
return mSize;
}
-BlobCache::CacheEntry::CacheEntry() {
-}
+BlobCache::CacheEntry::CacheEntry() {}
-BlobCache::CacheEntry::CacheEntry(
- const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value):
- mKey(key),
- mValue(value) {
-}
+BlobCache::CacheEntry::CacheEntry(const std::shared_ptr<Blob>& key,
+ const std::shared_ptr<Blob>& value)
+ : mKey(key), mValue(value) {}
-BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
- mKey(ce.mKey),
- mValue(ce.mValue) {
-}
+BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce) : mKey(ce.mKey), mValue(ce.mValue) {}
bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
return *mKey < *rhs.mKey;
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index e5c5e5b..50b4e4c 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -54,8 +54,7 @@
// 0 < keySize
// value != NULL
// 0 < valueSize
- void set(const void* key, size_t keySize, const void* value,
- size_t valueSize);
+ void set(const void* key, size_t keySize, const void* value, size_t valueSize);
// get retrieves from the cache the binary value associated with a given
// binary key. If the key is present in the cache then the length of the
@@ -75,7 +74,6 @@
// 0 <= valueSize
size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
-
// getFlattenedSize returns the number of bytes needed to store the entire
// serialized cache.
size_t getFlattenedSize() const;
@@ -168,7 +166,6 @@
void setValue(const std::shared_ptr<Blob>& value);
private:
-
// mKey is the key that identifies the cache entry.
std::shared_ptr<Blob> mKey;
@@ -245,6 +242,6 @@
std::vector<CacheEntry> mCacheEntries;
};
-}
+} // namespace android
#endif // ANDROID_BLOB_CACHE_H
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
index cf67cf4..d31373b 100644
--- a/opengl/libs/EGL/BlobCache_test.cpp
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -14,25 +14,24 @@
** limitations under the License.
*/
+#include "BlobCache.h"
+
#include <fcntl.h>
+#include <gtest/gtest.h>
#include <stdio.h>
#include <memory>
-#include <gtest/gtest.h>
-
-#include "BlobCache.h"
-
namespace android {
-template<typename T> using sp = std::shared_ptr<T>;
+template <typename T>
+using sp = std::shared_ptr<T>;
class BlobCacheTest : public ::testing::Test {
protected:
-
enum {
OK = 0,
- BAD_VALUE = -EINVAL
+ BAD_VALUE = -EINVAL,
};
enum {
@@ -41,19 +40,15 @@
MAX_TOTAL_SIZE = 13,
};
- virtual void SetUp() {
- mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
- }
+ virtual void SetUp() { mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE)); }
- virtual void TearDown() {
- mBC.reset();
- }
+ virtual void TearDown() { mBC.reset(); }
std::unique_ptr<BlobCache> mBC;
};
TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
ASSERT_EQ('e', buf[0]);
@@ -63,7 +58,7 @@
}
TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
- unsigned char buf[2] = { 0xee, 0xee };
+ unsigned char buf[2] = {0xee, 0xee};
mBC->set("ab", 2, "cd", 2);
mBC->set("ef", 2, "gh", 2);
ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
@@ -75,9 +70,9 @@
}
TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
- unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[6] = {0xee, 0xee, 0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf + 1, 4));
ASSERT_EQ(0xee, buf[0]);
ASSERT_EQ('e', buf[1]);
ASSERT_EQ('f', buf[2]);
@@ -87,7 +82,7 @@
}
TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
- unsigned char buf[3] = { 0xee, 0xee, 0xee };
+ unsigned char buf[3] = {0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
ASSERT_EQ(0xee, buf[0]);
@@ -101,7 +96,7 @@
}
TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
mBC->set("abcd", 4, "ijkl", 4);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
@@ -112,9 +107,9 @@
}
TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
- unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[MAX_VALUE_SIZE + 1] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1);
ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
ASSERT_EQ('e', buf[0]);
ASSERT_EQ('f', buf[1]);
@@ -123,13 +118,13 @@
}
TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
- char key[MAX_KEY_SIZE+1];
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
+ char key[MAX_KEY_SIZE + 1];
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
+ for (int i = 0; i < MAX_KEY_SIZE + 1; i++) {
key[i] = 'a';
}
- mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
- ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
+ mBC->set(key, MAX_KEY_SIZE + 1, "bbbb", 4);
+ ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE + 1, buf, 4));
ASSERT_EQ(0xee, buf[0]);
ASSERT_EQ(0xee, buf[1]);
ASSERT_EQ(0xee, buf[2]);
@@ -137,16 +132,16 @@
}
TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
- char buf[MAX_VALUE_SIZE+1];
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ char buf[MAX_VALUE_SIZE + 1];
+ for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
buf[i] = 'b';
}
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1);
+ for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
buf[i] = 0xee;
}
- ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE + 1));
+ for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
SCOPED_TRACE(i);
ASSERT_EQ(0xee, buf[i]);
}
@@ -174,7 +169,7 @@
TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
char key[MAX_KEY_SIZE];
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
for (int i = 0; i < MAX_KEY_SIZE; i++) {
key[i] = 'a';
}
@@ -195,8 +190,7 @@
for (int i = 0; i < MAX_VALUE_SIZE; i++) {
buf[i] = 0xee;
}
- ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
- MAX_VALUE_SIZE));
+ ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE));
for (int i = 0; i < MAX_VALUE_SIZE; i++) {
SCOPED_TRACE(i);
ASSERT_EQ('b', buf[i]);
@@ -223,7 +217,7 @@
}
TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
- unsigned char buf[1] = { 0xee };
+ unsigned char buf[1] = {0xee};
mBC->set("x", 1, "y", 1);
ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
ASSERT_EQ('y', buf[0]);
@@ -258,13 +252,13 @@
}
// Count the number of entries in the cache.
int numCached = 0;
- for (int i = 0; i < maxEntries+1; i++) {
+ for (int i = 0; i < maxEntries + 1; i++) {
uint8_t k = i;
if (mBC->get(&k, 1, nullptr, 0) == 1) {
numCached++;
}
}
- ASSERT_EQ(maxEntries/2 + 1, numCached);
+ ASSERT_EQ(maxEntries / 2 + 1, numCached);
}
class BlobCacheFlattenTest : public BlobCacheTest {
@@ -291,7 +285,7 @@
};
TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
roundTrip();
ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
@@ -359,7 +353,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
@@ -376,7 +370,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
@@ -395,7 +389,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
@@ -414,7 +408,7 @@
}
TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mBC->set("abcd", 4, "efgh", 4);
size_t size = mBC->getFlattenedSize();
diff --git a/opengl/libs/EGL/CallStack.h b/opengl/libs/EGL/CallStack.h
index 0e2a9b3..b7fdf97 100644
--- a/opengl/libs/EGL/CallStack.h
+++ b/opengl/libs/EGL/CallStack.h
@@ -16,8 +16,9 @@
#pragma once
-#include <log/log.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
+
#include <memory>
class CallStack {
@@ -30,9 +31,8 @@
if (backtrace->Unwind(2)) {
for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) {
__android_log_print(ANDROID_LOG_DEBUG, logtag, "%s",
- backtrace->FormatFrameData(i).c_str());
+ backtrace->FormatFrameData(i).c_str());
}
}
}
};
-
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 7b2d7c9..81742ab 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -1,29 +1,26 @@
-/*
+/*
** Copyright 2009, The Android Open Source Project
**
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
**
- ** http://www.apache.org/licenses/LICENSE-2.0
+ ** http://www.apache.org/licenses/LICENSE-2.0
**
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
** limitations under the License.
*/
#ifndef ANDROID_EGL_LOADER_H
#define ANDROID_EGL_LOADER_H
+#include <EGL/egl.h>
#include <stdint.h>
-#include <EGL/egl.h>
-
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
struct egl_connection_t;
@@ -62,16 +59,12 @@
void initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask);
void init_angle_backend(void* dso, egl_connection_t* cnx);
- static __attribute__((noinline))
- void init_api(void* dso,
- char const * const * api,
- char const * const * ref_api,
- __eglMustCastToProperFunctionPointerType* curr,
- getProcAddressType getProcAddress);
+ static __attribute__((noinline)) void init_api(void* dso, const char* const* api,
+ const char* const* ref_api,
+ __eglMustCastToProperFunctionPointerType* curr,
+ getProcAddressType getProcAddress);
};
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif /* ANDROID_EGL_LOADER_H */
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 43f7a07..e5b9e14 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -14,45 +14,35 @@
** limitations under the License.
*/
+#include <EGL/egl.h>
+#include <android-base/properties.h>
+#include <log/log.h>
#include <stdlib.h>
-#include <EGL/egl.h>
-
-#include <android-base/properties.h>
-
-#include <log/log.h>
-
#include "../egl_impl.h"
-
-#include "egldefs.h"
-#include "egl_tls.h"
-#include "egl_display.h"
-#include "egl_object.h"
-#include "egl_layers.h"
#include "CallStack.h"
#include "Loader.h"
+#include "egl_display.h"
+#include "egl_layers.h"
+#include "egl_object.h"
+#include "egl_tls.h"
+#include "egldefs.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
egl_connection_t gEGLImpl;
gl_hooks_t gHooks[2];
gl_hooks_t gHooksNoContext;
pthread_key_t gGLWrapperKey = -1;
-// ----------------------------------------------------------------------------
-
-void setGLHooksThreadSpecific(gl_hooks_t const *value) {
+void setGLHooksThreadSpecific(gl_hooks_t const* value) {
setGlThreadSpecific(value);
}
-/*****************************************************************************/
-
static int gl_no_context() {
if (egl_tls_t::logNoContextCall()) {
- char const* const error = "call to OpenGL ES API with "
- "no current context (logged once per thread)";
+ const char* const error = "call to OpenGL ES API with "
+ "no current context (logged once per thread)";
if (LOG_NDEBUG) {
ALOGE(error);
} else {
@@ -65,10 +55,9 @@
return 0;
}
-static void early_egl_init(void)
-{
+static void early_egl_init(void) {
int numHooks = sizeof(gHooksNoContext) / sizeof(EGLFuncPointer);
- EGLFuncPointer *iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext);
+ EGLFuncPointer* iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext);
for (int hook = 0; hook < numHooks; ++hook) {
*(iter++) = reinterpret_cast<EGLFuncPointer>(gl_no_context);
}
@@ -76,75 +65,40 @@
setGLHooksThreadSpecific(&gHooksNoContext);
}
-static pthread_once_t once_control = PTHREAD_ONCE_INIT;
-static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
-
-// ----------------------------------------------------------------------------
-
-egl_display_ptr validate_display(EGLDisplay dpy) {
- egl_display_ptr dp = get_display(dpy);
- if (!dp)
- return setError(EGL_BAD_DISPLAY, egl_display_ptr(nullptr));
- if (!dp->isReady())
- return setError(EGL_NOT_INITIALIZED, egl_display_ptr(nullptr));
-
- return dp;
-}
-
-egl_display_ptr validate_display_connection(EGLDisplay dpy,
- egl_connection_t*& cnx) {
- cnx = nullptr;
- egl_display_ptr dp = validate_display(dpy);
- if (!dp)
- return dp;
- cnx = &gEGLImpl;
- if (cnx->dso == nullptr) {
- return setError(EGL_BAD_CONFIG, egl_display_ptr(nullptr));
- }
- return dp;
-}
-
-// ----------------------------------------------------------------------------
-
-const GLubyte * egl_get_string_for_current_context(GLenum name) {
+const GLubyte* egl_get_string_for_current_context(GLenum name) {
// NOTE: returning NULL here will fall-back to the default
// implementation.
EGLContext context = egl_tls_t::getContext();
- if (context == EGL_NO_CONTEXT)
- return nullptr;
+ if (context == EGL_NO_CONTEXT) return nullptr;
- egl_context_t const * const c = get_context(context);
+ const egl_context_t* const c = get_context(context);
if (c == nullptr) // this should never happen, by construction
return nullptr;
- if (name != GL_EXTENSIONS)
- return nullptr;
+ if (name != GL_EXTENSIONS) return nullptr;
- return (const GLubyte *)c->gl_extensions.c_str();
+ return (const GLubyte*)c->gl_extensions.c_str();
}
-const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index) {
+const GLubyte* egl_get_string_for_current_context(GLenum name, GLuint index) {
// NOTE: returning NULL here will fall-back to the default
// implementation.
EGLContext context = egl_tls_t::getContext();
- if (context == EGL_NO_CONTEXT)
- return nullptr;
+ if (context == EGL_NO_CONTEXT) return nullptr;
- egl_context_t const * const c = get_context(context);
+ const egl_context_t* const c = get_context(context);
if (c == nullptr) // this should never happen, by construction
return nullptr;
- if (name != GL_EXTENSIONS)
- return nullptr;
+ if (name != GL_EXTENSIONS) return nullptr;
// if index is out of bounds, assume it will be in the default
// implementation too, so we don't have to generate a GL error here
- if (index >= c->tokenized_gl_extensions.size())
- return nullptr;
+ if (index >= c->tokenized_gl_extensions.size()) return nullptr;
- return (const GLubyte *)c->tokenized_gl_extensions[index].c_str();
+ return (const GLubyte*)c->tokenized_gl_extensions[index].c_str();
}
GLint egl_get_num_extensions_for_current_context() {
@@ -152,10 +106,9 @@
// implementation.
EGLContext context = egl_tls_t::getContext();
- if (context == EGL_NO_CONTEXT)
- return -1;
+ if (context == EGL_NO_CONTEXT) return -1;
- egl_context_t const * const c = get_context(context);
+ const egl_context_t* const c = get_context(context);
if (c == nullptr) // this should never happen, by construction
return -1;
@@ -166,7 +119,8 @@
return &gEGLImpl;
}
-// ----------------------------------------------------------------------------
+static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
static EGLBoolean egl_init_drivers_locked() {
if (sEarlyInitState) {
@@ -194,7 +148,6 @@
return cnx->dso ? EGL_TRUE : EGL_FALSE;
}
-
// this mutex protects driver load logic as a critical section since it accesses to global variable
// like gEGLImpl
static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
@@ -228,13 +181,10 @@
}
}
-void gl_noop() {
-}
+void gl_noop() {}
-// ----------------------------------------------------------------------------
-
-void setGlThreadSpecific(gl_hooks_t const *value) {
- gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
+void setGlThreadSpecific(gl_hooks_t const* value) {
+ gl_hooks_t const* volatile* tls_hooks = get_tls_hooks();
tls_hooks[TLS_SLOT_OPENGL_API] = value;
}
@@ -270,8 +220,4 @@
#undef GL_ENTRY
#undef EGL_ENTRY
-
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
-
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index c51a129..502c14f 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -20,7 +20,6 @@
#include <EGL/eglext.h>
#include "../egl_impl.h"
-
#include "egl_layers.h"
#include "egl_platform_entries.h"
#include "egl_tls.h"
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index 97dc0f1..dc8e587 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -16,22 +16,26 @@
#if defined(__ANDROID__)
-#include "Loader.h"
#include "egl_angle_platform.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <EGL/Platform.h>
#pragma GCC diagnostic pop
-
#include <android/dlext.h>
#include <dlfcn.h>
#include <graphicsenv/GraphicsEnv.h>
-#include <time.h>
#include <log/log.h>
+#include <time.h>
+#include <vndksupport/linker.h>
+
+#include "Loader.h"
namespace angle {
+constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so";
+constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW;
+
static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr;
static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr;
@@ -101,11 +105,22 @@
bool initializeAnglePlatform(EGLDisplay dpy) {
// Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform
android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE,
- .library_namespace = ns,
- };
- void* so = android_dlopen_ext("libGLESv2_angle.so", RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+ void* so = nullptr;
+ if (ns) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = ns,
+ };
+ so = android_dlopen_ext(kAngleEs2Lib, kAngleDlFlags, &dlextinfo);
+ } else {
+ // If we are here, ANGLE is loaded as built-in gl driver in the sphal.
+ so = android_load_sphal_library(kAngleEs2Lib, kAngleDlFlags);
+ }
+ if (!so) {
+ ALOGE("%s failed to dlopen %s!", __FUNCTION__, kAngleEs2Lib);
+ return false;
+ }
+
angleGetDisplayPlatform =
reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform"));
@@ -114,14 +129,12 @@
return false;
}
- angleResetDisplayPlatform =
- reinterpret_cast<ResetDisplayPlatformFunc>(
- eglGetProcAddress("ANGLEResetDisplayPlatform"));
+ angleResetDisplayPlatform = reinterpret_cast<ResetDisplayPlatformFunc>(
+ eglGetProcAddress("ANGLEResetDisplayPlatform"));
PlatformMethods* platformMethods = nullptr;
- if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames,
- g_NumPlatformMethods, nullptr,
- &platformMethods))) {
+ if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr,
+ &platformMethods))) {
ALOGE("ANGLEGetDisplayPlatform call failed!");
return false;
}
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index bcf4961..efa67db 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -16,17 +16,14 @@
#include "egl_cache.h"
-#include "../egl_impl.h"
-
-#include "egl_display.h"
-
+#include <log/log.h>
#include <private/EGL/cache.h>
-
#include <unistd.h>
#include <thread>
-#include <log/log.h>
+#include "../egl_impl.h"
+#include "egl_display.h"
// Cache size limits.
static const size_t maxKeySize = 12 * 1024;
@@ -36,9 +33,7 @@
// The time in seconds to wait before saving newly inserted cache entries.
static const unsigned int deferredSaveDelay = 4;
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
#define BC_EXT_STR "EGL_ANDROID_blob_cache"
@@ -50,25 +45,22 @@
//
// Callback functions passed to EGL.
//
-static void setBlob(const void* key, EGLsizeiANDROID keySize,
- const void* value, EGLsizeiANDROID valueSize) {
+static void setBlob(const void* key, EGLsizeiANDROID keySize, const void* value,
+ EGLsizeiANDROID valueSize) {
egl_cache_t::get()->setBlob(key, keySize, value, valueSize);
}
-static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize,
- void* value, EGLsizeiANDROID valueSize) {
+static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize, void* value,
+ EGLsizeiANDROID valueSize) {
return egl_cache_t::get()->getBlob(key, keySize, value, valueSize);
}
//
// egl_cache_t definition
//
-egl_cache_t::egl_cache_t() :
- mInitialized(false) {
-}
+egl_cache_t::egl_cache_t() : mInitialized(false) {}
-egl_cache_t::~egl_cache_t() {
-}
+egl_cache_t::~egl_cache_t() {}
egl_cache_t egl_cache_t::sCache;
@@ -76,7 +68,7 @@
return &sCache;
}
-void egl_cache_t::initialize(egl_display_t *display) {
+void egl_cache_t::initialize(egl_display_t* display) {
std::lock_guard<std::mutex> lock(mMutex);
egl_connection_t* const cnx = &gEGLImpl;
@@ -85,28 +77,26 @@
size_t bcExtLen = strlen(BC_EXT_STR);
size_t extsLen = strlen(exts);
bool equal = !strcmp(BC_EXT_STR, exts);
- bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
- bool atEnd = (bcExtLen+1) < extsLen &&
- !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
+ bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen + 1);
+ bool atEnd = (bcExtLen + 1) < extsLen &&
+ !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen + 1));
bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr;
if (equal || atStart || atEnd || inMiddle) {
PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
- eglSetBlobCacheFuncsANDROID =
- reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
- cnx->egl.eglGetProcAddress(
- "eglSetBlobCacheFuncsANDROID"));
+ eglSetBlobCacheFuncsANDROID = reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
+ cnx->egl.eglGetProcAddress("eglSetBlobCacheFuncsANDROID"));
if (eglSetBlobCacheFuncsANDROID == nullptr) {
ALOGE("EGL_ANDROID_blob_cache advertised, "
- "but unable to get eglSetBlobCacheFuncsANDROID");
+ "but unable to get eglSetBlobCacheFuncsANDROID");
return;
}
- eglSetBlobCacheFuncsANDROID(display->disp.dpy,
- android::setBlob, android::getBlob);
+ eglSetBlobCacheFuncsANDROID(display->disp.dpy, android::setBlob, android::getBlob);
EGLint err = cnx->egl.eglGetError();
if (err != EGL_SUCCESS) {
ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: "
- "%#x", err);
+ "%#x",
+ err);
}
}
}
@@ -122,8 +112,8 @@
mBlobCache = nullptr;
}
-void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
- const void* value, EGLsizeiANDROID valueSize) {
+void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, const void* value,
+ EGLsizeiANDROID valueSize) {
std::lock_guard<std::mutex> lock(mMutex);
if (keySize < 0 || valueSize < 0) {
@@ -150,8 +140,8 @@
}
}
-EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize,
- void* value, EGLsizeiANDROID valueSize) {
+EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, void* value,
+ EGLsizeiANDROID valueSize) {
std::lock_guard<std::mutex> lock(mMutex);
if (keySize < 0 || valueSize < 0) {
@@ -178,6 +168,4 @@
return mBlobCache.get();
}
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 7382b91..d10a615 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -20,21 +20,18 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include "FileBlobCache.h"
-
#include <memory>
#include <mutex>
#include <string>
-// ----------------------------------------------------------------------------
+#include "FileBlobCache.h"
+
namespace android {
-// ----------------------------------------------------------------------------
class egl_display_t;
class EGLAPI egl_cache_t {
public:
-
// get returns a pointer to the singleton egl_cache_t object. This
// singleton object will never be destroyed.
static egl_cache_t* get();
@@ -117,8 +114,6 @@
static egl_cache_t sCache;
};
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_CACHE_H
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 8c6f284..07ec327 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -14,7 +14,6 @@
** limitations under the License.
*/
-#define __STDC_LIMIT_MACROS 1
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "egl_display.h"
@@ -39,21 +38,17 @@
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
-static char const * const sVendorString = "Android";
-static char const* const sVersionString14 = "1.4 Android META-EGL";
-static char const* const sVersionString15 = "1.5 Android META-EGL";
-static char const * const sClientApiString = "OpenGL_ES";
+static const char* const sVendorString = "Android";
+static const char* const sVersionString14 = "1.4 Android META-EGL";
+static const char* const sVersionString15 = "1.5 Android META-EGL";
+static const char* const sClientApiString = "OpenGL_ES";
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
+extern const char* const gBuiltinExtensionString;
+extern const char* const gExtensionString;
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
-
-// ----------------------------------------------------------------------------
+extern void setGLHooksThreadSpecific(gl_hooks_t const* value);
bool findExtension(const char* exts, const char* name, size_t nameLen) {
if (exts) {
@@ -81,9 +76,12 @@
egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
-egl_display_t::egl_display_t() :
- magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0), eglIsInitialized(false) {
-}
+egl_display_t::egl_display_t()
+ : magic('_dpy'),
+ finishOnSwap(false),
+ traceGpuCompletion(false),
+ refs(0),
+ eglIsInitialized(false) {}
egl_display_t::~egl_display_t() {
magic = 0;
@@ -95,7 +93,7 @@
return nullptr;
}
- uintptr_t index = uintptr_t(dpy)-1U;
+ uintptr_t index = uintptr_t(dpy) - 1U;
if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) {
return nullptr;
}
@@ -125,8 +123,7 @@
EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp,
const EGLAttrib* attrib_list) {
- if (uintptr_t(disp) >= NUM_DISPLAYS)
- return nullptr;
+ if (uintptr_t(disp) >= NUM_DISPLAYS) return nullptr;
return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
}
@@ -201,7 +198,7 @@
// It is possible that eglGetPlatformDisplay does not have a
// working implementation for Android platform; in that case,
// one last fallback to eglGetDisplay
- if(dpy == EGL_NO_DISPLAY) {
+ if (dpy == EGL_NO_DISPLAY) {
if (attrib_list) {
ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored");
}
@@ -218,8 +215,7 @@
return EGLDisplay(uintptr_t(display) + 1U);
}
-EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) {
-
+EGLBoolean egl_display_t::initialize(EGLint* major, EGLint* minor) {
{ // scope for refLock
std::unique_lock<std::mutex> _l(refLock);
refs++;
@@ -227,7 +223,7 @@
// We don't know what to report until we know what the
// driver supports. Make sure we are initialized before
// returning the version info.
- while(!eglIsInitialized) {
+ while (!eglIsInitialized) {
refCond.wait(_l);
}
egl_connection_t* const cnx = &gEGLImpl;
@@ -240,7 +236,7 @@
if (minor != nullptr) *minor = cnx->minor;
return EGL_TRUE;
}
- while(eglIsInitialized) {
+ while (eglIsInitialized) {
refCond.wait(_l);
}
}
@@ -260,40 +256,31 @@
if (cnx->dso) {
EGLDisplay idpy = disp.dpy;
if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) {
- //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
+ // ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
// idpy, cnx->major, cnx->minor, cnx);
// display is now initialized
disp.state = egl_display_t::INITIALIZED;
// get the query-strings for this display for each implementation
- disp.queryString.vendor = cnx->egl.eglQueryString(idpy,
- EGL_VENDOR);
- disp.queryString.version = cnx->egl.eglQueryString(idpy,
- EGL_VERSION);
- disp.queryString.extensions = cnx->egl.eglQueryString(idpy,
- EGL_EXTENSIONS);
- disp.queryString.clientApi = cnx->egl.eglQueryString(idpy,
- EGL_CLIENT_APIS);
+ disp.queryString.vendor = cnx->egl.eglQueryString(idpy, EGL_VENDOR);
+ disp.queryString.version = cnx->egl.eglQueryString(idpy, EGL_VERSION);
+ disp.queryString.extensions = cnx->egl.eglQueryString(idpy, EGL_EXTENSIONS);
+ disp.queryString.clientApi = cnx->egl.eglQueryString(idpy, EGL_CLIENT_APIS);
} else {
ALOGW("eglInitialize(%p) failed (%s)", idpy,
- egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
+ egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
}
}
if (cnx->minor == 5) {
// full list in egl_entries.in
- if (!cnx->egl.eglCreateImage ||
- !cnx->egl.eglDestroyImage ||
- !cnx->egl.eglGetPlatformDisplay ||
- !cnx->egl.eglCreatePlatformWindowSurface ||
- !cnx->egl.eglCreatePlatformPixmapSurface ||
- !cnx->egl.eglCreateSync ||
- !cnx->egl.eglDestroySync ||
- !cnx->egl.eglClientWaitSync ||
- !cnx->egl.eglGetSyncAttrib ||
- !cnx->egl.eglWaitSync) {
+ if (!cnx->egl.eglCreateImage || !cnx->egl.eglDestroyImage ||
+ !cnx->egl.eglGetPlatformDisplay || !cnx->egl.eglCreatePlatformWindowSurface ||
+ !cnx->egl.eglCreatePlatformPixmapSurface || !cnx->egl.eglCreateSync ||
+ !cnx->egl.eglDestroySync || !cnx->egl.eglClientWaitSync ||
+ !cnx->egl.eglGetSyncAttrib || !cnx->egl.eglWaitSync) {
ALOGE("Driver indicates EGL 1.5 support, but does not have "
"a critical API");
cnx->minor = 4;
@@ -388,7 +375,6 @@
}
EGLBoolean egl_display_t::terminate() {
-
{ // scope for refLock
std::unique_lock<std::mutex> _rl(refLock);
if (refs == 0) {
@@ -421,7 +407,7 @@
}
if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
- egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
+ egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
}
// REVISIT: it's unclear what to do if eglTerminate() fails
disp.state = egl_display_t::TERMINATED;
@@ -454,8 +440,7 @@
return res;
}
-void egl_display_t::loseCurrent(egl_context_t * cur_c)
-{
+void egl_display_t::loseCurrent(egl_context_t* cur_c) {
if (cur_c) {
egl_display_t* display = cur_c->getDisplay();
if (display) {
@@ -464,8 +449,7 @@
}
}
-void egl_display_t::loseCurrentImpl(egl_context_t * cur_c)
-{
+void egl_display_t::loseCurrentImpl(egl_context_t* cur_c) {
// by construction, these are either 0 or valid (possibly terminated)
// it should be impossible for these to be invalid
ContextRef _cur_c(cur_c);
@@ -475,7 +459,6 @@
{ // scope for the lock
std::lock_guard<std::mutex> _l(lock);
cur_c->onLooseCurrent();
-
}
// This cannot be called with the lock held because it might end-up
@@ -486,10 +469,9 @@
_cur_d.release();
}
-EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
- EGLSurface draw, EGLSurface read, EGLContext /*ctx*/,
- EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx)
-{
+EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw,
+ EGLSurface read, EGLContext /*ctx*/, EGLSurface impl_draw,
+ EGLSurface impl_read, EGLContext impl_ctx) {
EGLBoolean result;
// by construction, these are either 0 or valid (possibly terminated)
@@ -501,14 +483,12 @@
{ // scope for the lock
std::lock_guard<std::mutex> _l(lock);
if (c) {
- result = c->cnx->egl.eglMakeCurrent(
- disp.dpy, impl_draw, impl_read, impl_ctx);
+ result = c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
c->onMakeCurrent(draw, read);
}
} else {
- result = cur_c->cnx->egl.eglMakeCurrent(
- disp.dpy, impl_draw, impl_read, impl_ctx);
+ result = cur_c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
cur_c->onLooseCurrent();
}
@@ -534,6 +514,23 @@
return findExtension(mExtensionString.c_str(), name, nameLen);
}
-// ----------------------------------------------------------------------------
+egl_display_t* validate_display(EGLDisplay dpy) {
+ egl_display_t* const dp = get_display(dpy);
+ if (!dp) return setError(EGL_BAD_DISPLAY, (egl_display_t*)nullptr);
+ if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (egl_display_t*)nullptr);
+
+ return dp;
+}
+
+egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx) {
+ *outCnx = nullptr;
+ egl_display_t* dp = validate_display(dpy);
+ if (!dp) return dp;
+ *outCnx = &gEGLImpl;
+ if ((*outCnx)->dso == nullptr) {
+ return setError(EGL_BAD_CONFIG, (egl_display_t*)nullptr);
+ }
+ return dp;
+}
+
}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index e117314..0155133 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -17,26 +17,20 @@
#ifndef ANDROID_EGL_DISPLAY_H
#define ANDROID_EGL_DISPLAY_H
-
-#include <stdint.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
#include <stddef.h>
+#include <stdint.h>
#include <condition_variable>
#include <mutex>
#include <string>
#include <unordered_set>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <cutils/compiler.h>
-
-#include "egldefs.h"
#include "../hooks.h"
+#include "egldefs.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
class egl_object_t;
class egl_context_t;
@@ -45,25 +39,23 @@
bool findExtension(const char* exts, const char* name, size_t nameLen = 0);
bool needsAndroidPEglMitigation();
-// ----------------------------------------------------------------------------
-
class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
static egl_display_t sDisplay[NUM_DISPLAYS];
EGLDisplay getDisplay(EGLNativeDisplayType display);
EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list);
- void loseCurrentImpl(egl_context_t * cur_c);
+ void loseCurrentImpl(egl_context_t* cur_c);
public:
enum {
NOT_INITIALIZED = 0,
- INITIALIZED = 1,
- TERMINATED = 2
+ INITIALIZED = 1,
+ TERMINATED = 2,
};
egl_display_t();
~egl_display_t();
- EGLBoolean initialize(EGLint *major, EGLint *minor);
+ EGLBoolean initialize(EGLint* major, EGLint* minor);
EGLBoolean terminate();
// add object to this display's list
@@ -76,123 +68,69 @@
static egl_display_t* get(EGLDisplay dpy);
static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list);
- EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c,
- EGLSurface draw, EGLSurface read, EGLContext ctx,
- EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx);
- static void loseCurrent(egl_context_t * cur_c);
+ EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw, EGLSurface read,
+ EGLContext ctx, EGLSurface impl_draw, EGLSurface impl_read,
+ EGLContext impl_ctx);
+ static void loseCurrent(egl_context_t* cur_c);
inline bool isReady() const { return (refs > 0); }
inline bool isValid() const { return magic == '_dpy'; }
inline bool isAlive() const { return isValid(); }
- char const * getVendorString() const { return mVendorString.c_str(); }
- char const * getVersionString() const { return mVersionString.c_str(); }
- char const * getClientApiString() const { return mClientApiString.c_str(); }
- char const * getExtensionString() const { return mExtensionString.c_str(); }
+ char const* getVendorString() const { return mVendorString.c_str(); }
+ char const* getVersionString() const { return mVersionString.c_str(); }
+ char const* getClientApiString() const { return mClientApiString.c_str(); }
+ char const* getExtensionString() const { return mExtensionString.c_str(); }
bool haveExtension(const char* name, size_t nameLen = 0) const;
inline uint32_t getRefsCount() const { return refs; }
struct strings_t {
- char const * vendor;
- char const * version;
- char const * clientApi;
- char const * extensions;
+ char const* vendor;
+ char const* version;
+ char const* clientApi;
+ char const* extensions;
};
struct DisplayImpl {
- DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) { }
- EGLDisplay dpy;
- EGLint state;
- strings_t queryString;
+ DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) {}
+ EGLDisplay dpy;
+ EGLint state;
+ strings_t queryString;
};
private:
- uint32_t magic;
+ uint32_t magic;
public:
- DisplayImpl disp;
- bool finishOnSwap; // property: debug.egl.finish
- bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion
- bool hasColorSpaceSupport;
+ DisplayImpl disp;
+ bool finishOnSwap; // property: debug.egl.finish
+ bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion
+ bool hasColorSpaceSupport;
private:
- friend class egl_display_ptr;
-
- uint32_t refs;
- bool eglIsInitialized;
- mutable std::mutex lock;
- mutable std::mutex refLock;
- mutable std::condition_variable refCond;
- std::unordered_set<egl_object_t*> objects;
- std::string mVendorString;
- std::string mVersionString;
- std::string mClientApiString;
- std::string mExtensionString;
+ uint32_t refs;
+ bool eglIsInitialized;
+ mutable std::mutex lock;
+ mutable std::mutex refLock;
+ mutable std::condition_variable refCond;
+ std::unordered_set<egl_object_t*> objects;
+ std::string mVendorString;
+ std::string mVersionString;
+ std::string mClientApiString;
+ std::string mExtensionString;
};
-// ----------------------------------------------------------------------------
-
-// An egl_display_ptr is a kind of smart pointer for egl_display_t objects.
-// It doesn't refcount the egl_display_t, but does ensure that the underlying
-// EGL implementation is "awake" (not hibernating) and ready for use as long
-// as the egl_display_ptr exists.
-class egl_display_ptr {
-public:
- explicit egl_display_ptr(egl_display_t* dpy): mDpy(dpy) {}
-
- // We only really need a C++11 move constructor, not a copy constructor.
- // A move constructor would save an enter()/leave() pair on every EGL API
- // call. But enabling -std=c++0x causes lots of errors elsewhere, so I
- // can't use a move constructor until those are cleaned up.
- //
- // egl_display_ptr(egl_display_ptr&& other) {
- // mDpy = other.mDpy;
- // other.mDpy = NULL;
- // }
- //
- egl_display_ptr(const egl_display_ptr& other): mDpy(other.mDpy) {}
-
- ~egl_display_ptr() {}
-
- const egl_display_t* operator->() const { return mDpy; }
- egl_display_t* operator->() { return mDpy; }
-
- const egl_display_t* get() const { return mDpy; }
- egl_display_t* get() { return mDpy; }
-
- operator bool() const { return mDpy != nullptr; }
-
-private:
- egl_display_t* mDpy;
-
- // non-assignable
- egl_display_ptr& operator=(const egl_display_ptr&);
-};
-
-// ----------------------------------------------------------------------------
-
-inline egl_display_ptr get_display(EGLDisplay dpy) {
- return egl_display_ptr(egl_display_t::get(dpy));
-}
-
-// Does not ensure EGL is unhibernated. Use with caution: calls into the
-// underlying EGL implementation are not safe.
-inline egl_display_t* get_display_nowake(EGLDisplay dpy) {
+inline egl_display_t* get_display(EGLDisplay dpy) {
return egl_display_t::get(dpy);
}
-// ----------------------------------------------------------------------------
-
-egl_display_ptr validate_display(EGLDisplay dpy);
-egl_display_ptr validate_display_connection(EGLDisplay dpy,
- egl_connection_t*& cnx);
+egl_display_t* validate_display(EGLDisplay dpy);
+egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx);
EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx);
EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface);
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_DISPLAY_H
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 2921d51..1c91f1d 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -104,11 +104,6 @@
EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
-/* IMG extensions */
-
-EGL_ENTRY(EGLBoolean, eglHibernateProcessIMG, void)
-EGL_ENTRY(EGLBoolean, eglAwakenProcessIMG, void)
-
/* Partial update extensions */
EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index ea86c9a..9752e38 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -85,17 +85,21 @@
// Look up which GPA we should use
int gpaIndex = func_indices["eglGetProcAddress"];
- ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, gpaIndex);
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name,
+ gpaIndex);
EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
- ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this address", name, gpaIndex, (unsigned long long)gpaNext);
-
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this "
+ "address",
+ name, gpaIndex, (unsigned long long)gpaNext);
// Call it for the requested function
typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
val = reinterpret_cast<EGLFuncPointer>(next(name));
- ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from GPA", name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from "
+ "GPA",
+ name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
// We should store it now, but to do that, we need to move func_idx to the class so we can
// increment it separately
@@ -105,7 +109,9 @@
int index = func_indices[name];
val = (*next_layer_funcs)[index];
- ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known entry", name, index, (unsigned long long)val);
+ ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known "
+ "entry",
+ name, index, (unsigned long long)val);
return reinterpret_cast<void*>(val);
}
@@ -117,20 +123,26 @@
// Some names overlap, only fill with initial entry
// This does mean that some indices will not be used
if (func_indices.find(name) == func_indices.end()) {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning now", name, func_idx);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning "
+ "now",
+ name, func_idx);
func_names[func_idx] = name;
func_indices[name] = func_idx;
} else {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name, func_idx);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name,
+ func_idx);
}
// Populate layer_functions once with initial value
// These values will arrive in priority order, starting with platform entries
if (functions[func_idx] == nullptr) {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning (%llu)", name, func_idx, (unsigned long long) *curr);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning "
+ "(%llu)",
+ name, func_idx, (unsigned long long)*curr);
functions[func_idx] = *curr;
} else {
- ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, func_idx, (unsigned long long) functions[func_idx]);
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name,
+ func_idx, (unsigned long long)functions[func_idx]);
}
entries++;
@@ -380,8 +392,8 @@
auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
char* error_message = nullptr;
- dlhandle_ = OpenNativeLibraryInNamespace(
- app_namespace, layer.c_str(), &native_bridge_, &error_message);
+ dlhandle_ = OpenNativeLibraryInNamespace(app_namespace, layer.c_str(),
+ &native_bridge_, &error_message);
if (!dlhandle_) {
ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
error_message);
diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h
index 1e2783f..705525d 100644
--- a/opengl/libs/EGL/egl_layers.h
+++ b/opengl/libs/EGL/egl_layers.h
@@ -17,19 +17,18 @@
#ifndef ANDROID_EGL_LAYERS_H
#define ANDROID_EGL_LAYERS_H
+#include <EGL/egldefs.h>
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <nativebridge/native_bridge.h>
+#include <nativeloader/native_loader.h>
+
#include <string>
#include <unordered_map>
#include <vector>
-#include <android/dlext.h>
-#include <dlfcn.h>
-
-#include <EGL/egldefs.h>
#include "egl_platform_entries.h"
-#include <nativebridge/native_bridge.h>
-#include <nativeloader/native_loader.h>
-
typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
namespace android {
@@ -46,8 +45,8 @@
void LoadLayers();
void InitLayers(egl_connection_t*);
- void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
- void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+ void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, const char* const*);
+ void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, const char* const*);
bool Initialized();
std::string GetDebugLayers();
@@ -59,18 +58,23 @@
std::vector<layer_setup_func> layer_setup_;
private:
- LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0), dlhandle_(nullptr), native_bridge_(false){};
+ LayerLoader()
+ : layers_loaded_(false),
+ initialized_(false),
+ current_layer_(0),
+ dlhandle_(nullptr),
+ native_bridge_(false){};
bool layers_loaded_;
bool initialized_;
unsigned current_layer_;
void* dlhandle_;
bool native_bridge_;
- template<typename Func = void*>
+ template <typename Func = void*>
Func GetTrampoline(const char* name) const {
if (native_bridge_) {
- return reinterpret_cast<Func>(android::NativeBridgeGetTrampoline(
- dlhandle_, name, nullptr, 0));
+ return reinterpret_cast<Func>(
+ android::NativeBridgeGetTrampoline(dlhandle_, name, nullptr, 0));
}
return reinterpret_cast<Func>(dlsym(dlhandle_, name));
}
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index fd426c2..847b351 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -18,19 +18,14 @@
#include <sstream>
-
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
-egl_object_t::egl_object_t(egl_display_t* disp) :
- display(disp), count(1) {
+egl_object_t::egl_object_t(egl_display_t* disp) : display(disp), count(1) {
// NOTE: this does an implicit incRef
display->addObject(this);
}
-egl_object_t::~egl_object_t() {
-}
+egl_object_t::~egl_object_t() {}
void egl_object_t::terminate() {
// this marks the object as "terminated"
@@ -53,8 +48,6 @@
return display->getObject(object);
}
-// ----------------------------------------------------------------------------
-
egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWindowType win,
EGLSurface surface, EGLint colorSpace, egl_connection_t const* cnx)
: egl_object_t(dpy),
@@ -66,10 +59,10 @@
colorSpace(colorSpace),
egl_smpte2086_dirty(false),
egl_cta861_3_dirty(false) {
- egl_smpte2086_metadata.displayPrimaryRed = { EGL_DONT_CARE, EGL_DONT_CARE };
- egl_smpte2086_metadata.displayPrimaryGreen = { EGL_DONT_CARE, EGL_DONT_CARE };
- egl_smpte2086_metadata.displayPrimaryBlue = { EGL_DONT_CARE, EGL_DONT_CARE };
- egl_smpte2086_metadata.whitePoint = { EGL_DONT_CARE, EGL_DONT_CARE };
+ egl_smpte2086_metadata.displayPrimaryRed = {EGL_DONT_CARE, EGL_DONT_CARE};
+ egl_smpte2086_metadata.displayPrimaryGreen = {EGL_DONT_CARE, EGL_DONT_CARE};
+ egl_smpte2086_metadata.displayPrimaryBlue = {EGL_DONT_CARE, EGL_DONT_CARE};
+ egl_smpte2086_metadata.whitePoint = {EGL_DONT_CARE, EGL_DONT_CARE};
egl_smpte2086_metadata.maxLuminance = EGL_DONT_CARE;
egl_smpte2086_metadata.minLuminance = EGL_DONT_CARE;
egl_cta861_3_metadata.maxFrameAverageLightLevel = EGL_DONT_CARE;
@@ -173,16 +166,30 @@
return EGL_FALSE;
}
- metadata.displayPrimaryRed.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.x) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryRed.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.y) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryGreen.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.x) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryGreen.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.y) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryBlue.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.x) / EGL_METADATA_SCALING_EXT;
- metadata.displayPrimaryBlue.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.y) / EGL_METADATA_SCALING_EXT;
- metadata.whitePoint.x = static_cast<float>(egl_smpte2086_metadata.whitePoint.x) / EGL_METADATA_SCALING_EXT;
- metadata.whitePoint.y = static_cast<float>(egl_smpte2086_metadata.whitePoint.y) / EGL_METADATA_SCALING_EXT;
- metadata.maxLuminance = static_cast<float>(egl_smpte2086_metadata.maxLuminance) / EGL_METADATA_SCALING_EXT;
- metadata.minLuminance = static_cast<float>(egl_smpte2086_metadata.minLuminance) / EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryRed.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.x) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryRed.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.y) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryGreen.x =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.x) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryGreen.y =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.y) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryBlue.x =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.x) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.displayPrimaryBlue.y =
+ static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.y) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.whitePoint.x =
+ static_cast<float>(egl_smpte2086_metadata.whitePoint.x) / EGL_METADATA_SCALING_EXT;
+ metadata.whitePoint.y =
+ static_cast<float>(egl_smpte2086_metadata.whitePoint.y) / EGL_METADATA_SCALING_EXT;
+ metadata.maxLuminance =
+ static_cast<float>(egl_smpte2086_metadata.maxLuminance) / EGL_METADATA_SCALING_EXT;
+ metadata.minLuminance =
+ static_cast<float>(egl_smpte2086_metadata.minLuminance) / EGL_METADATA_SCALING_EXT;
return EGL_TRUE;
}
@@ -196,13 +203,15 @@
return EGL_FALSE;
}
- metadata.maxContentLightLevel = static_cast<float>(egl_cta861_3_metadata.maxContentLightLevel) / EGL_METADATA_SCALING_EXT;
- metadata.maxFrameAverageLightLevel = static_cast<float>(egl_cta861_3_metadata.maxFrameAverageLightLevel) / EGL_METADATA_SCALING_EXT;
+ metadata.maxContentLightLevel = static_cast<float>(egl_cta861_3_metadata.maxContentLightLevel) /
+ EGL_METADATA_SCALING_EXT;
+ metadata.maxFrameAverageLightLevel =
+ static_cast<float>(egl_cta861_3_metadata.maxFrameAverageLightLevel) /
+ EGL_METADATA_SCALING_EXT;
return EGL_TRUE;
}
-
EGLBoolean egl_surface_t::getColorSpaceAttribute(EGLint attribute, EGLint* value) const {
if (attribute == EGL_GL_COLORSPACE_KHR) {
*value = colorSpace;
@@ -211,7 +220,7 @@
return EGL_FALSE;
}
-EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint *value) const {
+EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint* value) const {
switch (attribute) {
case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT:
*value = egl_smpte2086_metadata.displayPrimaryRed.x;
@@ -257,7 +266,7 @@
return EGL_FALSE;
}
-EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint *value) const {
+EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint* value) const {
switch (attribute) {
case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT:
*value = egl_cta861_3_metadata.maxContentLightLevel;
@@ -276,13 +285,16 @@
egl_object_t::terminate();
}
-// ----------------------------------------------------------------------------
-
egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
- egl_connection_t const* cnx, int version) :
- egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
- config(config), read(nullptr), draw(nullptr), cnx(cnx), version(version) {
-}
+ egl_connection_t const* cnx, int version)
+ : egl_object_t(get_display(dpy)),
+ dpy(dpy),
+ context(context),
+ config(config),
+ read(nullptr),
+ draw(nullptr),
+ cnx(cnx),
+ version(version) {}
void egl_context_t::onLooseCurrent() {
read = nullptr;
@@ -297,43 +309,39 @@
* Here we cache the GL_EXTENSIONS string for this context and we
* add the extensions always handled by the wrapper
*/
+ if (!gl_extensions.empty()) return;
- if (gl_extensions.empty()) {
- // call the implementation's glGetString(GL_EXTENSIONS)
- const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
+ // call the implementation's glGetString(GL_EXTENSIONS)
+ const char* exts = (const char*)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
+ if (!exts) return;
- // If this context is sharing with another context, and the other context was reset
- // e.g. due to robustness failure, this context might also be reset and glGetString can
- // return NULL.
- if (exts) {
- gl_extensions = exts;
- if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
- gl_extensions.insert(0, "GL_EXT_debug_marker ");
- // eglGetProcAddress could return function pointers to these
- // functions while they actually don't work. Fix them now.
- __eglMustCastToProperFunctionPointerType* f;
- f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
- ->gl.glInsertEventMarkerEXT;
- if (*f != gl_noop) *f = gl_noop;
- f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
- ->gl.glPushGroupMarkerEXT;
- if (*f != gl_noop) *f = gl_noop;
- f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
- ->gl.glPopGroupMarkerEXT;
- if (*f != gl_noop) *f = gl_noop;
- }
+ // If this context is sharing with another context, and the other context was reset
+ // e.g. due to robustness failure, this context might also be reset and glGetString can
+ // return NULL.
+ gl_extensions = exts;
+ if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
+ gl_extensions.insert(0, "GL_EXT_debug_marker ");
+ // eglGetProcAddress could return function pointers to these
+ // functions while they actually don't work. Fix them now.
+ __eglMustCastToProperFunctionPointerType* f;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glInsertEventMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glPushGroupMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glPopGroupMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ }
- // tokenize the supported extensions for the glGetStringi() wrapper
- std::stringstream ss;
- std::string str;
- ss << gl_extensions;
- while (ss >> str) {
- tokenized_gl_extensions.push_back(str);
- }
- }
+ // tokenize the supported extensions for the glGetStringi() wrapper
+ std::stringstream ss;
+ std::string str;
+ ss << gl_extensions;
+ while (ss >> str) {
+ tokenized_gl_extensions.push_back(str);
}
}
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index fb2bdf4..e593b1c 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -17,30 +17,25 @@
#ifndef ANDROID_EGL_OBJECT_H
#define ANDROID_EGL_OBJECT_H
-#include <atomic>
-#include <stdint.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <log/log.h>
#include <stddef.h>
+#include <stdint.h>
+#include <system/window.h>
+#include <atomic>
#include <string>
#include <vector>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <system/window.h>
-
-#include <log/log.h>
-
#include "egl_display.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
class egl_display_t;
class egl_object_t {
- egl_display_t *display;
+ egl_display_t* display;
mutable std::atomic_size_t count;
protected:
@@ -64,6 +59,7 @@
egl_object_t* ref;
LocalRef() = delete;
LocalRef(const LocalRef* rhs) = delete;
+
public:
~LocalRef();
explicit LocalRef(egl_object_t* rhs);
@@ -73,9 +69,7 @@
ref = native;
}
}
- inline N* get() {
- return static_cast<N*>(ref);
- }
+ inline N* get() { return static_cast<N*>(ref); }
void acquire() const;
void release() const;
void terminate();
@@ -84,7 +78,7 @@
friend class LocalRef;
};
-template<typename N, typename T>
+template <typename N, typename T>
egl_object_t::LocalRef<N, T>::LocalRef(egl_object_t* rhs) : ref(rhs) {
if (ref) {
ref->incRef();
@@ -92,21 +86,21 @@
}
template <typename N, typename T>
-egl_object_t::LocalRef<N,T>::~LocalRef() {
+egl_object_t::LocalRef<N, T>::~LocalRef() {
if (ref) {
ref->destroy();
}
}
template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::acquire() const {
+void egl_object_t::LocalRef<N, T>::acquire() const {
if (ref) {
ref->incRef();
}
}
template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::release() const {
+void egl_object_t::LocalRef<N, T>::release() const {
if (ref) {
if (ref->decRef() == 1) {
// shouldn't happen because this is called from LocalRef
@@ -116,7 +110,7 @@
}
template <typename N, typename T>
-void egl_object_t::LocalRef<N,T>::terminate() {
+void egl_object_t::LocalRef<N, T>::terminate() {
if (ref) {
ref->terminate();
}
@@ -128,6 +122,7 @@
protected:
~egl_surface_t();
void terminate() override;
+
public:
typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
@@ -151,10 +146,13 @@
// it's not hard to imagine native games accessing them.
EGLSurface surface;
EGLConfig config;
+
private:
ANativeWindow* win;
+
public:
egl_connection_t const* cnx;
+
private:
bool connected;
void disconnect();
@@ -186,14 +184,15 @@
egl_cta861_3_metadata egl_cta861_3_metadata;
};
-class egl_context_t: public egl_object_t {
+class egl_context_t : public egl_object_t {
protected:
~egl_context_t() {}
+
public:
typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
- egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
- egl_connection_t const* cnx, int version);
+ egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, egl_connection_t const* cnx,
+ int version);
void onLooseCurrent();
void onMakeCurrent(EGLSurface draw, EGLSurface read);
@@ -209,30 +208,22 @@
std::vector<std::string> tokenized_gl_extensions;
};
-// ----------------------------------------------------------------------------
+typedef egl_surface_t::Ref SurfaceRef;
+typedef egl_context_t::Ref ContextRef;
-typedef egl_surface_t::Ref SurfaceRef;
-typedef egl_context_t::Ref ContextRef;
-
-// ----------------------------------------------------------------------------
-
-template<typename NATIVE, typename EGL>
+template <typename NATIVE, typename EGL>
static inline NATIVE* egl_to_native_cast(EGL arg) {
return reinterpret_cast<NATIVE*>(arg);
}
-static inline
-egl_surface_t* get_surface(EGLSurface surface) {
+static inline egl_surface_t* get_surface(EGLSurface surface) {
return egl_to_native_cast<egl_surface_t>(surface);
}
-static inline
-egl_context_t* get_context(EGLContext context) {
+static inline egl_context_t* get_context(EGLContext context) {
return egl_to_native_cast<egl_context_t>(context);
}
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_OBJECT_H
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 1119e4a..398efc0 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -76,71 +76,70 @@
* NOTE: Both strings MUST have a single space as the last character.
*/
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
+extern const char* const gBuiltinExtensionString;
+extern const char* const gExtensionString;
// clang-format off
// Extensions implemented by the EGL wrapper.
-char const * const gBuiltinExtensionString =
- "EGL_KHR_get_all_proc_addresses "
- "EGL_ANDROID_presentation_time "
- "EGL_KHR_swap_buffers_with_damage "
- "EGL_ANDROID_get_native_client_buffer "
+const char* const gBuiltinExtensionString =
"EGL_ANDROID_front_buffer_auto_refresh "
"EGL_ANDROID_get_frame_timestamps "
- "EGL_EXT_surface_SMPTE2086_metadata "
+ "EGL_ANDROID_get_native_client_buffer "
+ "EGL_ANDROID_presentation_time "
"EGL_EXT_surface_CTA861_3_metadata "
+ "EGL_EXT_surface_SMPTE2086_metadata "
+ "EGL_KHR_get_all_proc_addresses "
+ "EGL_KHR_swap_buffers_with_damage "
;
// Allowed list of extensions exposed to applications if implemented in the vendor driver.
-char const * const gExtensionString =
- "EGL_KHR_image " // mandatory
- "EGL_KHR_image_base " // mandatory
+const char* const gExtensionString =
+ "EGL_ANDROID_image_native_buffer " // mandatory
+ "EGL_ANDROID_native_fence_sync " // strongly recommended
+ "EGL_ANDROID_recordable " // mandatory
+ "EGL_EXT_buffer_age " // strongly recommended with partial_update
+ "EGL_EXT_create_context_robustness "
"EGL_EXT_image_gl_colorspace "
- "EGL_KHR_image_pixmap "
- "EGL_KHR_lock_surface "
+ "EGL_EXT_pixel_format_float "
+ "EGL_EXT_protected_content "
+ "EGL_EXT_yuv_surface "
+ "EGL_IMG_context_priority "
+ "EGL_KHR_config_attribs "
+ "EGL_KHR_create_context "
+ "EGL_KHR_create_context_no_error "
+ "EGL_KHR_fence_sync "
"EGL_KHR_gl_colorspace "
+ "EGL_KHR_gl_renderbuffer_image "
"EGL_KHR_gl_texture_2D_image "
"EGL_KHR_gl_texture_3D_image "
"EGL_KHR_gl_texture_cubemap_image "
- "EGL_KHR_gl_renderbuffer_image "
+ "EGL_KHR_image " // mandatory
+ "EGL_KHR_image_base " // mandatory
+ "EGL_KHR_image_pixmap "
+ "EGL_KHR_lock_surface "
+ "EGL_KHR_mutable_render_buffer "
+ "EGL_KHR_no_config_context "
+ "EGL_KHR_partial_update " // strongly recommended
"EGL_KHR_reusable_sync "
- "EGL_KHR_fence_sync "
- "EGL_KHR_create_context "
- "EGL_KHR_config_attribs "
- "EGL_KHR_surfaceless_context "
"EGL_KHR_stream "
- "EGL_KHR_stream_fifo "
- "EGL_KHR_stream_producer_eglsurface "
"EGL_KHR_stream_consumer_gltexture "
"EGL_KHR_stream_cross_process_fd "
- "EGL_EXT_create_context_robustness "
- "EGL_NV_system_time "
- "EGL_ANDROID_image_native_buffer " // mandatory
+ "EGL_KHR_stream_fifo "
+ "EGL_KHR_stream_producer_eglsurface "
+ "EGL_KHR_surfaceless_context "
"EGL_KHR_wait_sync " // strongly recommended
- "EGL_ANDROID_recordable " // mandatory
- "EGL_KHR_partial_update " // strongly recommended
- "EGL_EXT_pixel_format_float "
- "EGL_EXT_buffer_age " // strongly recommended with partial_update
- "EGL_KHR_create_context_no_error "
- "EGL_KHR_mutable_render_buffer "
- "EGL_EXT_yuv_surface "
- "EGL_EXT_protected_content "
- "EGL_IMG_context_priority "
- "EGL_KHR_no_config_context "
+ "EGL_NV_system_time "
;
-char const * const gClientExtensionString =
+const char* const gClientExtensionString =
+ "EGL_ANDROID_GLES_layers "
+ "EGL_ANGLE_platform_angle "
"EGL_EXT_client_extensions "
"EGL_KHR_platform_android "
- "EGL_ANGLE_platform_angle "
- "EGL_ANDROID_GLES_layers";
-// clang-format on
+ ;
// extensions not exposed to applications but used by the ANDROID system
// "EGL_ANDROID_blob_cache " // strongly recommended
-// "EGL_IMG_hibernate_process " // optional
-// "EGL_ANDROID_native_fence_sync " // strongly recommended
// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1
/*
@@ -150,105 +149,69 @@
*/
static const extension_map_t sExtensionMap[] = {
// EGL_KHR_lock_surface
- { "eglLockSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
- { "eglUnlockSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
+ { "eglLockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
+ { "eglUnlockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
// EGL_KHR_image, EGL_KHR_image_base
- { "eglCreateImageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
- { "eglDestroyImageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
+ { "eglCreateImageKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
+ { "eglDestroyImageKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
// EGL_KHR_reusable_sync, EGL_KHR_fence_sync
- { "eglCreateSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
- { "eglDestroySyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
- { "eglClientWaitSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
- { "eglSignalSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
- { "eglGetSyncAttribKHR",
- (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
+ { "eglCreateSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
+ { "eglDestroySyncKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
+ { "eglClientWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
+ { "eglSignalSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
+ { "eglGetSyncAttribKHR", (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
// EGL_NV_system_time
- { "eglGetSystemTimeFrequencyNV",
- (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
- { "eglGetSystemTimeNV",
- (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
+ { "eglGetSystemTimeFrequencyNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
+ { "eglGetSystemTimeNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
// EGL_KHR_wait_sync
- { "eglWaitSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
+ { "eglWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
// EGL_ANDROID_presentation_time
- { "eglPresentationTimeANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
+ { "eglPresentationTimeANDROID", (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
// EGL_KHR_swap_buffers_with_damage
- { "eglSwapBuffersWithDamageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
+ { "eglSwapBuffersWithDamageKHR", (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
// EGL_ANDROID_get_native_client_buffer
- { "eglGetNativeClientBufferANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
+ { "eglGetNativeClientBufferANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
// EGL_KHR_partial_update
- { "eglSetDamageRegionKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
+ { "eglSetDamageRegionKHR", (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
- { "eglCreateStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
- { "eglDestroyStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
- { "eglStreamAttribKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
- { "eglQueryStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
- { "eglQueryStreamu64KHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
- { "eglQueryStreamTimeKHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
- { "eglCreateStreamProducerSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
- { "eglStreamConsumerGLTextureExternalKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
- { "eglStreamConsumerAcquireKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
- { "eglStreamConsumerReleaseKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
- { "eglGetStreamFileDescriptorKHR",
- (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
- { "eglCreateStreamFromFileDescriptorKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+ { "eglCreateStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
+ { "eglDestroyStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
+ { "eglStreamAttribKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
+ { "eglQueryStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
+ { "eglQueryStreamu64KHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
+ { "eglQueryStreamTimeKHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
+ { "eglCreateStreamProducerSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
+ { "eglStreamConsumerGLTextureExternalKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
+ { "eglStreamConsumerAcquireKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
+ { "eglStreamConsumerReleaseKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
+ { "eglGetStreamFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
+ { "eglCreateStreamFromFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
// EGL_ANDROID_get_frame_timestamps
- { "eglGetNextFrameIdANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
- { "eglGetCompositorTimingANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
- { "eglGetCompositorTimingSupportedANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
- { "eglGetFrameTimestampsANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
- { "eglGetFrameTimestampSupportedANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
+ { "eglGetNextFrameIdANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
+ { "eglGetCompositorTimingANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
+ { "eglGetCompositorTimingSupportedANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
+ { "eglGetFrameTimestampsANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+ { "eglGetFrameTimestampSupportedANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
// EGL_ANDROID_native_fence_sync
- { "eglDupNativeFenceFDANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
+ { "eglDupNativeFenceFDANDROID", (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
};
+// clang-format on
/*
* These extensions entry-points should not be exposed to applications.
* They're used internally by the Android EGL layer.
*/
-#define FILTER_EXTENSIONS(procname) \
- (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \
- !strcmp((procname), "eglHibernateProcessIMG") || \
- !strcmp((procname), "eglAwakenProcessIMG"))
+#define FILTER_EXTENSIONS(procname) (!strcmp((procname), "eglSetBlobCacheFuncsANDROID"))
// accesses protected by sExtensionMapMutex
static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtensionMap;
@@ -257,9 +220,8 @@
static int sGLExtensionSlot = 0;
static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
-static void(*findProcAddress(const char* name,
- const extension_map_t* map, size_t n))() {
- for (uint32_t i=0 ; i<n ; i++) {
+static void (*findProcAddress(const char* name, const extension_map_t* map, size_t n))() {
+ for (uint32_t i = 0; i < n; i++) {
if (!strcmp(name, map[i].name)) {
return map[i].address;
}
@@ -269,14 +231,17 @@
// ----------------------------------------------------------------------------
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
+extern void setGLHooksThreadSpecific(gl_hooks_t const* value);
extern EGLBoolean egl_init_drivers();
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
+extern const __eglMustCastToProperFunctionPointerType
+ gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
extern gl_hooks_t gHooksTrace;
// ----------------------------------------------------------------------------
-static inline EGLContext getContext() { return egl_tls_t::getContext(); }
+static inline EGLContext getContext() {
+ return egl_tls_t::getContext();
+}
// ----------------------------------------------------------------------------
@@ -309,9 +274,8 @@
// Initialization
// ----------------------------------------------------------------------------
-EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor)
-{
- egl_display_ptr dp = get_display(dpy);
+EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint* major, EGLint* minor) {
+ egl_display_t* dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
EGLBoolean res = dp->initialize(major, minor);
@@ -319,13 +283,12 @@
return res;
}
-EGLBoolean eglTerminateImpl(EGLDisplay dpy)
-{
+EGLBoolean eglTerminateImpl(EGLDisplay dpy) {
// NOTE: don't unload the drivers b/c some APIs can be called
// after eglTerminate() has been called. eglTerminate() only
// terminates an EGLDisplay, not a EGL itself.
- egl_display_ptr dp = get_display(dpy);
+ egl_display_t* dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
EGLBoolean res = dp->terminate();
@@ -337,14 +300,12 @@
// configuration
// ----------------------------------------------------------------------------
-EGLBoolean eglGetConfigsImpl(EGLDisplay dpy,
- EGLConfig *configs,
- EGLint config_size, EGLint *num_config)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetConfigsImpl(EGLDisplay dpy, EGLConfig* configs, EGLint config_size,
+ EGLint* num_config) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- if (num_config==nullptr) {
+ if (num_config == nullptr) {
return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
}
@@ -353,96 +314,88 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso) {
- res = cnx->egl.eglGetConfigs(
- dp->disp.dpy, configs, config_size, num_config);
+ res = cnx->egl.eglGetConfigs(dp->disp.dpy, configs, config_size, num_config);
}
return res;
}
-EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list,
- EGLConfig *configs, EGLint config_size,
- EGLint *num_config)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglChooseConfigImpl(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs,
+ EGLint config_size, EGLint* num_config) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- if (num_config==nullptr) {
+ if (num_config == nullptr) {
return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
}
- EGLBoolean res = EGL_FALSE;
*num_config = 0;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso) {
- if (attrib_list) {
- if (base::GetBoolProperty("debug.egl.force_msaa", false)) {
- size_t attribCount = 0;
- EGLint attrib = attrib_list[0];
+ if (!cnx->dso) return EGL_FALSE;
- // Only enable MSAA if the context is OpenGL ES 2.0 and
- // if no caveat is requested
- const EGLint *attribRendererable = nullptr;
- const EGLint *attribCaveat = nullptr;
+ if (!attrib_list || !base::GetBoolProperty("debug.egl.force_msaa", false))
+ return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size,
+ num_config);
- // Count the number of attributes and look for
- // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
- while (attrib != EGL_NONE) {
- attrib = attrib_list[attribCount];
- switch (attrib) {
- case EGL_RENDERABLE_TYPE:
- attribRendererable = &attrib_list[attribCount];
- break;
- case EGL_CONFIG_CAVEAT:
- attribCaveat = &attrib_list[attribCount];
- break;
- default:
- break;
- }
- attribCount++;
- }
+ // Force 4x MSAA
+ size_t attribCount = 0;
+ EGLint attrib = attrib_list[0];
- if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
- (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+ // Only enable MSAA if the context is OpenGL ES 2.0 and
+ // if no caveat is requested
+ const EGLint* attribRendererable = nullptr;
+ const EGLint* attribCaveat = nullptr;
- // Insert 2 extra attributes to force-enable MSAA 4x
- EGLint aaAttribs[attribCount + 4];
- aaAttribs[0] = EGL_SAMPLE_BUFFERS;
- aaAttribs[1] = 1;
- aaAttribs[2] = EGL_SAMPLES;
- aaAttribs[3] = 4;
-
- memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
-
- EGLint numConfigAA;
- EGLBoolean resAA = cnx->egl.eglChooseConfig(
- dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
-
- if (resAA == EGL_TRUE && numConfigAA > 0) {
- ALOGD("Enabling MSAA 4x");
- *num_config = numConfigAA;
- return resAA;
- }
- }
- }
+ // Count the number of attributes and look for
+ // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
+ while (attrib != EGL_NONE) {
+ attrib = attrib_list[attribCount];
+ switch (attrib) {
+ case EGL_RENDERABLE_TYPE:
+ attribRendererable = &attrib_list[attribCount];
+ break;
+ case EGL_CONFIG_CAVEAT:
+ attribCaveat = &attrib_list[attribCount];
+ break;
+ default:
+ break;
}
-
- res = cnx->egl.eglChooseConfig(
- dp->disp.dpy, attrib_list, configs, config_size, num_config);
+ attribCount++;
}
- return res;
+
+ if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
+ (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+ // Insert 2 extra attributes to force-enable MSAA 4x
+ EGLint aaAttribs[attribCount + 4];
+ aaAttribs[0] = EGL_SAMPLE_BUFFERS;
+ aaAttribs[1] = 1;
+ aaAttribs[2] = EGL_SAMPLES;
+ aaAttribs[3] = 4;
+
+ memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
+
+ EGLint numConfigAA;
+ EGLBoolean resAA = cnx->egl.eglChooseConfig(dp->disp.dpy, aaAttribs, configs, config_size,
+ &numConfigAA);
+
+ if (resAA == EGL_TRUE && numConfigAA > 0) {
+ ALOGD("Enabling MSAA 4x");
+ *num_config = numConfigAA;
+ return resAA;
+ }
+ }
+
+ return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size, num_config);
}
-EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config,
- EGLint attribute, EGLint *value)
-{
+EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, EGLint attribute,
+ EGLint* value) {
egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (!dp) return EGL_FALSE;
- return cnx->egl.eglGetConfigAttrib(
- dp->disp.dpy, config, attribute, value);
+ return cnx->egl.eglGetConfigAttrib(dp->disp.dpy, config, attribute, value);
}
// ----------------------------------------------------------------------------
@@ -480,7 +433,7 @@
}
// Returns a list of color spaces understood by the vendor EGL driver.
-static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) {
+static std::vector<EGLint> getDriverColorSpaces(egl_display_t* dp) {
std::vector<EGLint> colorSpaces;
// sRGB and linear are always supported when color space support is present.
@@ -505,7 +458,8 @@
if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) {
colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
}
- if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
+ if (findExtension(dp->disp.queryString.extensions,
+ "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT);
}
return colorSpaces;
@@ -515,7 +469,7 @@
// If there is no color space attribute in attrib_list, colorSpace is left
// unmodified.
template <typename AttrType>
-static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window,
+static EGLBoolean processAttributes(egl_display_t* dp, ANativeWindow* window,
const AttrType* attrib_list, EGLint* colorSpace,
std::vector<AttrType>* strippedAttribList) {
for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
@@ -695,7 +649,7 @@
}
template <typename AttrType, typename CreateFuncType>
-EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config,
+EGLSurface eglCreateWindowSurfaceTmpl(egl_display_t* dp, egl_connection_t* cnx, EGLConfig config,
ANativeWindow* window, const AttrType* attrib_list,
CreateFuncType createWindowSurfaceFunc) {
const AttrType* origAttribList = attrib_list;
@@ -764,7 +718,7 @@
EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface,
+ egl_surface_t* s = new egl_surface_t(dp, config, window, surface,
getReportedColorSpace(colorSpace), cnx);
return s;
}
@@ -785,8 +739,8 @@
EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
const EGLint* attrib_list) {
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ egl_connection_t* cnx = nullptr;
+ egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
return eglCreateWindowSurfaceTmpl<
EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list,
@@ -797,8 +751,8 @@
EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window,
const EGLAttrib* attrib_list) {
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ egl_connection_t* cnx = nullptr;
+ egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
if (cnx->egl.eglCreatePlatformWindowSurface) {
@@ -838,8 +792,8 @@
// belongs to the Android platform. Any such call fails and generates
// an EGL_BAD_PARAMETER error.
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ egl_connection_t* cnx = nullptr;
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
}
@@ -849,7 +803,7 @@
EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) {
egl_connection_t* cnx = nullptr;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
}
@@ -859,36 +813,33 @@
EGLSurface eglCreatePbufferSurfaceImpl(EGLDisplay dpy, EGLConfig config,
const EGLint* attrib_list) {
egl_connection_t* cnx = nullptr;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
- getNativePixelFormat(iDpy, cnx, config, &format);
+ egl_display_t* dp = validate_display_connection(dpy, &cnx);
+ if (!dp) return EGL_NO_SURFACE;
- // Select correct colorspace based on user's attribute list
- EGLint colorSpace = EGL_UNKNOWN;
- std::vector<EGLint> strippedAttribList;
- if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) {
- ALOGE("error invalid colorspace: %d", colorSpace);
- return EGL_NO_SURFACE;
- }
- attrib_list = strippedAttribList.data();
+ EGLDisplay iDpy = dp->disp.dpy;
+ android_pixel_format format;
+ getNativePixelFormat(iDpy, cnx, config, &format);
- EGLSurface surface = cnx->egl.eglCreatePbufferSurface(dp->disp.dpy, config, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
- getReportedColorSpace(colorSpace), cnx);
- return s;
- }
+ // Select correct colorspace based on user's attribute list
+ EGLint colorSpace = EGL_UNKNOWN;
+ std::vector<EGLint> strippedAttribList;
+ if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) {
+ ALOGE("error invalid colorspace: %d", colorSpace);
+ return EGL_NO_SURFACE;
}
- return EGL_NO_SURFACE;
+ attrib_list = strippedAttribList.data();
+
+ EGLSurface surface = cnx->egl.eglCreatePbufferSurface(iDpy, config, attrib_list);
+ if (surface == EGL_NO_SURFACE) return surface;
+
+ return new egl_surface_t(dp, config, nullptr, surface, getReportedColorSpace(colorSpace), cnx);
}
EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
egl_surface_t* const s = get_surface(surface);
@@ -901,10 +852,10 @@
EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute,
EGLint* value) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
egl_surface_t const* const s = get_surface(surface);
@@ -919,12 +870,12 @@
}
void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return;
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
setError(EGL_BAD_SURFACE, EGL_FALSE);
}
@@ -934,14 +885,13 @@
// Contexts
// ----------------------------------------------------------------------------
-EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config,
- EGLContext share_list, const EGLint *attrib_list)
-{
+EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, EGLContext share_list,
+ const EGLint* attrib_list) {
egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (dp) {
if (share_list != EGL_NO_CONTEXT) {
- if (!ContextRef(dp.get(), share_list).get()) {
+ if (!ContextRef(dp, share_list).get()) {
return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
}
egl_context_t* const c = get_context(share_list);
@@ -963,8 +913,8 @@
}
};
}
- EGLContext context = cnx->egl.eglCreateContext(
- dp->disp.dpy, config, share_list, attrib_list);
+ EGLContext context =
+ cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
// figure out if it's a GLESv1 or GLESv2
int version = egl_connection_t::GLESv1_INDEX;
@@ -988,17 +938,14 @@
return EGL_NO_CONTEXT;
}
-EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx)
-{
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp)
- return EGL_FALSE;
+EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx) {
+ const egl_display_t* dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
- ContextRef _c(dp.get(), ctx);
- if (!_c.get())
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ ContextRef _c(dp, ctx);
+ if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
- egl_context_t * const c = get_context(ctx);
+ egl_context_t* const c = get_context(ctx);
EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
if (result == EGL_TRUE) {
_c.terminate();
@@ -1006,24 +953,21 @@
return result;
}
-EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw,
- EGLSurface read, EGLContext ctx)
-{
- egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglMakeCurrentImpl(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
+ egl_display_t* dp = validate_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
// If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
// EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
// a valid but uninitialized display.
- if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
- (draw != EGL_NO_SURFACE) ) {
+ if ((ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || (draw != EGL_NO_SURFACE)) {
if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
}
// get a reference to the object passed in
- ContextRef _c(dp.get(), ctx);
- SurfaceRef _d(dp.get(), draw);
- SurfaceRef _r(dp.get(), read);
+ ContextRef _c(dp, ctx);
+ SurfaceRef _d(dp, draw);
+ SurfaceRef _r(dp, read);
// validate the context (if not EGL_NO_CONTEXT)
if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
@@ -1032,17 +976,17 @@
}
// these are the underlying implementation's object
- EGLContext impl_ctx = EGL_NO_CONTEXT;
+ EGLContext impl_ctx = EGL_NO_CONTEXT;
EGLSurface impl_draw = EGL_NO_SURFACE;
EGLSurface impl_read = EGL_NO_SURFACE;
// these are our objects structs passed in
- egl_context_t * c = nullptr;
- egl_surface_t const * d = nullptr;
- egl_surface_t const * r = nullptr;
+ egl_context_t* c = nullptr;
+ egl_surface_t const* d = nullptr;
+ egl_surface_t const* r = nullptr;
// these are the current objects structs
- egl_context_t * cur_c = get_context(getContext());
+ egl_context_t* cur_c = get_context(getContext());
if (ctx != EGL_NO_CONTEXT) {
c = get_context(ctx);
@@ -1074,10 +1018,7 @@
impl_read = r->surface;
}
-
- EGLBoolean result = dp->makeCurrent(c, cur_c,
- draw, read, ctx,
- impl_draw, impl_read, impl_ctx);
+ EGLBoolean result = dp->makeCurrent(c, cur_c, draw, read, ctx, impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
if (c) {
@@ -1098,81 +1039,72 @@
return result;
}
-EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx,
- EGLint attribute, EGLint *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryContextImpl(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- ContextRef _c(dp.get(), ctx);
+ ContextRef _c(dp, ctx);
if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
- egl_context_t * const c = get_context(ctx);
- return c->cnx->egl.eglQueryContext(
- dp->disp.dpy, c->context, attribute, value);
-
+ egl_context_t* const c = get_context(ctx);
+ return c->cnx->egl.eglQueryContext(dp->disp.dpy, c->context, attribute, value);
}
-EGLContext eglGetCurrentContextImpl(void)
-{
+EGLContext eglGetCurrentContextImpl(void) {
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would correctly return EGL_NO_CONTEXT.
EGLContext ctx = getContext();
return ctx;
}
-EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw)
-{
+EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw) {
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would correctly return EGL_NO_SURFACE.
EGLContext ctx = getContext();
if (ctx) {
- egl_context_t const * const c = get_context(ctx);
+ egl_context_t const* const c = get_context(ctx);
if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
switch (readdraw) {
- case EGL_READ: return c->read;
- case EGL_DRAW: return c->draw;
- default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ case EGL_READ:
+ return c->read;
+ case EGL_DRAW:
+ return c->draw;
+ default:
+ return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
}
}
return EGL_NO_SURFACE;
}
-EGLDisplay eglGetCurrentDisplayImpl(void)
-{
+EGLDisplay eglGetCurrentDisplayImpl(void) {
// could be called before eglInitialize(), but we wouldn't have a context
// then, and this function would correctly return EGL_NO_DISPLAY.
EGLContext ctx = getContext();
if (ctx) {
- egl_context_t const * const c = get_context(ctx);
+ egl_context_t const* const c = get_context(ctx);
if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
return c->dpy;
}
return EGL_NO_DISPLAY;
}
-EGLBoolean eglWaitGLImpl(void)
-{
+EGLBoolean eglWaitGLImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
return cnx->egl.eglWaitGL();
}
-EGLBoolean eglWaitNativeImpl(EGLint engine)
-{
+EGLBoolean eglWaitNativeImpl(EGLint engine) {
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
return cnx->egl.eglWaitNative(engine);
}
-EGLint eglGetErrorImpl(void)
-{
+EGLint eglGetErrorImpl(void) {
EGLint err = EGL_SUCCESS;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso) {
@@ -1184,8 +1116,7 @@
return err;
}
-static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
- const char* procname) {
+static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(const char* procname) {
const egl_connection_t* cnx = &gEGLImpl;
void* proc = nullptr;
@@ -1201,8 +1132,7 @@
return nullptr;
}
-__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname)
-{
+__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char* procname) {
if (FILTER_EXTENSIONS(procname)) {
return nullptr;
}
@@ -1249,13 +1179,10 @@
// Ensure we have room to track it
const int slot = sGLExtensionSlot;
if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
-
if (cnx->dso && cnx->egl.eglGetProcAddress) {
-
// Extensions are independent of the bound context
addr = cnx->egl.eglGetProcAddress(procname);
if (addr) {
-
// purposefully track the bottom of the stack in extensionMap
extensionMap[name] = addr;
@@ -1264,7 +1191,7 @@
// Track the top most entry point return the extension forwarder
cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
addr = gExtensionForwarders[slot];
// Remember the slot for this extension
@@ -1296,7 +1223,7 @@
// Track the top most entry point and return the extension forwarder
cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[ext_slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr;
addr = gExtensionForwarders[ext_slot];
}
@@ -1306,7 +1233,6 @@
class FrameCompletionThread {
public:
-
static void queueSync(EGLSyncKHR sync) {
static FrameCompletionThread thread;
@@ -1323,7 +1249,6 @@
}
private:
-
FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
std::thread thread(&FrameCompletionThread::loop, this);
thread.detach();
@@ -1378,15 +1303,13 @@
std::mutex mMutex;
};
-EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw,
- EGLint *rects, EGLint n_rects)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+ EGLint n_rects) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), draw);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, draw);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
if (n_rects < 0 || (n_rects > 0 && rects == NULL))
return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
@@ -1402,11 +1325,11 @@
if (CC_UNLIKELY(dp->finishOnSwap)) {
uint32_t pixel;
- egl_context_t * const c = get_context( egl_tls_t::getContext() );
+ egl_context_t* const c = get_context(egl_tls_t::getContext());
if (c) {
// glReadPixels() ensures that the frame is complete
- s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
- GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
+ s->cnx->hooks[c->version]->gl.glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
+ &pixel);
}
}
@@ -1441,41 +1364,35 @@
}
if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
- return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
- rects, n_rects);
- } else {
- return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+ return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, rects, n_rects);
}
+
+ return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
}
-EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface)
-{
+EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface) {
return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0);
}
-EGLBoolean eglCopyBuffersImpl( EGLDisplay dpy, EGLSurface surface,
- NativePixmapType target)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglCopyBuffersImpl(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
}
-const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name)
-{
+const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name) {
if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) {
// Return list of client extensions
return gClientExtensionString;
}
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return (const char *) nullptr;
+ const egl_display_t* dp = validate_display(dpy);
+ if (!dp) return (const char*)nullptr;
switch (name) {
case EGL_VENDOR:
@@ -1489,13 +1406,12 @@
default:
break;
}
- return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+ return setError(EGL_BAD_PARAMETER, (const char*)nullptr);
}
-EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name)
-{
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return (const char *) nullptr;
+EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name) {
+ const egl_display_t* dp = validate_display(dpy);
+ if (!dp) return (const char*)nullptr;
switch (name) {
case EGL_VENDOR:
@@ -1509,24 +1425,22 @@
default:
break;
}
- return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+ return setError(EGL_BAD_PARAMETER, (const char*)nullptr);
}
// ----------------------------------------------------------------------------
// EGL 1.1
// ----------------------------------------------------------------------------
-EGLBoolean eglSurfaceAttribImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute,
+ EGLint value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t * const s = get_surface(surface);
+ egl_surface_t* const s = get_surface(surface);
if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
if (!s->getNativeWindow()) {
@@ -1549,51 +1463,41 @@
} else if (s->setCta8613Attribute(attribute, value)) {
return EGL_TRUE;
} else if (s->cnx->egl.eglSurfaceAttrib) {
- return s->cnx->egl.eglSurfaceAttrib(
- dp->disp.dpy, s->surface, attribute, value);
+ return s->cnx->egl.eglSurfaceAttrib(dp->disp.dpy, s->surface, attribute, value);
}
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglBindTexImageImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglBindTexImageImpl(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglBindTexImage) {
- return s->cnx->egl.eglBindTexImage(
- dp->disp.dpy, s->surface, buffer);
+ return s->cnx->egl.eglBindTexImage(dp->disp.dpy, s->surface, buffer);
}
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglReleaseTexImageImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglReleaseTexImageImpl(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglReleaseTexImage) {
- return s->cnx->egl.eglReleaseTexImage(
- dp->disp.dpy, s->surface, buffer);
+ return s->cnx->egl.eglReleaseTexImage(dp->disp.dpy, s->surface, buffer);
}
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean res = EGL_TRUE;
@@ -1605,16 +1509,13 @@
return res;
}
-
// ----------------------------------------------------------------------------
// EGL 1.2
// ----------------------------------------------------------------------------
-EGLBoolean eglWaitClientImpl(void)
-{
+EGLBoolean eglWaitClientImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
EGLBoolean res;
if (cnx->egl.eglWaitClient) {
@@ -1625,8 +1526,7 @@
return res;
}
-EGLBoolean eglBindAPIImpl(EGLenum api)
-{
+EGLBoolean eglBindAPIImpl(EGLenum api) {
// bind this API on all EGLs
EGLBoolean res = EGL_TRUE;
egl_connection_t* const cnx = &gEGLImpl;
@@ -1636,8 +1536,7 @@
return res;
}
-EGLenum eglQueryAPIImpl(void)
-{
+EGLenum eglQueryAPIImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryAPI) {
return cnx->egl.eglQueryAPI();
@@ -1647,8 +1546,7 @@
return EGL_OPENGL_ES_API;
}
-EGLBoolean eglReleaseThreadImpl(void)
-{
+EGLBoolean eglReleaseThreadImpl(void) {
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglReleaseThread) {
cnx->egl.eglReleaseThread();
@@ -1661,16 +1559,15 @@
return EGL_TRUE;
}
-EGLSurface eglCreatePbufferFromClientBufferImpl(
- EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
- EGLConfig config, const EGLint *attrib_list)
-{
+EGLSurface eglCreatePbufferFromClientBufferImpl(EGLDisplay dpy, EGLenum buftype,
+ EGLClientBuffer buffer, EGLConfig config,
+ const EGLint* attrib_list) {
egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ const egl_display_t* dp = validate_display_connection(dpy, &cnx);
if (!dp) return EGL_FALSE;
if (cnx->egl.eglCreatePbufferFromClientBuffer) {
- return cnx->egl.eglCreatePbufferFromClientBuffer(
- dp->disp.dpy, buftype, buffer, config, attrib_list);
+ return cnx->egl.eglCreatePbufferFromClientBuffer(dp->disp.dpy, buftype, buffer, config,
+ attrib_list);
}
return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
}
@@ -1679,34 +1576,28 @@
// EGL_EGLEXT_VERSION 3
// ----------------------------------------------------------------------------
-EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface,
- const EGLint *attrib_list)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglLockSurfaceKHR) {
- return s->cnx->egl.eglLockSurfaceKHR(
- dp->disp.dpy, s->surface, attrib_list);
+ return s->cnx->egl.eglLockSurfaceKHR(dp->disp.dpy, s->surface, attrib_list);
}
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
-EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ SurfaceRef _s(dp, surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglUnlockSurfaceKHR) {
return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
}
@@ -1719,7 +1610,7 @@
EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
EGLClientBuffer buffer, const AttrType* attrib_list,
FuncType eglCreateImageFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_IMAGE_KHR;
std::vector<AttrType> strippedAttribs;
@@ -1728,7 +1619,7 @@
// EGL_GL_COLORSPACE_LINEAR_KHR, EGL_GL_COLORSPACE_SRGB_KHR and
// EGL_GL_COLORSPACE_DEFAULT_EXT if EGL_EXT_image_gl_colorspace is supported,
// but some drivers don't like the DEFAULT value and generate an error.
- for (const AttrType *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
+ for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
if (attr[0] == EGL_GL_COLORSPACE_KHR &&
dp->haveExtension("EGL_EXT_image_gl_colorspace")) {
if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR &&
@@ -1742,14 +1633,15 @@
strippedAttribs.push_back(EGL_NONE);
}
- ContextRef _c(dp.get(), ctx);
+ ContextRef _c(dp, ctx);
egl_context_t* const c = _c.get();
EGLImageKHR result = EGL_NO_IMAGE_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && eglCreateImageFunc) {
result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer,
- needsAndroidPEglMitigation() ? strippedAttribs.data() : attrib_list);
+ needsAndroidPEglMitigation() ? strippedAttribs.data()
+ : attrib_list);
}
return result;
}
@@ -1788,7 +1680,7 @@
EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img,
PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1825,7 +1717,7 @@
template <typename AttrType, typename FuncType>
EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list,
FuncType eglCreateSyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_SYNC_KHR;
egl_connection_t* const cnx = &gEGLImpl;
@@ -1864,7 +1756,7 @@
EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync,
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1893,7 +1785,7 @@
}
EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1906,7 +1798,7 @@
EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout,
PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLint result = EGL_FALSE;
@@ -1938,7 +1830,7 @@
template <typename AttrType, typename FuncType>
EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value,
FuncType eglGetSyncAttribFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
@@ -1983,106 +1875,93 @@
.eglGetSyncAttribKHR);
}
-EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint* attrib_list) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_STREAM_KHR;
EGLStreamKHR result = EGL_NO_STREAM_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
- result = cnx->egl.eglCreateStreamKHR(
- dp->disp.dpy, attrib_list);
+ result = cnx->egl.eglCreateStreamKHR(dp->disp.dpy, attrib_list);
}
return result;
}
-EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
- result = cnx->egl.eglDestroyStreamKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglDestroyStreamKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLint value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLint value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
- result = cnx->egl.eglStreamAttribKHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglStreamAttribKHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
-EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLint *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLint* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
- result = cnx->egl.eglQueryStreamKHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglQueryStreamKHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
-EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLuint64KHR *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLuint64KHR* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
- result = cnx->egl.eglQueryStreamu64KHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglQueryStreamu64KHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
-EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLTimeKHR *value)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLTimeKHR* value) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
- result = cnx->egl.eglQueryStreamTimeKHR(
- dp->disp.dpy, stream, attribute, value);
+ result = cnx->egl.eglQueryStreamTimeKHR(dp->disp.dpy, stream, attribute, value);
}
return result;
}
EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config,
- EGLStreamKHR stream, const EGLint *attrib_list)
-{
- egl_display_ptr dp = validate_display(dpy);
+ EGLStreamKHR stream, const EGLint* attrib_list) {
+ egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_SURFACE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
- EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
- dp->disp.dpy, config, stream, attrib_list);
+ EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(dp->disp.dpy, config,
+ stream, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
+ egl_surface_t* s = new egl_surface_t(dp, config, nullptr, surface,
EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
return s;
}
@@ -2090,77 +1969,63 @@
return EGL_NO_SURFACE;
}
-EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
- result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
- result = cnx->egl.eglStreamConsumerAcquireKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglStreamConsumerAcquireKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
- result = cnx->egl.eglStreamConsumerReleaseKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglStreamConsumerReleaseKHR(dp->disp.dpy, stream);
}
return result;
}
-EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(
- EGLDisplay dpy, EGLStreamKHR stream)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
- result = cnx->egl.eglGetStreamFileDescriptorKHR(
- dp->disp.dpy, stream);
+ result = cnx->egl.eglGetStreamFileDescriptorKHR(dpy, stream);
}
return result;
}
-EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(
- EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(EGLDisplay dpy,
+ EGLNativeFileDescriptorKHR file_descriptor) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_STREAM_KHR;
EGLStreamKHR result = EGL_NO_STREAM_KHR;
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
- result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
- dp->disp.dpy, file_descriptor);
+ result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(dp->disp.dpy, file_descriptor);
}
return result;
}
@@ -2173,7 +2038,7 @@
template <typename ReturnType, typename FuncType>
ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags,
FuncType eglWaitSyncFunc) {
- const egl_display_ptr dp = validate_display(dpy);
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
ReturnType result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
@@ -2210,9 +2075,8 @@
// ANDROID extensions
// ----------------------------------------------------------------------------
-EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
@@ -2224,35 +2088,33 @@
}
EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLnsecsANDROID time)
-{
- const egl_display_ptr dp = validate_display(dpy);
+ EGLnsecsANDROID time) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return EGL_FALSE;
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
setError(EGL_BAD_SURFACE, EGL_FALSE);
return EGL_FALSE;
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
native_window_set_buffers_timestamp(s->getNativeWindow(), time);
return EGL_TRUE;
}
-EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) {
+EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer* buffer) {
if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
- return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
+ return const_cast<ANativeWindowBuffer*>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
}
// ----------------------------------------------------------------------------
// NVIDIA extensions
// ----------------------------------------------------------------------------
-EGLuint64NV eglGetSystemTimeFrequencyNVImpl()
-{
+EGLuint64NV eglGetSystemTimeFrequencyNVImpl() {
EGLuint64NV ret = 0;
egl_connection_t* const cnx = &gEGLImpl;
@@ -2263,8 +2125,7 @@
return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
}
-EGLuint64NV eglGetSystemTimeNVImpl()
-{
+EGLuint64NV eglGetSystemTimeNVImpl() {
EGLuint64NV ret = 0;
egl_connection_t* const cnx = &gEGLImpl;
@@ -2278,43 +2139,40 @@
// ----------------------------------------------------------------------------
// Partial update extension
// ----------------------------------------------------------------------------
-EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface,
- EGLint *rects, EGLint n_rects)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface, EGLint* rects,
+ EGLint n_rects) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
setError(EGL_BAD_DISPLAY, EGL_FALSE);
return EGL_FALSE;
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
setError(EGL_BAD_SURFACE, EGL_FALSE);
return EGL_FALSE;
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (s->cnx->egl.eglSetDamageRegionKHR) {
- return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
- rects, n_rects);
+ return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, rects, n_rects);
}
return EGL_FALSE;
}
-EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLuint64KHR *frameId) {
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (!s->getNativeWindow()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2335,19 +2193,19 @@
}
EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
-{
- const egl_display_ptr dp = validate_display(dpy);
+ EGLint numTimestamps, const EGLint* names,
+ EGLnsecsANDROID* values) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (!s->getNativeWindow()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2373,36 +2231,35 @@
}
}
- int ret = native_window_get_compositor_timing(s->getNativeWindow(),
- compositeDeadline, compositeInterval, compositeToPresentLatency);
+ int ret = native_window_get_compositor_timing(s->getNativeWindow(), compositeDeadline,
+ compositeInterval, compositeToPresentLatency);
switch (ret) {
- case 0:
- return EGL_TRUE;
- case -ENOSYS:
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- default:
- // This should not happen. Return an error that is not in the spec
- // so it's obvious something is very wrong.
- ALOGE("eglGetCompositorTiming: Unexpected error.");
- return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ case 0:
+ return EGL_TRUE;
+ case -ENOSYS:
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ default:
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ ALOGE("eglGetCompositorTiming: Unexpected error.");
+ return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
}
}
-EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint name)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLint name) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
ANativeWindow* window = s->getNativeWindow();
if (!window) {
@@ -2420,20 +2277,19 @@
}
EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
- EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
- EGLnsecsANDROID *values)
-{
- const egl_display_ptr dp = validate_display(dpy);
+ EGLuint64KHR frameId, EGLint numTimestamps,
+ const EGLint* timestamps, EGLnsecsANDROID* values) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
if (!s->getNativeWindow()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
@@ -2483,10 +2339,11 @@
}
}
- int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
- requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
- lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
- dequeueReadyTime, releaseTime);
+ int ret =
+ native_window_get_frame_timestamps(s->getNativeWindow(), frameId, requestedPresentTime,
+ acquireTime, latchTime, firstRefreshStartTime,
+ lastRefreshStartTime, gpuCompositionDoneTime,
+ displayPresentTime, dequeueReadyTime, releaseTime);
switch (ret) {
case 0:
@@ -2505,20 +2362,19 @@
}
}
-EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(
- EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
-{
- const egl_display_ptr dp = validate_display(dpy);
+EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLint timestamp) {
+ const egl_display_t* dp = validate_display(dpy);
if (!dp) {
return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
}
- SurfaceRef _s(dp.get(), surface);
+ SurfaceRef _s(dp, surface);
if (!_s.get()) {
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
}
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t const* const s = get_surface(surface);
ANativeWindow* window = s->getNativeWindow();
if (!window) {
@@ -2540,8 +2396,7 @@
return EGL_TRUE;
case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
int value = 0;
- window->query(window,
- NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+ window->query(window, NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
return value == 0 ? EGL_FALSE : EGL_TRUE;
}
default:
@@ -2549,25 +2404,25 @@
}
}
-const GLubyte * glGetStringImpl(GLenum name) {
- const GLubyte * ret = egl_get_string_for_current_context(name);
+const GLubyte* glGetStringImpl(GLenum name) {
+ const GLubyte* ret = egl_get_string_for_current_context(name);
if (ret == NULL) {
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if(_c) ret = _c->glGetString(name);
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
+ if (_c) ret = _c->glGetString(name);
}
return ret;
}
-const GLubyte * glGetStringiImpl(GLenum name, GLuint index) {
- const GLubyte * ret = egl_get_string_for_current_context(name, index);
+const GLubyte* glGetStringiImpl(GLenum name, GLuint index) {
+ const GLubyte* ret = egl_get_string_for_current_context(name, index);
if (ret == NULL) {
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if(_c) ret = _c->glGetStringi(name, index);
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
+ if (_c) ret = _c->glGetStringi(name, index);
}
return ret;
}
-void glGetBooleanvImpl(GLenum pname, GLboolean * data) {
+void glGetBooleanvImpl(GLenum pname, GLboolean* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2576,11 +2431,11 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetBooleanv(pname, data);
}
-void glGetFloatvImpl(GLenum pname, GLfloat * data) {
+void glGetFloatvImpl(GLenum pname, GLfloat* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2589,11 +2444,11 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetFloatv(pname, data);
}
-void glGetIntegervImpl(GLenum pname, GLint * data) {
+void glGetIntegervImpl(GLenum pname, GLint* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2602,11 +2457,11 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetIntegerv(pname, data);
}
-void glGetInteger64vImpl(GLenum pname, GLint64 * data) {
+void glGetInteger64vImpl(GLenum pname, GLint64* data) {
if (pname == GL_NUM_EXTENSIONS) {
int num_exts = egl_get_num_extensions_for_current_context();
if (num_exts >= 0) {
@@ -2615,7 +2470,7 @@
}
}
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetInteger64v(pname, data);
}
@@ -2624,8 +2479,8 @@
EGLFuncPointer address;
};
+// clang-format off
static const implementation_map_t sPlatformImplMap[] = {
- // clang-format off
{ "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl },
{ "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl },
{ "eglInitialize", (EGLFuncPointer)&eglInitializeImpl },
@@ -2712,11 +2567,10 @@
{ "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl },
{ "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl },
{ "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl },
- // clang-format on
};
+// clang-format on
-EGLFuncPointer FindPlatformImplAddr(const char* name)
-{
+EGLFuncPointer FindPlatformImplAddr(const char* name) {
static const bool DEBUG = false;
if (name == nullptr) {
@@ -2730,7 +2584,8 @@
return nullptr;
}
if (!strcmp(name, sPlatformImplMap[i].name)) {
- ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name);
+ ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)",
+ (unsigned long long)sPlatformImplMap[i].address, i, name);
return sPlatformImplMap[i].address;
}
}
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index 8d118e0..dd1dcc2 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -16,10 +16,10 @@
#include "egl_tls.h"
-#include <stdlib.h>
-
#include <android-base/properties.h>
#include <log/log.h>
+#include <stdlib.h>
+
#include "CallStack.h"
#include "egl_platform_entries.h"
@@ -28,33 +28,46 @@
pthread_key_t egl_tls_t::sKey = TLS_KEY_NOT_INITIALIZED;
pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT;
-egl_tls_t::egl_tls_t()
- : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {
-}
+egl_tls_t::egl_tls_t() : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {}
-const char *egl_tls_t::egl_strerror(EGLint err) {
+const char* egl_tls_t::egl_strerror(EGLint err) {
switch (err) {
- case EGL_SUCCESS: return "EGL_SUCCESS";
- case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
- case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
- case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
- case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
- case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
- case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
- case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
- case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
- case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
- case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
- case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
- case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
- case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
- case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
- default: return "UNKNOWN";
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return "UNKNOWN";
}
}
-void egl_tls_t::validateTLSKey()
-{
+void egl_tls_t::validateTLSKey() {
struct TlsKeyInitializer {
static void create() { pthread_key_create(&sKey, destructTLSData); }
};
@@ -88,14 +101,12 @@
"EGL TLS data still exists after eglReleaseThread");
}
-void egl_tls_t::setErrorEtcImpl(
- const char* caller, int line, EGLint error, bool quiet) {
+void egl_tls_t::setErrorEtcImpl(const char* caller, int line, EGLint error, bool quiet) {
validateTLSKey();
egl_tls_t* tls = getTLS();
if (tls->error != error) {
if (!quiet) {
- ALOGE("%s:%d error %x (%s)",
- caller, line, error, egl_strerror(error));
+ ALOGE("%s:%d error %x (%s)", caller, line, error, egl_strerror(error));
if (base::GetBoolProperty("debug.egl.callstack", false)) {
CallStack::log(LOG_TAG);
}
@@ -111,7 +122,6 @@
return true;
}
return false;
-
}
egl_tls_t* egl_tls_t::getTLS() {
@@ -161,10 +171,9 @@
if (sKey == TLS_KEY_NOT_INITIALIZED) {
return EGL_NO_CONTEXT;
}
- egl_tls_t* tls = (egl_tls_t *)pthread_getspecific(sKey);
+ egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey);
if (!tls) return EGL_NO_CONTEXT;
return tls->ctx;
}
-
} // namespace android
diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h
index 86a375c..b5fcc1a 100644
--- a/opengl/libs/EGL/egl_tls.h
+++ b/opengl/libs/EGL/egl_tls.h
@@ -18,12 +18,9 @@
#define ANDROID_EGL_TLS_H
#include <EGL/egl.h>
-
#include <pthread.h>
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
class DbgContext;
@@ -32,15 +29,14 @@
static pthread_key_t sKey;
static pthread_once_t sOnceKey;
- EGLint error;
- EGLContext ctx;
- bool logCallWithNoContext;
+ EGLint error;
+ EGLContext ctx;
+ bool logCallWithNoContext;
egl_tls_t();
static void validateTLSKey();
static void destructTLSData(void* data);
- static void setErrorEtcImpl(
- const char* caller, int line, EGLint error, bool quiet);
+ static void setErrorEtcImpl(const char* caller, int line, EGLint error, bool quiet);
public:
static egl_tls_t* getTLS();
@@ -50,24 +46,20 @@
static void setContext(EGLContext ctx);
static EGLContext getContext();
static bool logNoContextCall();
- static const char *egl_strerror(EGLint err);
+ static const char* egl_strerror(EGLint err);
- template<typename T>
- static T setErrorEtc(const char* caller,
- int line, EGLint error, T returnValue, bool quiet = false) {
+ template <typename T>
+ static T setErrorEtc(const char* caller, int line, EGLint error, T returnValue,
+ bool quiet = false) {
setErrorEtcImpl(caller, line, error, quiet);
return returnValue;
}
};
-#define setError(_e, _r) \
- egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
+#define setError(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
-#define setErrorQuiet(_e, _r) \
- egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r, true)
+#define setErrorQuiet(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r, true)
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif // ANDROID_EGL_TLS_H
diff --git a/opengl/libs/EGL/egl_trace.h b/opengl/libs/EGL/egl_trace.h
index 7664de2..ffdf676 100644
--- a/opengl/libs/EGL/egl_trace.h
+++ b/opengl/libs/EGL/egl_trace.h
@@ -18,16 +18,14 @@
#if defined(__ANDROID__)
-#include <stdint.h>
-
#include <cutils/trace.h>
+#include <stdint.h>
// See <cutils/trace.h> for more ATRACE_* macros.
// ATRACE_NAME traces from its location until the end of its enclosing scope.
-#define _PASTE(x, y) x ## y
-#define PASTE(x, y) _PASTE(x,y)
-#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+#define PASTE(x, y) x##y
+#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__)(ATRACE_TAG, name)
// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
@@ -36,13 +34,9 @@
class EglScopedTrace {
public:
- inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) {
- atrace_begin(mTag, name);
- }
+ inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); }
- inline ~EglScopedTrace() {
- atrace_end(mTag);
- }
+ inline ~EglScopedTrace() { atrace_end(mTag); }
private:
uint64_t mTag;
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 5fbffbd..fcc11f1 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -17,40 +17,32 @@
#ifndef ANDROID_EGLDEFS_H
#define ANDROID_EGLDEFS_H
+#include <log/log.h>
+
#include "../hooks.h"
#include "egl_platform_entries.h"
-#include <log/log.h>
-
#define VERSION_MAJOR 1
#define VERSION_MINOR 4
#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
-// EGLDisplay are global, not attached to a given thread
+// EGLDisplay are global, not attached to a given thread
const unsigned int NUM_DISPLAYS = 1;
-// ----------------------------------------------------------------------------
+extern const char* const platform_names[];
-extern char const * const platform_names[];
-
-// clang-format off
struct egl_connection_t {
- enum {
- GLESv1_INDEX = 0,
- GLESv2_INDEX = 1
- };
+ enum { GLESv1_INDEX = 0, GLESv2_INDEX = 1 };
- inline egl_connection_t() : dso(nullptr),
- libEgl(nullptr),
- libGles1(nullptr),
- libGles2(nullptr),
- systemDriverUnloaded(false) {
-
- char const* const* entries = platform_names;
+ inline egl_connection_t()
+ : dso(nullptr),
+ libEgl(nullptr),
+ libGles1(nullptr),
+ libGles2(nullptr),
+ systemDriverUnloaded(false) {
+ const char* const* entries = platform_names;
EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
while (*entries) {
const char* name = *entries;
@@ -66,41 +58,34 @@
}
}
- void * dso;
- gl_hooks_t * hooks[2];
- EGLint major;
- EGLint minor;
- EGLint driverVersion;
- egl_t egl;
+ void* dso;
+ gl_hooks_t* hooks[2];
+ EGLint major;
+ EGLint minor;
+ EGLint driverVersion;
+ egl_t egl;
// Functions implemented or redirected by platform libraries
- platform_impl_t platform;
+ platform_impl_t platform;
- void* libEgl;
- void* libGles1;
- void* libGles2;
+ void* libEgl;
+ void* libGles1;
+ void* libGles2;
- bool systemDriverUnloaded;
- bool useAngle; // Was ANGLE successfully loaded
+ bool systemDriverUnloaded;
+ bool useAngle; // Was ANGLE successfully loaded
};
-// clang-format on
-
-// ----------------------------------------------------------------------------
extern gl_hooks_t gHooks[2];
extern gl_hooks_t gHooksNoContext;
extern pthread_key_t gGLWrapperKey;
extern "C" void gl_unimplemented();
extern "C" void gl_noop();
-
-extern char const * const gl_names[];
-extern char const * const gl_names_1[];
-extern char const * const egl_names[];
-
+extern const char* const gl_names[];
+extern const char* const gl_names_1[];
+extern const char* const egl_names[];
extern egl_connection_t gEGLImpl;
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
#endif /* ANDROID_EGLDEFS_H */
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index fedc789..b3d6f74 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -16,15 +16,12 @@
#include <ctype.h>
#include <errno.h>
-#include <stdlib.h>
-
#include <log/log.h>
+#include <stdlib.h>
#include "egldefs.h"
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
#undef API_ENTRY
#undef CALL_GL_EXTENSION_API
@@ -34,6 +31,7 @@
#undef GL_EXTENSION_LIST
#undef GET_TLS
+// clang-format off
#if defined(__arm__)
#define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n"
@@ -239,13 +237,13 @@
name(248) name(249) name(250) name(251) name(252) name(253) name(254) name(255)
-GL_EXTENSION_LIST( GL_EXTENSION )
+GL_EXTENSION_LIST(GL_EXTENSION)
-#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n),
+#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n),
+// clang-format on
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {
- GL_EXTENSION_LIST( GL_EXTENSION_ARRAY )
- };
+extern const __eglMustCastToProperFunctionPointerType
+ gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {GL_EXTENSION_LIST(GL_EXTENSION_ARRAY)};
#undef GET_TLS
#undef GL_EXTENSION_LIST
@@ -255,7 +253,4 @@
#undef API_ENTRY
#undef CALL_GL_EXTENSION_API
-// ----------------------------------------------------------------------------
}; // namespace android
-// ----------------------------------------------------------------------------
-
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
index 245edb8..c78322e 100644
--- a/services/gpuservice/gpumem/GpuMem.cpp
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -25,6 +25,7 @@
#include <libbpf_android.h>
#include <log/log.h>
#include <unistd.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include <unordered_map>
@@ -128,4 +129,24 @@
}
}
+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
index 49a9f95..de691e2 100644
--- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -20,6 +20,8 @@
#include <utils/String16.h>
#include <utils/Vector.h>
+#include <functional>
+
namespace android {
class GpuMem {
@@ -33,27 +35,9 @@
void dump(const Vector<String16>& args, std::string* result);
bool isInitialized() { return mInitialized.load(); }
- // Traverse the map and send each value read back to the callback function.
- // Used for tracing.
- template <typename lambda>
- void traceGpuMemTotals(lambda tracerCallback) {
- 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();
-
- tracerCallback(gpu_id, pid, size);
- res = mGpuMemTotalMap.getNextKey(key);
- if (!res.ok()) break;
- key = res.value();
- }
- }
+ // 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.
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index abaf30a..45e8367 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -41,6 +41,8 @@
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:
@@ -143,5 +145,37 @@
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/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
index 9e01a64..6366e1d 100644
--- a/services/gpuservice/tracing/GpuMemTracer.cpp
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -23,9 +23,7 @@
#include <gpumem/GpuMem.h>
#include <perfetto/trace/android/gpu_mem_event.pbzero.h>
#include <unistd.h>
-#include <utils/Timers.h>
-#include <algorithm>
#include <thread>
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::GpuMemTracer::GpuMemDataSource);
@@ -79,10 +77,10 @@
ALOGE("Cannot trace without GpuMem initialization");
return;
}
- mGpuMem->traceGpuMemTotals([](uint32_t gpuId, uint32_t pid, uint64_t size) {
+ 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(systemTime());
+ packet->set_timestamp(ts);
auto* event = packet->set_gpu_mem_total_event();
event->set_gpu_id(gpuId);
event->set_pid(pid);
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index d1a3e9a..a0de607 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -22,7 +22,12 @@
"-Wno-unused-parameter",
"-Wthread-safety",
"-Wshadow",
+ "-Wshadow-field-in-constructor-modified",
+ "-Wshadow-uncaptured-local",
],
+ sanitize: {
+ misc_undefined: ["bounds"],
+ },
}
/////////////////////////////////////////////////
diff --git a/services/inputflinger/VibrationElement.cpp b/services/inputflinger/VibrationElement.cpp
index a69f5d0..aaf5834 100644
--- a/services/inputflinger/VibrationElement.cpp
+++ b/services/inputflinger/VibrationElement.cpp
@@ -25,38 +25,27 @@
namespace android {
-// The sentinel to use the default amplitude
-static const int DEFAULT_AMPLITUDE = -1;
-
-// The vibration magnitude for the "DEFAULT_AMPLITUDE" magnitude constant.
-static const uint16_t DEFAULT_MAGNITUDE = 0xc000;
-
-void VibrationElement::dump(std::string& dump) const {
+const std::string VibrationElement::toString() const {
+ std::string dump;
dump += StringPrintf("[duration=%lldms, channels=[", duration.count());
- if (channels.size()) {
- dump += std::to_string(channels[0]);
- std::for_each(channels.begin() + 1, channels.end(), [&dump](int channel) {
+ for (auto it = channels.begin(); it != channels.end(); ++it) {
+ dump += std::to_string(*it);
+ if (std::next(it) != channels.end()) {
dump += ", ";
- dump += std::to_string(channel);
- });
+ }
}
+
dump += "]]";
+ return dump;
}
-uint16_t VibrationElement::getChannel(int id) const {
- if (id >= (int)channels.size()) {
+uint16_t VibrationElement::getMagnitude(size_t channelIdx) const {
+ if (channelIdx >= channels.size()) {
return 0;
}
-
- // android framework uses DEFAULT_AMPLITUDE to signal that the vibration
- // should use some built-in default value, denoted here as DEFAULT_MAGNITUDE
- if (channels[id] == DEFAULT_AMPLITUDE) {
- return DEFAULT_MAGNITUDE;
- }
-
// convert range [0,255] to [0,65535] (android framework to linux ff ranges)
- return ((uint16_t)channels[id]) << 8;
+ return static_cast<uint16_t>(channels[channelIdx]) << 8;
}
bool VibrationElement::isOn() const {
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index b31980b..f99fffe 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -103,7 +103,7 @@
class FakeInputReceiver {
public:
void consumeEvent() {
- uint32_t consumeSeq;
+ uint32_t consumeSeq = 0;
InputEvent* event;
std::chrono::time_point start = std::chrono::steady_clock::now();
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index ded74ba..4328e03 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -185,7 +185,6 @@
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords, float xOffset, float yOffset)
: EventEntry(id, Type::MOTION, eventTime, policyFlags),
- eventTime(eventTime),
deviceId(deviceId),
source(source),
displayId(displayId),
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 5fd772e..d5b589e 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -156,7 +156,6 @@
};
struct MotionEntry : EventEntry {
- nsecs_t eventTime;
int32_t deviceId;
uint32_t source;
int32_t displayId;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5ba5ad8..5d71666 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3947,7 +3947,7 @@
}
mFocusedDisplayId = displayId;
- // Sanity check
+ // Find new focused window and validate
sp<InputWindowHandle> newFocusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
notifyFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
@@ -4245,7 +4245,7 @@
"ms\n",
windowInfo->ownerPid, windowInfo->ownerUid,
millis(windowInfo->dispatchingTimeout));
- windowInfo->transform.dump(dump, INDENT4 "transform=");
+ windowInfo->transform.dump(dump, "transform", INDENT4);
}
} else {
dump += INDENT2 "Windows: <none>\n";
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index f6958d4..d39113b 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -74,10 +74,10 @@
}
std::string InputTarget::getPointerInfoString() const {
- std::string out;
+ std::string out = "\n";
if (useDefaultPointerTransform()) {
const ui::Transform& transform = getDefaultPointerTransform();
- transform.dump(out, "default");
+ transform.dump(out, "default", " ");
return out;
}
@@ -86,9 +86,8 @@
continue;
}
- out += "\n";
const std::string name = "pointerId " + std::to_string(i) + ":";
- pointerTransforms[i].dump(out, name.c_str());
+ pointerTransforms[i].dump(out, name.c_str(), " ");
}
return out;
}
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 5a832e7..a24b293 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -39,11 +39,6 @@
// Must be at least 2.
#define MAX_VIBRATE_PATTERN_SIZE 100
-// Maximum allowable delay value in a vibration pattern before
-// which the delay will be truncated.
-#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
-#define MAX_VIBRATE_PATTERN_DELAY_MSECS (1000000 * 1000LL)
-
namespace android {
// --- InputReaderInterface ---
diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h
index 8a134ee..b60ffac 100644
--- a/services/inputflinger/include/VibrationElement.h
+++ b/services/inputflinger/include/VibrationElement.h
@@ -17,22 +17,25 @@
#ifndef _VIBRATION_ELEMENT_H
#define _VIBRATION_ELEMENT_H
+#include <array>
#include <chrono>
#include <cstdint>
#include <string>
-#include <vector>
namespace android {
+// evdev FF_RUMBLE effect only supports two channels of vibration.
+constexpr size_t CHANNEL_SIZE = 2;
/*
* Describes a rumble effect
*/
struct VibrationElement {
std::chrono::milliseconds duration;
- std::vector<int> channels;
+ // Channel amplitude range 0-255.
+ std::array<uint8_t, CHANNEL_SIZE> channels = {0, 0};
- void dump(std::string& dump) const;
- uint16_t getChannel(int id) const;
+ const std::string toString() const;
+ uint16_t getMagnitude(size_t channelIndex) const;
bool isOn() const;
};
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 8f34e8a..76b9419 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -61,6 +61,9 @@
// v4l2 devices go directly into /dev
static const char* VIDEO_DEVICE_PATH = "/dev";
+static constexpr size_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
+static constexpr size_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
+
static inline const char* toString(bool value) {
return value ? "true" : "false";
}
@@ -184,7 +187,6 @@
EventHub::Device::~Device() {
close();
- delete configuration;
}
void EventHub::Device::close() {
@@ -281,11 +283,14 @@
if (configurationFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
} else {
- status_t status = PropertyMap::load(String8(configurationFile.c_str()), &configuration);
+ PropertyMap* propertyMap;
+ status_t status = PropertyMap::load(String8(configurationFile.c_str()), &propertyMap);
if (status) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
identifier.name.c_str());
+ } else {
+ configuration = std::unique_ptr<PropertyMap>(propertyMap);
}
}
}
@@ -302,7 +307,7 @@
}
status_t EventHub::Device::loadKeyMapLocked() {
- return keyMap.load(identifier, configuration);
+ return keyMap.load(identifier, configuration.get());
}
bool EventHub::Device::isExternalDeviceLocked() {
@@ -412,8 +417,6 @@
: mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
mNextDeviceId(1),
mControllerNumbers(),
- mOpeningDevices(0),
- mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false),
mNeedToScanDevices(true),
@@ -468,8 +471,6 @@
EventHub::~EventHub(void) {
closeAllDevicesLocked();
- mClosingDevices.clear();
-
::close(mEpollFd);
::close(mINotifyFd);
::close(mWakeReadPipeFd);
@@ -834,8 +835,8 @@
effect.type = FF_RUMBLE;
effect.id = device->ffEffectId;
// evdev FF_RUMBLE effect only supports two channels of vibration.
- effect.u.rumble.strong_magnitude = element.getChannel(0);
- effect.u.rumble.weak_magnitude = element.getChannel(1);
+ effect.u.rumble.strong_magnitude = element.getMagnitude(FF_STRONG_MAGNITUDE_CHANNEL_IDX);
+ effect.u.rumble.weak_magnitude = element.getMagnitude(FF_WEAK_MAGNITUDE_CHANNEL_IDX);
effect.replay.length = element.duration.count();
effect.replay.delay = 0;
if (ioctl(device->fd, EVIOCSFF, &effect)) {
@@ -1484,7 +1485,8 @@
if (device->classes.test(InputDeviceClass::KEYBOARD)) {
// Register the keyboard as a built-in keyboard if it is eligible.
if (!keyMapStatus && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD &&
- isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) {
+ isEligibleBuiltInKeyboard(device->identifier, device->configuration.get(),
+ &device->keyMap)) {
mBuiltInKeyboardId = device->id;
}
@@ -1708,8 +1710,8 @@
void EventHub::closeAllDevicesLocked() {
mUnattachedVideoDevices.clear();
- for (const auto& [id, device] : mDevices) {
- closeDeviceLocked(*device);
+ while (!mDevices.empty()) {
+ closeDeviceLocked(*(mDevices.begin()->second));
}
}
@@ -1730,11 +1732,10 @@
}
releaseControllerNumberLocked(device.controllerNumber);
+ device.controllerNumber = 0;
device.close();
-
- // Move device to mClosingDevices
mClosingDevices.push_back(std::move(mDevices[device.id]));
- // Erase device from mDevices
+
mDevices.erase(device.id);
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 80e80cb..ff12d98 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -417,7 +417,7 @@
BitArray<INPUT_PROP_MAX> propBitmask;
std::string configurationFile;
- PropertyMap* configuration;
+ std::unique_ptr<PropertyMap> configuration;
std::unique_ptr<VirtualKeyMap> virtualKeyMap;
KeyMap keyMap;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 86229f9..e867c6f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -170,6 +170,8 @@
mRawSurfaceHeight(-1),
mSurfaceLeft(0),
mSurfaceTop(0),
+ mSurfaceRight(0),
+ mSurfaceBottom(0),
mPhysicalWidth(-1),
mPhysicalHeight(-1),
mPhysicalLeft(0),
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 8c1e224..ac7c266 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -91,8 +91,7 @@
const VibrationElement& element = mPattern[mIndex];
if (element.isOn()) {
#if DEBUG_VIBRATOR
- std::string description;
- element.dump(description);
+ std::string description = element.toString();
ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
description.c_str());
#endif
@@ -135,13 +134,13 @@
void VibratorInputMapper::dumpPattern(std::string& dump) const {
dump += "[";
- if (mPattern.size() > 0) {
- mPattern[0].dump(dump);
- std::for_each(mPattern.begin() + 1, mPattern.end(), [&dump](const auto& element) {
+ for (auto it = mPattern.begin(); it != mPattern.end(); ++it) {
+ dump += it->toString();
+ if (std::next(it) != mPattern.end()) {
dump += ", ";
- element.dump(dump);
- });
+ }
}
+
dump += "]";
}
diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/services/inputflinger/tests/BlockingQueue_test.cpp
index 0dea8d7..fd9d9d5 100644
--- a/services/inputflinger/tests/BlockingQueue_test.cpp
+++ b/services/inputflinger/tests/BlockingQueue_test.cpp
@@ -26,7 +26,7 @@
// --- BlockingQueueTest ---
/**
- * Sanity check of basic pop and push operation.
+ * Validate basic pop and push operation.
*/
TEST(BlockingQueueTest, Queue_AddAndRemove) {
constexpr size_t capacity = 10;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 4b00ee4..3872de5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1137,17 +1137,17 @@
// --- InstrumentedInputReader ---
class InstrumentedInputReader : public InputReader {
- std::shared_ptr<InputDevice> mNextDevice;
+ std::queue<std::shared_ptr<InputDevice>> mNextDevices;
public:
InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener)
- : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {}
+ : InputReader(eventHub, policy, listener) {}
virtual ~InstrumentedInputReader() {}
- void setNextDevice(std::shared_ptr<InputDevice> device) { mNextDevice = device; }
+ void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); }
std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
const std::string& location = "") {
@@ -1164,9 +1164,9 @@
protected:
virtual std::shared_ptr<InputDevice> createDeviceLocked(
int32_t eventHubId, const InputDeviceIdentifier& identifier) {
- if (mNextDevice) {
- std::shared_ptr<InputDevice> device(mNextDevice);
- mNextDevice = nullptr;
+ if (!mNextDevices.empty()) {
+ std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
+ mNextDevices.pop();
return device;
}
return InputReader::createDeviceLocked(eventHubId, identifier);
@@ -1415,7 +1415,7 @@
const PropertyMap* configuration) {
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, name);
FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(eventHubId, sources);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
addDevice(eventHubId, name, classes, configuration);
return mapper;
}
@@ -1452,7 +1452,7 @@
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
// Must add at least one mapper or the device will be ignored!
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
@@ -1661,7 +1661,7 @@
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
// Must add at least one mapper or the device will be ignored!
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
NotifyDeviceResetArgs resetArgs;
@@ -1694,7 +1694,7 @@
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
// Must add at least one mapper or the device will be ignored!
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
NotifyDeviceResetArgs resetArgs;
@@ -1710,7 +1710,7 @@
std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
FakeInputMapper& mapper =
device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_TOUCHSCREEN);
- mReader->setNextDevice(device);
+ mReader->pushNextDevice(device);
const uint8_t hdmi1 = 1;
@@ -1747,6 +1747,73 @@
ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
}
+TEST_F(InputReaderTest, WhenEnabledChanges_AllSubdevicesAreUpdated) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+ // Must add at least one mapper or the device will be ignored!
+ device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+ device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+ mReader->pushNextDevice(device);
+ mReader->pushNextDevice(device);
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
+
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_TRUE(device->isEnabled());
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+
+ disableDevice(deviceId);
+ mReader->loopOnce();
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_FALSE(device->isEnabled());
+ ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+ ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+
+ enableDevice(deviceId);
+ mReader->loopOnce();
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_TRUE(device->isEnabled());
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+ ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+}
+
+TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToSubdeviceMappers) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+ // Add two subdevices to device
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+ FakeInputMapper& mapperDevice1 =
+ device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+ FakeInputMapper& mapperDevice2 =
+ device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+ mReader->pushNextDevice(device);
+ mReader->pushNextDevice(device);
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr));
+
+ mapperDevice1.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+ mapperDevice2.setKeyCodeState(AKEYCODE_B, AKEY_STATE_DOWN);
+
+ ASSERT_EQ(AKEY_STATE_DOWN,
+ mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_A));
+ ASSERT_EQ(AKEY_STATE_DOWN,
+ mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_B));
+ ASSERT_EQ(AKEY_STATE_UNKNOWN,
+ mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_C));
+}
+
// --- InputReaderIntegrationTest ---
// These tests create and interact with the InputReader only through its interface.
@@ -2310,6 +2377,7 @@
T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
configureDevice(0);
mDevice->reset(ARBITRARY_TIME);
+ mapper.reset(ARBITRARY_TIME);
return mapper;
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index c55e9df..e341af1 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -169,15 +169,15 @@
"Scheduler/LayerInfo.cpp",
"Scheduler/LayerInfoV2.cpp",
"Scheduler/MessageQueue.cpp",
- "Scheduler/PhaseOffsets.cpp",
"Scheduler/RefreshRateConfigs.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
"Scheduler/Timer.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncPredictor.cpp",
- "Scheduler/VSyncModulator.cpp",
+ "Scheduler/VsyncModulator.cpp",
"Scheduler/VSyncReactor.cpp",
+ "Scheduler/VsyncConfiguration.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceFlingerDefaultFactory.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 6cc90cb..5c7f12d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -80,10 +80,6 @@
// The clip region, or visible region that is being rendered to
const Region& clip;
- // If true, the layer should use an identity transform for its position
- // transform. Used only by the captureScreen API call.
- const bool useIdentityTransform;
-
// If set to true, the layer should enable filtering when rendering.
const bool needsFiltering;
@@ -148,7 +144,6 @@
static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
const LayerFE::ClientCompositionTargetSettings& rhs) {
return lhs.clip.hasSameRects(rhs.clip) &&
- lhs.useIdentityTransform == rhs.useIdentityTransform &&
lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport &&
@@ -170,7 +165,6 @@
*os << "ClientCompositionTargetSettings{";
*os << "\n .clip = \n";
PrintTo(settings.clip, os);
- *os << "\n .useIdentityTransform = " << settings.useIdentityTransform;
*os << "\n .needsFiltering = " << settings.needsFiltering;
*os << "\n .isSecure = " << settings.isSecure;
*os << "\n .supportsProtectedContent = " << settings.supportsProtectedContent;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 26299e9..6cc7a53 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,11 +163,12 @@
virtual void setCompositionEnabled(bool) = 0;
// Sets the projection state to use
- virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& destinationClip,
+ virtual void setProjection(const ui::Transform&, uint32_t orientation,
+ const Rect& orientedDisplaySpaceRect,
+ const Rect& layerStackSpaceRect, const Rect& displaySpaceRect,
bool needsFiltering) = 0;
// Sets the bounds to use
- virtual void setBounds(const ui::Size&) = 0;
+ virtual void setDisplaySpaceSize(const ui::Size&) = 0;
// Sets the layer stack filtering settings for this output. See
// belongsInOutput for full details.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
new file mode 100644
index 0000000..9d15665
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ostream>
+
+#include <android-base/stringprintf.h>
+#include <ui/Rect.h>
+#include <ui/Rotation.h>
+#include <ui/Transform.h>
+
+namespace android {
+namespace compositionengine {
+
+// Geometrical space to which content is projected.
+// For example, this can be the layer space or the physical display space.
+struct ProjectionSpace {
+ ProjectionSpace() = default;
+ ProjectionSpace(ui::Size size, Rect content)
+ : bounds(std::move(size)), content(std::move(content)) {}
+
+ // Bounds of this space. Always starts at (0,0).
+ Rect bounds;
+
+ // Rect onto which content is projected.
+ Rect content;
+};
+
+} // namespace compositionengine
+
+inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
+ return android::base::StringPrintf("ProjectionSpace(bounds = %s, content = %s)",
+ to_string(space.bounds).c_str(),
+ to_string(space.content).c_str());
+}
+
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(const android::compositionengine::ProjectionSpace& space, ::std::ostream* os) {
+ *os << to_string(space);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 0ac2545..57b7a97 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -38,10 +38,10 @@
bool isValid() const override;
std::optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
- void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& destinationClip,
- bool needsFiltering) override;
- void setBounds(const ui::Size&) override;
+ void setProjection(const ui::Transform&, uint32_t orientation,
+ const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
+ const Rect& displaySpaceRect, bool needsFiltering) override;
+ void setDisplaySpaceSize(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 7120a48..462d952 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -29,6 +29,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+#include <compositionengine/ProjectionSpace.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Transform.h>
@@ -50,8 +51,7 @@
// If true, the current frame on this output uses device composition
bool usesDeviceComposition{false};
- // If true, the client target should be flipped when performing client
- // composition
+ // If true, the client target should be flipped when performing client composition
bool flipClientTarget{false};
// If true, the current frame reused the buffer from a previous client composition
@@ -63,25 +63,26 @@
// The layer stack to display on this display
uint32_t layerStackId{~0u};
- // The physical space screen bounds
- Rect bounds;
+ // The common space for all layers in the layer stack. layerStackSpace.content is the Rect
+ // which gets projected on the display. The content in this space is always in a single
+ // orientation.
+ ProjectionSpace layerStackSpace;
- // The logical to physical transformation to use
+ // Oriented physical display space. It will have the same size as displaySpace oriented to
+ // match the orientation of layerStackSpace. The content in this space is always in a single
+ // orientation.
+ ProjectionSpace orientedDisplaySpace;
+
+ // The space of the physical display. It is as big as the currently active display mode. The
+ // content in this space can be rotated.
+ ProjectionSpace displaySpace;
+
+ // Transformation from layerStackSpace to displaySpace
ui::Transform transform;
- // The physical orientation of the display, expressed as ui::Transform
- // orientation flags.
+ // The physical orientation of the display, expressed as ui::Transform orientation flags.
uint32_t orientation{0};
- // The logical space user visible bounds
- Rect frame;
-
- // The logical space user viewport rectangle
- Rect viewport;
-
- // The physical space destination clip rectangle
- Rect destinationClip;
-
// If true, RenderEngine filtering should be enabled
bool needsFiltering{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index c4dff73..375d334 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -38,7 +38,7 @@
MOCK_METHOD1(setCompositionEnabled, void(bool));
MOCK_METHOD6(setProjection,
void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&, bool));
- MOCK_METHOD1(setBounds, void(const ui::Size&));
+ MOCK_METHOD1(setDisplaySpaceSize, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 9598430..9d1bb02 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -77,6 +77,7 @@
void dumpVal(std::string& out, const char* name, const ui::Transform& transform) {
transform.dump(out, name);
+ out.append(" ");
}
void dumpVal(std::string& out, const char* name, const ui::Size& size) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 09b3dd7..9e0a43a 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -105,24 +105,32 @@
dirtyEntireOutput();
}
-void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& destinationClip, bool needsFiltering) {
+void Output::setProjection(const ui::Transform& transform, uint32_t orientation,
+ const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
+ const Rect& displaySpaceRect, bool needsFiltering) {
auto& outputState = editState();
outputState.transform = transform;
outputState.orientation = orientation;
- outputState.destinationClip = destinationClip;
- outputState.frame = frame;
- outputState.viewport = viewport;
+ outputState.displaySpace.content = displaySpaceRect;
+ // outputState.displaySpace.bounds should be already set from setDisplaySpaceSize().
+ outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+
+ ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
+ if (orientation == ui::Transform::ROT_90 || orientation == ui::Transform::ROT_270) {
+ std::swap(orientedSize.width, orientedSize.height);
+ }
+ outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
+
+ outputState.layerStackSpace.content = layerStackSpaceRect;
+ outputState.layerStackSpace.bounds = layerStackSpaceRect;
outputState.needsFiltering = needsFiltering;
dirtyEntireOutput();
}
-// TODO(b/121291683): Rename setSize() once more is moved.
-void Output::setBounds(const ui::Size& size) {
+void Output::setDisplaySpaceSize(const ui::Size& size) {
mRenderSurface->setDisplaySize(size);
- // TODO(b/121291683): Rename outputState.size once more is moved.
- editState().bounds = Rect(mRenderSurface->getSize());
+ editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
dirtyEntireOutput();
}
@@ -230,7 +238,7 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- editState().bounds = Rect(mRenderSurface->getSize());
+ editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
dirtyEntireOutput();
}
@@ -249,7 +257,7 @@
Region Output::getDirtyRegion(bool repaintEverything) const {
const auto& outputState = getState();
- Region dirty(outputState.viewport);
+ Region dirty(outputState.layerStackSpace.content);
if (!repaintEverything) {
dirty.andSelf(outputState.dirtyRegion);
}
@@ -334,7 +342,7 @@
// Compute the resulting coverage for this output, and store it for later
const ui::Transform& tr = outputState.transform;
- Region undefinedRegion{outputState.bounds};
+ Region undefinedRegion{outputState.displaySpace.bounds};
undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
outputState.undefinedRegion = undefinedRegion;
@@ -537,7 +545,7 @@
// TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
const auto& outputState = getState();
Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
- drawRegion.andSelf(outputState.bounds);
+ drawRegion.andSelf(outputState.displaySpace.bounds);
if (drawRegion.isEmpty()) {
return;
}
@@ -554,8 +562,8 @@
outputLayerState.visibleRegion = visibleRegion;
outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
outputLayerState.coveredRegion = coveredRegion;
- outputLayerState.outputSpaceVisibleRegion =
- outputState.transform.transform(visibleNonShadowRegion.intersect(outputState.viewport));
+ outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
+ visibleNonShadowRegion.intersect(outputState.layerStackSpace.content));
outputLayerState.shadowRegion = shadowRegion;
}
@@ -860,8 +868,8 @@
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
- clientCompositionDisplay.clip = outputState.viewport;
+ clientCompositionDisplay.physicalDisplay = outputState.displaySpace.content;
+ clientCompositionDisplay.clip = outputState.layerStackSpace.content;
clientCompositionDisplay.orientation = outputState.orientation;
clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
? outputState.dataspace
@@ -945,8 +953,7 @@
ALOGV("Rendering client layers");
const auto& outputState = getState();
- const Region viewportRegion(outputState.viewport);
- const bool useIdentityTransform = false;
+ const Region viewportRegion(outputState.layerStackSpace.content);
bool firstLayer = true;
// Used when a layer clears part of the buffer.
Region stubRegion;
@@ -982,18 +989,17 @@
!layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
if (clientComposition || clearClientComposition) {
- compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
- clip,
- useIdentityTransform,
- layer->needsFiltering() || outputState.needsFiltering,
- outputState.isSecure,
- supportsProtectedContent,
- clientComposition ? clearRegion : stubRegion,
- outputState.viewport,
- outputDataspace,
- realContentIsVisible,
- !clientComposition, /* clearContent */
- };
+ compositionengine::LayerFE::ClientCompositionTargetSettings
+ targetSettings{.clip = clip,
+ .needsFiltering =
+ layer->needsFiltering() || outputState.needsFiltering,
+ .isSecure = outputState.isSecure,
+ .supportsProtectedContent = supportsProtectedContent,
+ .clearRegion = clientComposition ? clearRegion : stubRegion,
+ .viewport = outputState.layerStackSpace.content,
+ .dataspace = outputDataspace,
+ .realContentIsVisible = realContentIsVisible,
+ .clearContent = !clientComposition};
std::vector<LayerFE::LayerSettings> results =
layerFE.prepareClientCompositionList(targetSettings);
if (realContentIsVisible && !results.empty()) {
@@ -1090,7 +1096,7 @@
void Output::dirtyEntireOutput() {
auto& outputState = editState();
- outputState.dirtyRegion.set(outputState.bounds);
+ outputState.dirtyRegion.set(outputState.displaySpace.bounds);
}
void Output::chooseCompositionStrategy() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index f3b2da1..776fdde 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -37,11 +37,12 @@
dumpVal(out, "transform", transform);
out.append("\n ");
-
- dumpVal(out, "bounds", bounds);
- dumpVal(out, "frame", frame);
- dumpVal(out, "viewport", viewport);
- dumpVal(out, "destinationClip", destinationClip);
+ dumpVal(out, "layerStackSpace", to_string(layerStackSpace));
+ out.append("\n ");
+ dumpVal(out, "orientedDisplaySpace", to_string(orientedDisplaySpace));
+ out.append("\n ");
+ dumpVal(out, "displaySpace", to_string(displaySpace));
+ out.append("\n ");
dumpVal(out, "needsFiltering", needsFiltering);
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 1faf775..376b4b3 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -77,7 +77,7 @@
FloatRect activeCropFloat =
reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
- const Rect& viewport = getOutput().getState().viewport;
+ const Rect& viewport = getOutput().getState().layerStackSpace.content;
const ui::Transform& layerTransform = layerState.geomLayerTransform;
const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
// Transform to screen space.
@@ -189,7 +189,7 @@
Rect activeCrop = layerState.geomCrop;
if (!activeCrop.isEmpty() && bufferSize.isValid()) {
activeCrop = layerTransform.transform(activeCrop);
- if (!activeCrop.intersect(outputState.viewport, &activeCrop)) {
+ if (!activeCrop.intersect(outputState.layerStackSpace.content, &activeCrop)) {
activeCrop.clear();
}
activeCrop = inverseLayerTransform.transform(activeCrop, true);
@@ -215,7 +215,7 @@
// transformation. We then round upon constructing 'frame'.
Rect frame{
layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))};
- if (!frame.intersect(outputState.viewport, &frame)) {
+ if (!frame.intersect(outputState.layerStackSpace.content, &frame)) {
frame.clear();
}
const ui::Transform displayTransform{outputState.transform};
@@ -568,7 +568,7 @@
const auto& outputState = getOutput().getState();
Rect frame = layerFEState->cursorFrame;
- frame.intersect(outputState.viewport, &frame);
+ frame.intersect(outputState.layerStackSpace.content, &frame);
Rect position = outputState.transform.transform(frame);
if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 378c050..4519a9d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -908,7 +908,7 @@
mDisplay->editState().isEnabled = true;
mDisplay->editState().usesClientComposition = false;
- mDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
@@ -929,7 +929,7 @@
nonHwcDisplay->editState().isEnabled = true;
nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
@@ -950,7 +950,7 @@
nonHwcDisplay->editState().isEnabled = true;
nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
nonHwcDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
CompositionRefreshArgs refreshArgs;
@@ -971,7 +971,7 @@
nonHwcDisplay->editState().isEnabled = true;
nonHwcDisplay->editState().usesClientComposition = false;
- nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+ nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
CompositionRefreshArgs refreshArgs;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 020f93a..df3da85 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -138,7 +138,7 @@
mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
mLayerFEState.geomBufferTransform = TR_IDENT;
- mOutputState.viewport = Rect{0, 0, 1920, 1080};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
}
FloatRect calculateOutputSourceCrop() {
@@ -223,7 +223,7 @@
}
TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
- mOutputState.viewport = Rect{0, 0, 960, 540};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
const FloatRect expected{0.f, 0.f, 960.f, 540.f};
EXPECT_THAT(calculateOutputSourceCrop(), expected);
@@ -245,7 +245,7 @@
mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
- mOutputState.viewport = Rect{0, 0, 1920, 1080};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
mOutputState.transform = ui::Transform{TR_IDENT};
}
@@ -293,7 +293,7 @@
}
TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
- mOutputState.viewport = Rect{0, 0, 960, 540};
+ mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
const Rect expected{0, 0, 960, 540};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
@@ -988,7 +988,7 @@
mLayerFEState.cursorFrame = kDefaultCursorFrame;
- mOutputState.viewport = kDefaultDisplayViewport;
+ mOutputState.layerStackSpace.content = kDefaultDisplayViewport;
mOutputState.transform = ui::Transform{kDefaultTransform};
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 6ba06a6..3dd26c0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -136,7 +136,7 @@
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
- mOutput->editState().bounds = kDefaultDisplaySize;
+ mOutput->editState().displaySpace.bounds = kDefaultDisplaySize;
}
void injectOutputLayer(InjectedLayer& layer) {
@@ -248,14 +248,14 @@
EXPECT_THAT(mOutput->getState().transform, transform);
EXPECT_EQ(orientation, mOutput->getState().orientation);
- EXPECT_EQ(frame, mOutput->getState().frame);
- EXPECT_EQ(viewport, mOutput->getState().viewport);
- EXPECT_EQ(destinationClip, mOutput->getState().destinationClip);
+ EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
+ EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+ EXPECT_EQ(destinationClip, mOutput->getState().displaySpace.content);
EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering);
}
/*
- * Output::setBounds()
+ * Output::setDisplaySpaceSize()
*/
TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
@@ -264,9 +264,9 @@
EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
- mOutput->setBounds(displaySize);
+ mOutput->setDisplaySpaceSize(displaySize);
- EXPECT_EQ(Rect(displaySize), mOutput->getState().bounds);
+ EXPECT_EQ(Rect(displaySize), mOutput->getState().displaySpace.bounds);
EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
}
@@ -429,7 +429,7 @@
mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
- EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().bounds);
+ EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().displaySpace.bounds);
}
/*
@@ -438,7 +438,7 @@
TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
const Rect viewport{100, 200};
- mOutput->editState().viewport = viewport;
+ mOutput->editState().layerStackSpace.content = viewport;
mOutput->editState().dirtyRegion.set(50, 300);
{
@@ -450,7 +450,7 @@
TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
const Rect viewport{100, 200};
- mOutput->editState().viewport = viewport;
+ mOutput->editState().layerStackSpace.content = viewport;
mOutput->editState().dirtyRegion.set(50, 300);
{
@@ -858,7 +858,7 @@
OutputRebuildLayerStacksTest() {
mOutput.mState.isEnabled = true;
mOutput.mState.transform = kIdentityTransform;
- mOutput.mState.bounds = kOutputBounds;
+ mOutput.mState.displaySpace.bounds = kOutputBounds;
mRefreshArgs.updatingOutputGeometryThisFrame = true;
@@ -1065,8 +1065,8 @@
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
.WillRepeatedly(Return(&mLayer.outputLayer));
- mOutput.mState.bounds = Rect(0, 0, 200, 300);
- mOutput.mState.viewport = Rect(0, 0, 200, 300);
+ mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300);
+ mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300);
mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
mLayer.layerFEState.isVisible = true;
@@ -1146,7 +1146,7 @@
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
- mOutput.mState.bounds = Rect(0, 0, 0, 0);
+ mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0);
ensureOutputLayerIfVisible();
}
@@ -1343,7 +1343,7 @@
mLayer.layerFEState.contentDirty = true;
mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mOutput.mState.viewport = Rect(0, 0, 300, 200);
+ mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
@@ -1369,7 +1369,7 @@
mLayer.layerFEState.contentDirty = true;
mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mOutput.mState.viewport = Rect(0, 0, 300, 200);
+ mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
@@ -2783,9 +2783,9 @@
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE);
- mOutput.mState.frame = kDefaultOutputFrame;
- mOutput.mState.viewport = kDefaultOutputViewport;
- mOutput.mState.destinationClip = kDefaultOutputDestinationClip;
+ mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
+ mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
+ mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
mOutput.mState.orientation = kDefaultOutputOrientation;
mOutput.mState.dataspace = kDefaultOutputDataspace;
@@ -3409,9 +3409,9 @@
struct GenerateClientCompositionRequestsTest_ThreeLayers
: public GenerateClientCompositionRequestsTest {
GenerateClientCompositionRequestsTest_ThreeLayers() {
- mOutput.mState.frame = kDisplayFrame;
- mOutput.mState.viewport = kDisplayViewport;
- mOutput.mState.destinationClip = kDisplayDestinationClip;
+ mOutput.mState.orientedDisplaySpace.content = kDisplayFrame;
+ mOutput.mState.layerStackSpace.content = kDisplayViewport;
+ mOutput.mState.displaySpace.content = kDisplayDestinationClip;
mOutput.mState.transform = ui::Transform{kDisplayOrientation};
mOutput.mState.orientation = kDisplayOrientation;
mOutput.mState.needsFiltering = false;
@@ -3579,7 +3579,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3591,7 +3590,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3635,7 +3633,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(Rect(10, 10, 20, 20)),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3647,7 +3644,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(Rect(0, 0, 30, 30)),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3659,7 +3655,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(Rect(0, 0, 40, 201)),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3691,7 +3686,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3703,7 +3697,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3715,7 +3708,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3747,7 +3739,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3760,7 +3751,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3772,7 +3762,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -3803,7 +3792,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
@@ -3815,7 +3803,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
@@ -3827,7 +3814,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
@@ -3856,7 +3842,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
@@ -3868,7 +3853,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
@@ -3880,7 +3864,6 @@
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
@@ -3941,9 +3924,9 @@
const uint32_t kPortraitOrientation = TR_ROT_90;
constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
- mOutput.mState.frame = kPortraitFrame;
- mOutput.mState.viewport = kPortraitViewport;
- mOutput.mState.destinationClip = kPortraitDestinationClip;
+ mOutput.mState.orientedDisplaySpace.content = kPortraitFrame;
+ mOutput.mState.layerStackSpace.content = kPortraitViewport;
+ mOutput.mState.displaySpace.content = kPortraitDestinationClip;
mOutput.mState.transform = ui::Transform{kPortraitOrientation};
mOutput.mState.orientation = kPortraitOrientation;
mOutput.mState.needsFiltering = false;
@@ -3972,7 +3955,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
Region(Rect(0, 0, 1000, 1000)),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
true, /* supports protected content */
@@ -3990,7 +3972,6 @@
compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
Region(Rect(1000, 0, 2000, 1000)),
- false, /* identity transform */
false, /* needs filtering */
true, /* secure */
true, /* supports protected content */
@@ -4024,7 +4005,6 @@
Region accumClearRegion(Rect(10, 11, 12, 13));
compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
@@ -4070,7 +4050,6 @@
Region accumClearRegion(Rect(10, 11, 12, 13));
compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
- false, /* identity transform */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a4fc833..016b6ca 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -29,6 +29,7 @@
#include <compositionengine/DisplayColorProfileCreationArgs.h>
#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/ProjectionSpace.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/RenderSurfaceCreationArgs.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -101,11 +102,11 @@
}
int DisplayDevice::getWidth() const {
- return mCompositionDisplay->getState().bounds.getWidth();
+ return mCompositionDisplay->getState().displaySpace.bounds.getWidth();
}
int DisplayDevice::getHeight() const {
- return mCompositionDisplay->getState().bounds.getHeight();
+ return mCompositionDisplay->getState().displaySpace.bounds.getHeight();
}
void DisplayDevice::setDisplayName(const std::string& displayName) {
@@ -155,13 +156,14 @@
}
void DisplayDevice::setDisplaySize(int width, int height) {
- mCompositionDisplay->setBounds(ui::Size(width, height));
+ mCompositionDisplay->setDisplaySpaceSize(ui::Size(width, height));
}
-void DisplayDevice::setProjection(ui::Rotation orientation, Rect viewport, Rect frame) {
+void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
+ Rect orientedDisplaySpaceRect) {
mOrientation = orientation;
- const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
+ const Rect& displayBounds = getCompositionDisplay()->getState().displaySpace.bounds;
const int displayWidth = displayBounds.width();
const int displayHeight = displayBounds.height();
@@ -171,40 +173,38 @@
rotation.set(flags, displayWidth, displayHeight);
}
- if (!frame.isValid()) {
+ if (!orientedDisplaySpaceRect.isValid()) {
// the destination frame can be invalid if it has never been set,
// in that case we assume the whole display frame.
- frame = Rect(displayWidth, displayHeight);
+ orientedDisplaySpaceRect = Rect(displayWidth, displayHeight);
}
- if (viewport.isEmpty()) {
- // viewport can be invalid if it has never been set, in that case
+ if (layerStackSpaceRect.isEmpty()) {
+ // layerStackSpaceRect can be invalid if it has never been set, in that case
// we assume the whole display size.
- // it's also invalid to have an empty viewport, so we handle that
+ // It's also invalid to have an empty layerStackSpaceRect, so we handle that
// case in the same way.
- viewport = Rect(displayWidth, displayHeight);
+ layerStackSpaceRect = Rect(displayWidth, displayHeight);
if (rotation.getOrientation() & ui::Transform::ROT_90) {
- // viewport is always specified in the logical orientation
- // of the display (ie: post-rotation).
- std::swap(viewport.right, viewport.bottom);
+ std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
}
}
ui::Transform logicalTranslation, physicalTranslation, scale;
- const float sourceWidth = viewport.width();
- const float sourceHeight = viewport.height();
- const float destWidth = frame.width();
- const float destHeight = frame.height();
+ const float sourceWidth = layerStackSpaceRect.width();
+ const float sourceHeight = layerStackSpaceRect.height();
+ const float destWidth = orientedDisplaySpaceRect.width();
+ const float destHeight = orientedDisplaySpaceRect.height();
if (sourceWidth != destWidth || sourceHeight != destHeight) {
const float scaleX = destWidth / sourceWidth;
const float scaleY = destHeight / sourceHeight;
scale.set(scaleX, 0, 0, scaleY);
}
- const float sourceX = viewport.left;
- const float sourceY = viewport.top;
- const float destX = frame.left;
- const float destY = frame.top;
+ const float sourceX = layerStackSpaceRect.left;
+ const float sourceY = layerStackSpaceRect.top;
+ const float destX = orientedDisplaySpaceRect.left;
+ const float destY = orientedDisplaySpaceRect.top;
logicalTranslation.set(-sourceX, -sourceY);
physicalTranslation.set(destX, destY);
@@ -217,7 +217,7 @@
}
}
- // The viewport and frame are both in the logical orientation.
+ // The layerStackSpaceRect and orientedDisplaySpaceRect are both in the logical orientation.
// Apply the logical translation, scale to physical size, apply the
// physical translation and finally rotate to the physical orientation.
ui::Transform globalTransform = rotation * physicalTranslation * scale * logicalTranslation;
@@ -226,12 +226,12 @@
const bool needsFiltering =
(!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
- Rect destinationClip = globalTransform.transform(viewport);
- if (destinationClip.isEmpty()) {
- destinationClip = displayBounds;
+ Rect displaySpaceRect = globalTransform.transform(layerStackSpaceRect);
+ if (displaySpaceRect.isEmpty()) {
+ displaySpaceRect = displayBounds;
}
- // Make sure the destination clip is contained in the display bounds
- destinationClip.intersect(displayBounds, &destinationClip);
+ // Make sure the displaySpaceRect is contained in the display bounds
+ displaySpaceRect.intersect(displayBounds, &displaySpaceRect);
uint32_t transformOrientation;
@@ -242,8 +242,9 @@
transformOrientation = ui::Transform::toRotationFlags(orientation);
}
- getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport,
- destinationClip, needsFiltering);
+ getCompositionDisplay()->setProjection(globalTransform, transformOrientation,
+ orientedDisplaySpaceRect, layerStackSpaceRect,
+ displaySpaceRect, needsFiltering);
}
ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
@@ -296,7 +297,7 @@
}
const Rect& DisplayDevice::getBounds() const {
- return mCompositionDisplay->getState().bounds;
+ return mCompositionDisplay->getState().displaySpace.bounds;
}
const Region& DisplayDevice::getUndefinedRegion() const {
@@ -315,12 +316,12 @@
return mCompositionDisplay->getState().transform;
}
-const Rect& DisplayDevice::getViewport() const {
- return mCompositionDisplay->getState().viewport;
+const Rect& DisplayDevice::getLayerStackSpaceRect() const {
+ return mCompositionDisplay->getState().layerStackSpace.content;
}
-const Rect& DisplayDevice::getFrame() const {
- return mCompositionDisplay->getState().frame;
+const Rect& DisplayDevice::getOrientedDisplaySpaceRect() const {
+ return mCompositionDisplay->getState().orientedDisplaySpace.content;
}
bool DisplayDevice::hasWideColorGamut() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 1319679..35a8b62 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -99,8 +99,8 @@
}
const ui::Transform& getTransform() const;
- const Rect& getViewport() const;
- const Rect& getFrame() const;
+ const Rect& getLayerStackSpaceRect() const;
+ const Rect& getOrientedDisplaySpaceRect() const;
bool needsFiltering() const;
ui::LayerStack getLayerStack() const;
@@ -208,8 +208,8 @@
std::optional<Physical> physical;
sp<IGraphicBufferProducer> surface;
ui::LayerStack layerStack = ui::NO_LAYER_STACK;
- Rect viewport;
- Rect frame;
+ Rect layerStackSpaceRect;
+ Rect orientedDisplaySpaceRect;
ui::Rotation orientation = ui::ROTATION_0;
uint32_t width = 0;
uint32_t height = 0;
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
index bcebf23..9a6b328 100644
--- a/services/surfaceflinger/DisplayRenderArea.cpp
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -20,49 +20,14 @@
namespace android {
namespace {
-RenderArea::RotationFlags applyDeviceOrientation(RenderArea::RotationFlags rotation,
+RenderArea::RotationFlags applyDeviceOrientation(bool useIdentityTransform,
const DisplayDevice& display) {
- uint32_t inverseRotate90 = 0;
- uint32_t inverseReflect = 0;
-
- // Reverse the logical orientation.
- ui::Rotation logicalOrientation = display.getOrientation();
- if (logicalOrientation == ui::Rotation::Rotation90) {
- logicalOrientation = ui::Rotation::Rotation270;
- } else if (logicalOrientation == ui::Rotation::Rotation270) {
- logicalOrientation = ui::Rotation::Rotation90;
+ if (!useIdentityTransform) {
+ return RenderArea::RotationFlags::ROT_0;
}
- const ui::Rotation orientation = display.getPhysicalOrientation() + logicalOrientation;
-
- switch (orientation) {
- case ui::ROTATION_0:
- return rotation;
-
- case ui::ROTATION_90:
- inverseRotate90 = ui::Transform::ROT_90;
- inverseReflect = ui::Transform::ROT_180;
- break;
-
- case ui::ROTATION_180:
- inverseReflect = ui::Transform::ROT_180;
- break;
-
- case ui::ROTATION_270:
- inverseRotate90 = ui::Transform::ROT_90;
- break;
- }
-
- const uint32_t rotate90 = rotation & ui::Transform::ROT_90;
- uint32_t reflect = rotation & ui::Transform::ROT_180;
-
- // Apply reflection for double rotation.
- if (rotate90 & inverseRotate90) {
- reflect = ~reflect & ui::Transform::ROT_180;
- }
-
- return static_cast<RenderArea::RotationFlags>((rotate90 ^ inverseRotate90) |
- (reflect ^ inverseReflect));
+ const ui::Rotation orientation = display.getPhysicalOrientation() + display.getOrientation();
+ return ui::Transform::toRotationFlags(orientation);
}
} // namespace
@@ -70,25 +35,24 @@
std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak,
const Rect& sourceCrop, ui::Size reqSize,
ui::Dataspace reqDataSpace,
- RotationFlags rotation,
+ bool useIdentityTransform,
bool allowSecureLayers) {
if (auto display = displayWeak.promote()) {
// Using new to access a private constructor.
return std::unique_ptr<DisplayRenderArea>(
new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
- rotation, allowSecureLayers));
+ useIdentityTransform, allowSecureLayers));
}
return nullptr;
}
DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
ui::Size reqSize, ui::Dataspace reqDataSpace,
- RotationFlags rotation, bool allowSecureLayers)
- : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, display->getViewport(),
- applyDeviceOrientation(rotation, *display)),
+ bool useIdentityTransform, bool allowSecureLayers)
+ : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, display->getLayerStackSpaceRect(),
+ allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)),
mDisplay(std::move(display)),
- mSourceCrop(sourceCrop),
- mAllowSecureLayers(allowSecureLayers) {}
+ mSourceCrop(sourceCrop) {}
const ui::Transform& DisplayRenderArea::getTransform() const {
return mTransform;
@@ -129,23 +93,16 @@
Rect DisplayRenderArea::getSourceCrop() const {
// use the projected display viewport by default.
if (mSourceCrop.isEmpty()) {
- return mDisplay->getViewport();
+ return mDisplay->getLayerStackSpaceRect();
}
- // If there is a source crop provided then it is assumed that the device
- // was in portrait orientation. This may not logically be true, so
- // correct for the orientation error by undoing the rotation
-
- ui::Rotation logicalOrientation = mDisplay->getOrientation();
- if (logicalOrientation == ui::Rotation::Rotation90) {
- logicalOrientation = ui::Rotation::Rotation270;
- } else if (logicalOrientation == ui::Rotation::Rotation270) {
- logicalOrientation = ui::Rotation::Rotation90;
- }
-
- const auto flags = ui::Transform::toRotationFlags(logicalOrientation);
- int width = mDisplay->getViewport().getWidth();
- int height = mDisplay->getViewport().getHeight();
+ // Correct for the orientation when the screen capture request contained
+ // useIdentityTransform. This will cause the rotation flag to be non 0 since
+ // it needs to rotate based on the screen orientation to allow the screenshot
+ // to be taken in the ROT_0 orientation
+ const auto flags = getRotationFlags();
+ int width = mDisplay->getLayerStackSpaceRect().getWidth();
+ int height = mDisplay->getLayerStackSpaceRect().getHeight();
ui::Transform rotation;
rotation.set(flags, width, height);
return rotation.transform(mSourceCrop);
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
index 340efb5..3478fc1 100644
--- a/services/surfaceflinger/DisplayRenderArea.h
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -29,7 +29,7 @@
public:
static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
ui::Size reqSize, ui::Dataspace,
- RotationFlags rotation,
+ bool useIdentityTransform,
bool allowSecureLayers = true);
const ui::Transform& getTransform() const override;
@@ -43,11 +43,10 @@
private:
DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
- ui::Dataspace, RotationFlags rotation, bool allowSecureLayers = true);
+ ui::Dataspace, bool useIdentityTransform, bool allowSecureLayers = true);
const sp<const DisplayDevice> mDisplay;
const Rect mSourceCrop;
- const bool mAllowSecureLayers;
const ui::Transform mTransform;
};
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 89c95d2..138d08c 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -39,6 +39,7 @@
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <math.h>
+#include <private/android_filesystem_config.h>
#include <renderengine/RenderEngine.h>
#include <stdint.h>
#include <stdlib.h>
@@ -139,6 +140,14 @@
mCallingPid = args.callingPid;
mCallingUid = args.callingUid;
+
+ if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) {
+ // If the system didn't send an ownerUid, use the callingUid for the ownerUid.
+ mOwnerUid = args.metadata.getInt32(METADATA_OWNER_UID, mCallingUid);
+ } else {
+ // A create layer request from a non system request cannot specify the owner uid
+ mOwnerUid = mCallingUid;
+ }
}
void Layer::onFirstRef() {
@@ -611,7 +620,7 @@
// ---------------------------------------------------------------------------
std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+ compositionengine::LayerFE::ClientCompositionTargetSettings& /* targetSettings */) {
if (!getCompositionState()) {
return {};
}
@@ -621,11 +630,7 @@
compositionengine::LayerFE::LayerSettings layerSettings;
layerSettings.geometry.boundaries = bounds;
- if (targetSettings.useIdentityTransform) {
- layerSettings.geometry.positionTransform = mat4();
- } else {
- layerSettings.geometry.positionTransform = getTransform().asMatrix4();
- }
+ layerSettings.geometry.positionTransform = getTransform().asMatrix4();
if (hasColorTransform()) {
layerSettings.colorTransform = getColorTransform();
@@ -642,9 +647,9 @@
}
std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareShadowClientComposition(
- const LayerFE::LayerSettings& casterLayerSettings, const Rect& displayViewport,
+ const LayerFE::LayerSettings& casterLayerSettings, const Rect& layerStackRect,
ui::Dataspace outputDataspace) {
- renderengine::ShadowSettings shadow = getShadowSettings(displayViewport);
+ renderengine::ShadowSettings shadow = getShadowSettings(layerStackRect);
if (shadow.length <= 0.f) {
return {};
}
@@ -1669,8 +1674,8 @@
}
void Layer::dumpCallingUidPid(std::string& result) const {
- StringAppendF(&result, "Layer %s (%s) pid:%d uid:%d\n", getName().c_str(), getType(),
- mCallingPid, mCallingUid);
+ StringAppendF(&result, "Layer %s (%s) callingPid:%d callingUid:%d ownerUid:%d\n",
+ getName().c_str(), getType(), mCallingPid, mCallingUid, mOwnerUid);
}
void Layer::onDisconnect() {
@@ -2153,12 +2158,12 @@
: RoundedCornerState();
}
-renderengine::ShadowSettings Layer::getShadowSettings(const Rect& viewport) const {
+renderengine::ShadowSettings Layer::getShadowSettings(const Rect& layerStackRect) const {
renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
// Shift the spot light x-position to the middle of the display and then
// offset it by casting layer's screen pos.
- state.lightPos.x = (viewport.width() / 2.f) - mScreenBounds.left;
+ state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left;
state.lightPos.y -= mScreenBounds.top;
state.length = mEffectiveShadowRadius;
@@ -2343,6 +2348,8 @@
}
layerInfo->set_is_relative_of(state.isRelativeOf);
+
+ layerInfo->set_owner_uid(mOwnerUid);
}
if (traceFlags & SurfaceTracing::TRACE_INPUT) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 99b1bb1..521659d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -366,7 +366,7 @@
virtual bool setMetadata(const LayerMetadata& data);
bool reparentChildren(const sp<IBinder>& newParentHandle);
void reparentChildren(const sp<Layer>& newParent);
- virtual void setChildrenDrawingParent(const sp<Layer>& layer);
+ virtual void setChildrenDrawingParent(const sp<Layer>&);
virtual bool reparent(const sp<IBinder>& newParentHandle);
virtual bool detachChildren();
bool attachChildren();
@@ -436,9 +436,7 @@
// Deprecated, please use compositionengine::Output::belongsInOutput()
// instead.
// TODO(lpique): Move the remaining callers (screencap) to the new function.
- bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
- return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
- }
+ bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; }
FloatRect getBounds(const Region& activeTransparentRegion) const;
FloatRect getBounds() const;
@@ -537,7 +535,7 @@
// Write drawing or current state. If writing current state, the caller should hold the
// external mStateLock. If writing drawing state, this function should be called on the
// main or tracing thread.
- void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet,
+ void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet,
uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
@@ -587,16 +585,16 @@
void updateClonedChildren(const sp<Layer>& mirrorRoot,
std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void addChildToDrawing(const sp<Layer>& layer);
+ void addChildToDrawing(const sp<Layer>&);
void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&);
virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition(
- const LayerFE::LayerSettings& layerSettings, const Rect& displayViewport,
+ const LayerFE::LayerSettings&, const Rect& layerStackRect,
ui::Dataspace outputDataspace);
// Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
// the settings clears the content with a solid black fill.
- void prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, bool blackout) const;
+ void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
public:
/*
@@ -758,7 +756,7 @@
// ignored.
virtual RoundedCornerState getRoundedCornerState() const;
- renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+ renderengine::ShadowSettings getShadowSettings(const Rect& layerStackRect) const;
/**
* Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
@@ -768,17 +766,15 @@
* the scene state, but it's also more efficient than traverseInZOrder and so useful for
* book-keeping.
*/
- void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
- void traverseInReverseZOrder(LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor);
- void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
+ void traverse(LayerVector::StateSet, const LayerVector::Visitor&);
+ void traverseInReverseZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
+ void traverseInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
/**
* Traverse only children in z order, ignoring relative layers that are not children of the
* parent.
*/
- void traverseChildrenInZOrder(LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor);
+ void traverseChildrenInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
size_t getChildrenCount() const;
@@ -790,7 +786,7 @@
// the current state, but should not be called anywhere else!
LayerVector& getCurrentChildren() { return mCurrentChildren; }
- void addChild(const sp<Layer>& layer);
+ void addChild(const sp<Layer>&);
// Returns index if removed, or negative value otherwise
// for symmetry with Vector::remove
ssize_t removeChild(const sp<Layer>& layer);
@@ -804,7 +800,7 @@
// Copy the current list of children to the drawing state. Called by
// SurfaceFlinger to complete a transaction.
void commitChildList();
- int32_t getZ(LayerVector::StateSet stateSet) const;
+ int32_t getZ(LayerVector::StateSet) const;
virtual void pushPendingState();
/**
@@ -830,7 +826,7 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- bool setFrameRate(FrameRate frameRate);
+ bool setFrameRate(FrameRate);
virtual FrameRate getFrameRateForLayerTree() const;
static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
@@ -872,8 +868,8 @@
* crop coordinates, transforming them into layer space.
*/
void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
- void setParent(const sp<Layer>& layer);
- LayerVector makeTraversalList(LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers);
+ void setParent(const sp<Layer>&);
+ LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers);
void addZOrderRelative(const wp<Layer>& relative);
void removeZOrderRelative(const wp<Layer>& relative);
@@ -967,10 +963,12 @@
*/
virtual bool needsInputInfo() const { return hasInputInfo(); }
+ uid_t getOwnerUid() { return mOwnerUid; }
+
protected:
compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
- bool usingRelativeZ(LayerVector::StateSet stateSet) const;
+ bool usingRelativeZ(LayerVector::StateSet) const;
bool mPremultipliedAlpha{true};
const std::string mName;
@@ -1050,15 +1048,14 @@
* Returns an unsorted vector of all layers that are part of this tree.
* That includes the current layer and all its descendants.
*/
- std::vector<Layer*> getLayersInTree(LayerVector::StateSet stateSet);
+ std::vector<Layer*> getLayersInTree(LayerVector::StateSet);
/**
* Traverses layers that are part of this tree in the correct z order.
* layersInTree must be sorted before calling this method.
*/
void traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree,
- LayerVector::StateSet stateSet,
- const LayerVector::Visitor& visitor);
- LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
+ LayerVector::StateSet, const LayerVector::Visitor&);
+ LayerVector makeChildrenTraversalList(LayerVector::StateSet,
const std::vector<Layer*>& layersInTree);
void updateTreeHasFrameRateVote();
@@ -1089,6 +1086,10 @@
pid_t mCallingPid;
uid_t mCallingUid;
+ // The owner of the layer. If created from a non system process, it will be the calling uid.
+ // If created from a system process, the value can be passed in.
+ uid_t mOwnerUid;
+
// The current layer is a clone of mClonedFrom. This means that this layer will update it's
// properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
// this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index c4f8666..e84508f 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -44,8 +44,8 @@
LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop,
ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly,
- const Rect& displayViewport)
- : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, displayViewport),
+ const Rect& layerStackRect, bool allowSecureLayers)
+ : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, layerStackRect, allowSecureLayers),
mLayer(std::move(layer)),
mCrop(crop),
mFlinger(flinger),
@@ -68,7 +68,7 @@
}
bool LayerRenderArea::isSecure() const {
- return false;
+ return mAllowSecureLayers;
}
bool LayerRenderArea::needsFiltering() const {
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
index 81690b9..6a90694 100644
--- a/services/surfaceflinger/LayerRenderArea.h
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -33,7 +33,8 @@
class LayerRenderArea : public RenderArea {
public:
LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
- ui::Dataspace reqDataSpace, bool childrenOnly, const Rect& displayViewport);
+ ui::Dataspace reqDataSpace, bool childrenOnly, const Rect& layerStackRect,
+ bool allowSecureLayers);
const ui::Transform& getTransform() const override;
Rect getBounds() const override;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 17daadb..0157a7f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -429,7 +429,7 @@
bounds.top, bounds.right, bounds.bottom);
visitor(layer);
};
- mFlinger.traverseLayersInLayerStack(layerStack, filterVisitor);
+ mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
};
sp<GraphicBuffer> buffer = nullptr;
@@ -444,8 +444,7 @@
ScreenCaptureResults captureResults;
mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
- false /* identityTransform */, true /* regionSampling */,
- captureResults);
+ true /* regionSampling */, captureResults);
std::vector<Descriptor> activeDescriptors;
for (const auto& descriptor : descriptors) {
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index a6246d9..c9f7f46 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -24,12 +24,14 @@
static float getCaptureFillValue(CaptureFill captureFill);
RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
- const Rect& displayViewport, RotationFlags rotation = ui::Transform::ROT_0)
- : mReqSize(reqSize),
+ const Rect& layerStackRect, bool allowSecureLayers = false,
+ RotationFlags rotation = ui::Transform::ROT_0)
+ : mAllowSecureLayers(allowSecureLayers),
+ mReqSize(reqSize),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
mRotationFlags(rotation),
- mDisplayViewport(displayViewport) {}
+ mLayerStackSpaceRect(layerStackRect) {}
virtual ~RenderArea() = default;
@@ -81,14 +83,17 @@
virtual sp<const DisplayDevice> getDisplayDevice() const = 0;
// Returns the source display viewport.
- const Rect& getDisplayViewport() const { return mDisplayViewport; }
+ const Rect& getLayerStackSpaceRect() const { return mLayerStackSpaceRect; }
+
+protected:
+ const bool mAllowSecureLayers;
private:
const ui::Size mReqSize;
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const RotationFlags mRotationFlags;
- const Rect mDisplayViewport;
+ const Rect mLayerStackSpaceRect;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 2020e0c..846190c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -137,6 +137,7 @@
status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
outChannel->setReceiveFd(mChannel.moveReceiveFd());
+ outChannel->setSendFd(base::unique_fd(dup(mChannel.getSendFd())));
return NO_ERROR;
}
@@ -150,11 +151,6 @@
mEventThread->requestNextVsync(this);
}
-void EventThreadConnection::requestLatestConfig() {
- ATRACE_NAME("requestLatestConfig");
- mEventThread->requestLatestConfig(this);
-}
-
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
return size < 0 ? status_t(size) : status_t(NO_ERROR);
@@ -267,28 +263,6 @@
}
}
-void EventThread::requestLatestConfig(const sp<EventThreadConnection>& connection) {
- std::lock_guard<std::mutex> lock(mMutex);
- if (connection->mForcedConfigChangeDispatch) {
- return;
- }
- connection->mForcedConfigChangeDispatch = true;
- auto pendingConfigChange =
- std::find_if(std::begin(mPendingEvents), std::end(mPendingEvents),
- [&](const DisplayEventReceiver::Event& event) {
- return event.header.type ==
- DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED;
- });
-
- // If we didn't find a pending config change event, then push out the
- // latest one we've ever seen.
- if (pendingConfigChange == std::end(mPendingEvents)) {
- mPendingEvents.push_back(mLastConfigChangeEvent);
- }
-
- mCondition.notify_all();
-}
-
void EventThread::onScreenReleased() {
std::lock_guard<std::mutex> lock(mMutex);
if (!mVSyncState || mVSyncState->synthetic) {
@@ -364,9 +338,6 @@
mInterceptVSyncsCallback(event->header.timestamp);
}
break;
- case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
- mLastConfigChangeEvent = *event;
- break;
}
}
@@ -379,10 +350,6 @@
vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
if (event && shouldConsumeEvent(*event, connection)) {
- if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
- connection->mForcedConfigChangeDispatch) {
- connection->mForcedConfigChangeDispatch = false;
- }
consumers.push_back(connection);
}
@@ -458,9 +425,7 @@
return true;
case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: {
- const bool oneTimeDispatch = connection->mForcedConfigChangeDispatch;
- return oneTimeDispatch ||
- connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
+ return connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
}
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 64acbd7..49f624c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -81,19 +81,13 @@
status_t stealReceiveChannel(gui::BitTube* outChannel) override;
status_t setVsyncRate(uint32_t rate) override;
void requestNextVsync() override; // asynchronous
- void requestLatestConfig() override; // asynchronous
// Called in response to requestNextVsync.
const ResyncCallback resyncCallback;
VSyncRequest vsyncRequest = VSyncRequest::None;
- ISurfaceComposer::ConfigChanged mConfigChanged =
+ const ISurfaceComposer::ConfigChanged mConfigChanged =
ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
- // Store whether we need to force dispatching a config change separately -
- // if mConfigChanged ever changes before the config change is dispatched
- // then we still need to propagate an initial config to the app if we
- // haven't already.
- bool mForcedConfigChangeDispatch = false;
private:
virtual void onFirstRef();
@@ -129,10 +123,6 @@
virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
// Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0;
- // Dispatches the most recent configuration
- // Usage of this method assumes that only the primary internal display
- // supports multiple display configurations.
- virtual void requestLatestConfig(const sp<EventThreadConnection>& connection) = 0;
// Retrieves the number of event connections tracked by this EventThread.
virtual size_t getEventThreadConnectionCount() = 0;
@@ -153,7 +143,6 @@
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
void requestNextVsync(const sp<EventThreadConnection>& connection) override;
- void requestLatestConfig(const sp<EventThreadConnection>& connection) override;
// called before the screen is turned off from main thread
void onScreenReleased() override;
@@ -201,7 +190,6 @@
std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex);
std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
- DisplayEventReceiver::Event mLastConfigChangeEvent GUARDED_BY(mMutex);
// VSYNC state of connected display.
struct VSyncState {
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
deleted file mode 100644
index fe2e406..0000000
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "PhaseOffsets.h"
-
-#include <cutils/properties.h>
-
-#include <optional>
-
-#include "SurfaceFlingerProperties.h"
-
-namespace {
-
-std::optional<nsecs_t> getProperty(const char* name) {
- char value[PROPERTY_VALUE_MAX];
- property_get(name, value, "-1");
- if (const int i = atoi(value); i != -1) return i;
- return std::nullopt;
-}
-
-bool fpsEqualsWithMargin(float fpsA, float fpsB) {
- static constexpr float MARGIN = 0.01f;
- return std::abs(fpsA - fpsB) <= MARGIN;
-}
-
-std::vector<float> getRefreshRatesFromConfigs(
- const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
- const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
- std::vector<float> refreshRates;
- refreshRates.reserve(allRefreshRates.size());
-
- for (const auto& [ignored, refreshRate] : allRefreshRates) {
- refreshRates.emplace_back(refreshRate->getFps());
- }
-
- return refreshRates;
-}
-
-} // namespace
-
-namespace android::scheduler {
-
-PhaseConfiguration::~PhaseConfiguration() = default;
-
-namespace impl {
-
-PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().getFps(),
- sysprop::vsync_event_phase_offset_ns(1000000),
- sysprop::vsync_sf_event_phase_offset_ns(1000000),
- getProperty("debug.sf.early_phase_offset_ns"),
- getProperty("debug.sf.early_gl_phase_offset_ns"),
- getProperty("debug.sf.early_app_phase_offset_ns"),
- getProperty("debug.sf.early_gl_app_phase_offset_ns"),
- // Below defines the threshold when an offset is considered to be negative,
- // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
- // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
- // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
- // vsync.
- getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
- .value_or(std::numeric_limits<nsecs_t>::max())) {}
-
-PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
- std::optional<nsecs_t> earlySfOffsetNs,
- std::optional<nsecs_t> earlyGlSfOffsetNs,
- std::optional<nsecs_t> earlyAppOffsetNs,
- std::optional<nsecs_t> earlyGlAppOffsetNs, nsecs_t thresholdForNextVsync)
- : mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
- mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
- mEarlySfOffsetNs(earlySfOffsetNs),
- mEarlyGlSfOffsetNs(earlyGlSfOffsetNs),
- mEarlyAppOffsetNs(earlyAppOffsetNs),
- mEarlyGlAppOffsetNs(earlyGlAppOffsetNs),
- mThresholdForNextVsync(thresholdForNextVsync),
- mOffsets(initializeOffsets(refreshRates)),
- mRefreshRateFps(currentFps) {}
-
-void PhaseOffsets::dump(std::string& result) const {
- const auto [early, earlyGl, late] = getCurrentOffsets();
- using base::StringAppendF;
- StringAppendF(&result,
- " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n"
- " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n"
- " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
- "next VSYNC threshold: %9" PRId64 " ns\n",
- late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
- mThresholdForNextVsync);
-}
-
-std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
- const std::vector<float>& refreshRates) const {
- std::unordered_map<float, Offsets> offsets;
-
- for (const auto& refreshRate : refreshRates) {
- offsets.emplace(refreshRate,
- getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
- }
- return offsets;
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
- if (fps > 65.0f) {
- return getHighFpsOffsets(vsyncPeriod);
- } else {
- return getDefaultOffsets(vsyncPeriod);
- }
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
- return {
- {
- mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
- ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
- : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
-
- mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
- },
- {
- mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
- ? mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
- : mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
-
- mEarlyGlAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
- },
- {
- mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
- ? mSfVSyncPhaseOffsetNs
- : mSfVSyncPhaseOffsetNs - vsyncDuration,
-
- mVSyncPhaseOffsetNs,
- },
- };
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
- const auto highFpsLateAppOffsetNs =
- getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
- const auto highFpsLateSfOffsetNs =
- getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
-
- const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
- const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
- const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns");
- const auto highFpsEarlyGlAppOffsetNs =
- getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
-
- return {
- {
- highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
- ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
- : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
- vsyncDuration,
-
- highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
- },
- {
- highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
- mThresholdForNextVsync
- ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
- : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
- vsyncDuration,
-
- highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
- },
- {
- highFpsLateSfOffsetNs < mThresholdForNextVsync
- ? highFpsLateSfOffsetNs
- : highFpsLateSfOffsetNs - vsyncDuration,
-
- highFpsLateAppOffsetNs,
- },
- };
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
- const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
- [&fps](const std::pair<float, Offsets>& candidateFps) {
- return fpsEqualsWithMargin(fps, candidateFps.first);
- });
-
- if (iter != mOffsets.end()) {
- return iter->second;
- }
-
- // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
- // In this case just construct the offset.
- ALOGW("Can't find offset for %.2f fps", fps);
- return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
-}
-
-static void validateSysprops() {
- const auto validatePropertyBool = [](const char* prop) {
- LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
- };
-
- validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
-
- LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
- "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
- "duration");
-
- LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
- "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
- "duration");
-
- const auto validateProperty = [](const char* prop) {
- LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
- "%s is set to %" PRId64 " but expecting duration", prop,
- getProperty(prop).value_or(-1));
- };
-
- validateProperty("debug.sf.early_phase_offset_ns");
- validateProperty("debug.sf.early_gl_phase_offset_ns");
- validateProperty("debug.sf.early_app_phase_offset_ns");
- validateProperty("debug.sf.early_gl_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
- validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
-}
-
-static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) {
- return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration;
-}
-
-static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) {
- return sfDuration == -1 ? 1'000'000
- : vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
-}
-
-PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const {
- return Offsets{
- {
- mSfEarlyDuration < vsyncDuration
- ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration)
- : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration),
- },
- {
- mSfEarlyGlDuration < vsyncDuration
- ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration)
- : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration),
- },
- {
- mSfDuration < vsyncDuration
- ? sfDurationToOffset(mSfDuration, vsyncDuration)
- : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration,
-
- appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration),
- },
- };
-}
-
-std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
- const std::vector<float>& refreshRates) const {
- std::unordered_map<float, Offsets> offsets;
-
- for (const auto fps : refreshRates) {
- offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
- }
- return offsets;
-}
-
-PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().getFps(),
- getProperty("debug.sf.late.sf.duration").value_or(-1),
- getProperty("debug.sf.late.app.duration").value_or(-1),
- getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
- getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
- getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
- getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
- validateSysprops();
-}
-
-PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
- nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration,
- nsecs_t appEarlyGlDuration)
- : mSfDuration(sfDuration),
- mAppDuration(appDuration),
- mSfEarlyDuration(sfEarlyDuration),
- mAppEarlyDuration(appEarlyDuration),
- mSfEarlyGlDuration(sfEarlyGlDuration),
- mAppEarlyGlDuration(appEarlyGlDuration),
- mOffsets(initializeOffsets(refreshRates)),
- mRefreshRateFps(currentFps) {}
-
-PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
- const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
- return fpsEqualsWithMargin(fps, candidateFps.first);
- });
-
- if (iter != mOffsets.end()) {
- return iter->second;
- }
-
- // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
- // In this case just construct the offset.
- ALOGW("Can't find offset for %.2f fps", fps);
- return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
-}
-
-void PhaseDurations::dump(std::string& result) const {
- const auto [early, earlyGl, late] = getCurrentOffsets();
- using base::StringAppendF;
- StringAppendF(&result,
- " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
- " ns\n"
- " app duration: %9" PRId64 " ns\t SF duration: %9" PRId64
- " ns\n"
- " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
- " ns\n"
- " early app duration: %9" PRId64 " ns\t early SF duration: %9" PRId64
- " ns\n"
- " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
- " ns\n"
- " GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64
- " ns\n",
- late.app,
-
- late.sf,
-
- mAppDuration, mSfDuration,
-
- early.app, early.sf,
-
- mAppEarlyDuration, mSfEarlyDuration,
-
- earlyGl.app,
-
- earlyGl.sf,
-
- mAppEarlyGlDuration, mSfEarlyGlDuration);
-}
-
-} // namespace impl
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
deleted file mode 100644
index 9ec6d56..0000000
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <unordered_map>
-
-#include "RefreshRateConfigs.h"
-#include "VSyncModulator.h"
-
-namespace android::scheduler {
-
-/*
- * This class encapsulates offsets for different refresh rates. Depending
- * on what refresh rate we are using, and wheter we are composing in GL,
- * different offsets will help us with latency. This class keeps track of
- * which mode the device is on, and returns approprate offsets when needed.
- */
-class PhaseConfiguration {
-public:
- using Offsets = VSyncModulator::OffsetsConfig;
-
- virtual ~PhaseConfiguration();
-
- virtual Offsets getCurrentOffsets() const = 0;
- virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
-
- virtual void setRefreshRateFps(float fps) = 0;
-
- virtual void dump(std::string& result) const = 0;
-};
-
-namespace impl {
-
-/*
- * This is the old implementation of phase offsets and considered as deprecated.
- * PhaseDurations is the new implementation.
- */
-class PhaseOffsets : public scheduler::PhaseConfiguration {
-public:
- PhaseOffsets(const scheduler::RefreshRateConfigs&);
-
- // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(float fps) const override;
-
- // Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
-
- // This function should be called when the device is switching between different
- // refresh rates, to properly update the offsets.
- void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
-
- // Returns current offsets in human friendly format.
- void dump(std::string& result) const override;
-
-protected:
- // Used for unit tests
- PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
- nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
- std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGlSfOffsetNs,
- std::optional<nsecs_t> earlyAppOffsetNs, std::optional<nsecs_t> earlyGlAppOffsetNs,
- nsecs_t thresholdForNextVsync);
- std::unordered_map<float, Offsets> initializeOffsets(
- const std::vector<float>& refreshRates) const;
- Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
- Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
- Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
-
- const nsecs_t mVSyncPhaseOffsetNs;
- const nsecs_t mSfVSyncPhaseOffsetNs;
- const std::optional<nsecs_t> mEarlySfOffsetNs;
- const std::optional<nsecs_t> mEarlyGlSfOffsetNs;
- const std::optional<nsecs_t> mEarlyAppOffsetNs;
- const std::optional<nsecs_t> mEarlyGlAppOffsetNs;
- const nsecs_t mThresholdForNextVsync;
- const std::unordered_map<float, Offsets> mOffsets;
-
- std::atomic<float> mRefreshRateFps;
-};
-
-/*
- * Class that encapsulates the phase offsets for SurfaceFlinger and App.
- * The offsets are calculated from durations for each one of the (late, early, earlyGL)
- * offset types.
- */
-class PhaseDurations : public scheduler::PhaseConfiguration {
-public:
- PhaseDurations(const scheduler::RefreshRateConfigs&);
-
- // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(float fps) const override;
-
- // Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
-
- // This function should be called when the device is switching between different
- // refresh rates, to properly update the offsets.
- void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
-
- // Returns current offsets in human friendly format.
- void dump(std::string& result) const override;
-
-protected:
- // Used for unit tests
- PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
- nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
- nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration);
-
-private:
- std::unordered_map<float, Offsets> initializeOffsets(const std::vector<float>&) const;
- PhaseDurations::Offsets constructOffsets(nsecs_t vsyncDuration) const;
-
- const nsecs_t mSfDuration;
- const nsecs_t mAppDuration;
-
- const nsecs_t mSfEarlyDuration;
- const nsecs_t mAppEarlyDuration;
-
- const nsecs_t mSfEarlyGlDuration;
- const nsecs_t mAppEarlyGlDuration;
-
- const std::unordered_map<float, Offsets> mOffsets;
-
- std::atomic<float> mRefreshRateFps;
-};
-
-} // namespace impl
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 6a5082a..4e7a9a1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -60,28 +60,13 @@
~ISchedulerCallback() = default;
};
-struct IPhaseOffsetControl {
- virtual void setPhaseOffset(scheduler::ConnectionHandle, nsecs_t phaseOffset) = 0;
-
-protected:
- ~IPhaseOffsetControl() = default;
-};
-
-class Scheduler : public IPhaseOffsetControl {
+class Scheduler {
public:
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
- // Indicates whether to start the transaction early, or at vsync time.
- enum class TransactionStart {
- Early, // DEPRECATED. Start the transaction early. Times out on its own
- EarlyStart, // Start the transaction early and keep this config until EarlyEnd
- EarlyEnd, // End the early config started at EarlyStart
- Normal // Start the transaction at the normal time
- };
-
Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&);
- virtual ~Scheduler();
+ ~Scheduler();
DispSync& getPrimaryDispSync();
@@ -104,7 +89,7 @@
void onScreenReleased(ConnectionHandle);
// Modifies phase offset in the event thread.
- void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset) override;
+ void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset);
void getDisplayStatInfo(DisplayStatInfo* stats);
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
deleted file mode 100644
index 2567c04..0000000
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "VSyncModulator.h"
-
-#include <cutils/properties.h>
-#include <utils/Trace.h>
-
-#include <chrono>
-#include <cinttypes>
-#include <mutex>
-
-namespace android::scheduler {
-
-VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl,
- Scheduler::ConnectionHandle appConnectionHandle,
- Scheduler::ConnectionHandle sfConnectionHandle,
- const OffsetsConfig& config)
- : mPhaseOffsetControl(phaseOffsetControl),
- mAppConnectionHandle(appConnectionHandle),
- mSfConnectionHandle(sfConnectionHandle),
- mOffsetsConfig(config) {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.vsync_trace_detailed_info", value, "0");
- mTraceDetailedInfo = atoi(value);
-}
-
-void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
- std::lock_guard<std::mutex> lock(mMutex);
- mOffsetsConfig = config;
- updateOffsetsLocked();
-}
-
-void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
- switch (transactionStart) {
- case Scheduler::TransactionStart::EarlyStart:
- ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart");
- mExplicitEarlyWakeup = true;
- break;
- case Scheduler::TransactionStart::EarlyEnd:
- ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart");
- mExplicitEarlyWakeup = false;
- break;
- case Scheduler::TransactionStart::Normal:
- case Scheduler::TransactionStart::Early:
- // Non explicit don't change the explicit early wakeup state
- break;
- }
-
- if (mTraceDetailedInfo) {
- ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
- }
-
- if (!mExplicitEarlyWakeup &&
- (transactionStart == Scheduler::TransactionStart::Early ||
- transactionStart == Scheduler::TransactionStart::EarlyEnd)) {
- mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
- mEarlyTxnStartTime = std::chrono::steady_clock::now();
- }
-
- // An early transaction stays an early transaction.
- if (transactionStart == mTransactionStart ||
- mTransactionStart == Scheduler::TransactionStart::EarlyEnd) {
- return;
- }
- mTransactionStart = transactionStart;
- updateOffsets();
-}
-
-void VSyncModulator::onTransactionHandled() {
- mTxnAppliedTime = std::chrono::steady_clock::now();
- if (mTransactionStart == Scheduler::TransactionStart::Normal) return;
- mTransactionStart = Scheduler::TransactionStart::Normal;
- updateOffsets();
-}
-
-void VSyncModulator::onRefreshRateChangeInitiated() {
- if (mRefreshRateChangePending) {
- return;
- }
- mRefreshRateChangePending = true;
- updateOffsets();
-}
-
-void VSyncModulator::onRefreshRateChangeCompleted() {
- if (!mRefreshRateChangePending) {
- return;
- }
- mRefreshRateChangePending = false;
- updateOffsets();
-}
-
-void VSyncModulator::onRefreshed(bool usedRenderEngine) {
- bool updateOffsetsNeeded = false;
-
- // Apply a margin to account for potential data races
- // This might make us stay in early offsets for one
- // additional frame but it's better to be conservative here.
- if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) {
- if (mRemainingEarlyFrameCount > 0) {
- mRemainingEarlyFrameCount--;
- updateOffsetsNeeded = true;
- }
- }
- if (usedRenderEngine) {
- mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION;
- updateOffsetsNeeded = true;
- } else if (mRemainingRenderEngineUsageCount > 0) {
- mRemainingRenderEngineUsageCount--;
- updateOffsetsNeeded = true;
- }
- if (updateOffsetsNeeded) {
- updateOffsets();
- }
-}
-
-VSyncModulator::Offsets VSyncModulator::getOffsets() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mOffsets;
-}
-
-const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
- // Early offsets are used if we're in the middle of a refresh rate
- // change, or if we recently begin a transaction.
- if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd ||
- mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
- return mOffsetsConfig.early;
- } else if (mRemainingRenderEngineUsageCount > 0) {
- return mOffsetsConfig.earlyGl;
- } else {
- return mOffsetsConfig.late;
- }
-}
-
-void VSyncModulator::updateOffsets() {
- std::lock_guard<std::mutex> lock(mMutex);
- updateOffsetsLocked();
-}
-
-void VSyncModulator::updateOffsetsLocked() {
- const Offsets& offsets = getNextOffsets();
-
- mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf);
- mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app);
-
- mOffsets = offsets;
-
- if (!mTraceDetailedInfo) {
- return;
- }
-
- const bool isEarly = &offsets == &mOffsetsConfig.early;
- const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
- const bool isLate = &offsets == &mOffsetsConfig.late;
-
- ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
- ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl);
- ATRACE_INT("Vsync-LateOffsetsOn", isLate);
-}
-
-} // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
deleted file mode 100644
index ab678c9..0000000
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <chrono>
-#include <mutex>
-
-#include "Scheduler.h"
-
-namespace android::scheduler {
-
-/*
- * Modulates the vsync-offsets depending on current SurfaceFlinger state.
- */
-class VSyncModulator {
-private:
- // Number of frames we'll keep the early phase offsets once they are activated for a
- // transaction. This acts as a low-pass filter in case the client isn't quick enough in
- // sending new transactions.
- static constexpr int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
-
- // Number of frames we'll keep the early gl phase offsets once they are activated.
- // This acts as a low-pass filter to avoid scenarios where we rapidly
- // switch in and out of gl composition.
- static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
-
- // Margin used to account for potential data races
- static const constexpr std::chrono::nanoseconds MARGIN_FOR_TX_APPLY = 1ms;
-
-public:
- // Wrapper for a collection of surfaceflinger/app offsets for a particular
- // configuration.
- struct Offsets {
- nsecs_t sf;
- nsecs_t app;
-
- bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
-
- bool operator!=(const Offsets& other) const { return !(*this == other); }
- };
-
- struct OffsetsConfig {
- Offsets early; // For transactions with the eEarlyWakeup flag.
- Offsets earlyGl; // As above but while compositing with GL.
- Offsets late; // Default.
-
- bool operator==(const OffsetsConfig& other) const {
- return early == other.early && earlyGl == other.earlyGl && late == other.late;
- }
-
- bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
- };
-
- VSyncModulator(IPhaseOffsetControl&, ConnectionHandle appConnectionHandle,
- ConnectionHandle sfConnectionHandle, const OffsetsConfig&);
-
- void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
-
- // Signals that a transaction has started, and changes offsets accordingly.
- void setTransactionStart(Scheduler::TransactionStart transactionStart);
-
- // Signals that a transaction has been completed, so that we can finish
- // special handling for a transaction.
- void onTransactionHandled();
-
- // Called when we send a refresh rate change to hardware composer, so that
- // we can move into early offsets.
- void onRefreshRateChangeInitiated();
-
- // Called when we detect from vsync signals that the refresh rate changed.
- // This way we can move out of early offsets if no longer necessary.
- void onRefreshRateChangeCompleted();
-
- // Called when the display is presenting a new frame. usedRenderEngine
- // should be set to true if RenderEngine was involved with composing the new
- // frame.
- void onRefreshed(bool usedRenderEngine);
-
- // Returns the offsets that we are currently using
- Offsets getOffsets() const EXCLUDES(mMutex);
-
-private:
- friend class VSyncModulatorTest;
- // Returns the next offsets that we should be using
- const Offsets& getNextOffsets() const REQUIRES(mMutex);
- // Updates offsets and persists them into the scheduler framework.
- void updateOffsets() EXCLUDES(mMutex);
- void updateOffsetsLocked() REQUIRES(mMutex);
-
- IPhaseOffsetControl& mPhaseOffsetControl;
- const ConnectionHandle mAppConnectionHandle;
- const ConnectionHandle mSfConnectionHandle;
-
- mutable std::mutex mMutex;
- OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex);
-
- Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
-
- std::atomic<Scheduler::TransactionStart> mTransactionStart =
- Scheduler::TransactionStart::Normal;
- std::atomic<bool> mRefreshRateChangePending = false;
- std::atomic<bool> mExplicitEarlyWakeup = false;
- std::atomic<int> mRemainingEarlyFrameCount = 0;
- std::atomic<int> mRemainingRenderEngineUsageCount = 0;
- std::atomic<std::chrono::steady_clock::time_point> mEarlyTxnStartTime = {};
- std::atomic<std::chrono::steady_clock::time_point> mTxnAppliedTime = {};
-
- bool mTraceDetailedInfo = false;
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
new file mode 100644
index 0000000..aac2569
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VsyncConfiguration.h"
+
+#include <cutils/properties.h>
+
+#include <optional>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace {
+
+std::optional<nsecs_t> getProperty(const char* name) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get(name, value, "-1");
+ if (const int i = atoi(value); i != -1) return i;
+ return std::nullopt;
+}
+
+bool fpsEqualsWithMargin(float fpsA, float fpsB) {
+ static constexpr float MARGIN = 0.01f;
+ return std::abs(fpsA - fpsB) <= MARGIN;
+}
+
+std::vector<float> getRefreshRatesFromConfigs(
+ const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+ std::vector<float> refreshRates;
+ refreshRates.reserve(allRefreshRates.size());
+
+ for (const auto& [ignored, refreshRate] : allRefreshRates) {
+ refreshRates.emplace_back(refreshRate->getFps());
+ }
+
+ return refreshRates;
+}
+
+} // namespace
+
+namespace android::scheduler::impl {
+
+VsyncConfiguration::VsyncConfiguration(float currentFps) : mRefreshRateFps(currentFps) {}
+
+PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(float fps) const {
+ const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
+ [&fps](const std::pair<float, VsyncConfigSet>& candidateFps) {
+ return fpsEqualsWithMargin(fps, candidateFps.first);
+ });
+
+ if (iter != mOffsets.end()) {
+ return iter->second;
+ }
+
+ // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+ // In this case just construct the offset.
+ ALOGW("Can't find offset for %.2f fps", fps);
+ return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
+}
+
+void VsyncConfiguration::initializeOffsets(const std::vector<float>& refreshRates) {
+ for (const auto fps : refreshRates) {
+ mOffsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
+ }
+}
+
+void VsyncConfiguration::dump(std::string& result) const {
+ const auto [early, earlyGpu, late] = getCurrentConfigs();
+ using base::StringAppendF;
+ StringAppendF(&result,
+ " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
+ " ns\n"
+ " app duration: %9lld ns\t SF duration: %9lld ns\n"
+ " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
+ " ns\n"
+ " early app duration: %9lld ns\t early SF duration: %9lld ns\n"
+ " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
+ " ns\n"
+ " GL early app duration: %9lld ns\tGL early SF duration: %9lld ns\n",
+ late.appOffset, late.sfOffset,
+
+ late.appWorkDuration.count(), late.sfWorkDuration.count(),
+
+ early.appOffset, early.sfOffset,
+
+ early.appWorkDuration.count(), early.sfWorkDuration.count(),
+
+ earlyGpu.appOffset, earlyGpu.sfOffset,
+
+ earlyGpu.appWorkDuration.count(), earlyGpu.sfWorkDuration.count());
+}
+
+PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().getFps(),
+ sysprop::vsync_event_phase_offset_ns(1000000),
+ sysprop::vsync_sf_event_phase_offset_ns(1000000),
+ getProperty("debug.sf.early_phase_offset_ns"),
+ getProperty("debug.sf.early_gl_phase_offset_ns"),
+ getProperty("debug.sf.early_app_phase_offset_ns"),
+ getProperty("debug.sf.early_gl_app_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000),
+ getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000),
+ getProperty("debug.sf.high_fps_early_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_gl_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_app_phase_offset_ns"),
+ getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"),
+ // Below defines the threshold when an offset is considered to be negative,
+ // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+ // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+ // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+ // vsync.
+ getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+ .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(
+ const std::vector<float>& refreshRates, float currentFps, nsecs_t vsyncPhaseOffsetNs,
+ nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs,
+ std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
+ nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync)
+ : VsyncConfiguration(currentFps),
+ mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
+ mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
+ mEarlySfOffsetNs(earlySfOffsetNs),
+ mEarlyGpuSfOffsetNs(earlyGpuSfOffsetNs),
+ mEarlyAppOffsetNs(earlyAppOffsetNs),
+ mEarlyGpuAppOffsetNs(earlyGpuAppOffsetNs),
+ mHighFpsVSyncPhaseOffsetNs(highFpsVsyncPhaseOffsetNs),
+ mHighFpsSfVSyncPhaseOffsetNs(highFpsSfVSyncPhaseOffsetNs),
+ mHighFpsEarlySfOffsetNs(highFpsEarlySfOffsetNs),
+ mHighFpsEarlyGpuSfOffsetNs(highFpsEarlyGpuSfOffsetNs),
+ mHighFpsEarlyAppOffsetNs(highFpsEarlyAppOffsetNs),
+ mHighFpsEarlyGpuAppOffsetNs(highFpsEarlyGpuAppOffsetNs),
+ mThresholdForNextVsync(thresholdForNextVsync) {
+ initializeOffsets(refreshRates);
+}
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
+ if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
+ return getHighFpsOffsets(vsyncDuration);
+ } else {
+ return getDefaultOffsets(vsyncDuration);
+ }
+}
+
+namespace {
+std::chrono::nanoseconds sfOffsetToDuration(nsecs_t sfOffset, nsecs_t vsyncDuration) {
+ return std::chrono::nanoseconds(vsyncDuration - sfOffset);
+}
+
+std::chrono::nanoseconds appOffsetToDuration(nsecs_t appOffset, nsecs_t sfOffset,
+ nsecs_t vsyncDuration) {
+ auto duration = vsyncDuration + (sfOffset - appOffset);
+ if (duration < vsyncDuration) {
+ duration += vsyncDuration;
+ }
+
+ return std::chrono::nanoseconds(duration);
+}
+} // namespace
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+ const auto earlySfOffset =
+ mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+ : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyAppOffset = mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
+ const auto earlyGpuSfOffset =
+ mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+ : mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyGpuAppOffset = mEarlyGpuAppOffsetNs.value_or(mVSyncPhaseOffsetNs);
+ const auto lateSfOffset = mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+ ? mSfVSyncPhaseOffsetNs
+ : mSfVSyncPhaseOffsetNs - vsyncDuration;
+ const auto lateAppOffset = mVSyncPhaseOffsetNs;
+
+ return {
+ .early = {.sfOffset = earlySfOffset,
+ .appOffset = earlyAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(earlyAppOffset, earlySfOffset, vsyncDuration)},
+ .earlyGpu = {.sfOffset = earlyGpuSfOffset,
+ .appOffset = earlyGpuAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset, earlyGpuSfOffset,
+ vsyncDuration)},
+ .late = {.sfOffset = lateSfOffset,
+ .appOffset = lateAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration)},
+ };
+}
+
+PhaseOffsets::VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+ const auto earlySfOffset =
+ mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+ ? mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
+ : mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyAppOffset = mHighFpsEarlyAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
+ const auto earlyGpuSfOffset = mHighFpsEarlyGpuSfOffsetNs.value_or(
+ mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+
+ ? mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
+ : mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration;
+ const auto earlyGpuAppOffset = mHighFpsEarlyGpuAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs);
+ const auto lateSfOffset = mHighFpsSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+ ? mHighFpsSfVSyncPhaseOffsetNs
+ : mHighFpsSfVSyncPhaseOffsetNs - vsyncDuration;
+ const auto lateAppOffset = mHighFpsVSyncPhaseOffsetNs;
+
+ return {
+ .early =
+ {
+ .sfOffset = earlySfOffset,
+ .appOffset = earlyAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyAppOffset, earlySfOffset,
+ vsyncDuration),
+ },
+ .earlyGpu =
+ {
+ .sfOffset = earlyGpuSfOffset,
+ .appOffset = earlyGpuAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration),
+ .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset,
+ earlyGpuSfOffset, vsyncDuration),
+ },
+ .late =
+ {
+ .sfOffset = lateSfOffset,
+ .appOffset = lateAppOffset,
+ .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration),
+ .appWorkDuration =
+ appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration),
+ },
+ };
+}
+
+static void validateSysprops() {
+ const auto validatePropertyBool = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
+ };
+
+ validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ const auto validateProperty = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
+ "%s is set to %" PRId64 " but expecting duration", prop,
+ getProperty(prop).value_or(-1));
+ };
+
+ validateProperty("debug.sf.early_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_phase_offset_ns");
+ validateProperty("debug.sf.early_app_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+}
+
+namespace {
+nsecs_t sfDurationToOffset(std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
+ return vsyncDuration - sfDuration.count() % vsyncDuration;
+}
+
+nsecs_t appDurationToOffset(std::chrono::nanoseconds appDuration,
+ std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) {
+ return vsyncDuration - (appDuration + sfDuration).count() % vsyncDuration;
+}
+} // namespace
+
+WorkDuration::VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
+ const auto sfDurationFixup = [vsyncDuration](nsecs_t duration) {
+ return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) - 1ms
+ : std::chrono::nanoseconds(duration);
+ };
+
+ const auto appDurationFixup = [vsyncDuration](nsecs_t duration) {
+ return duration == -1 ? std::chrono::nanoseconds(vsyncDuration)
+ : std::chrono::nanoseconds(duration);
+ };
+
+ const auto sfEarlyDuration = sfDurationFixup(mSfEarlyDuration);
+ const auto appEarlyDuration = appDurationFixup(mAppEarlyDuration);
+ const auto sfEarlyGpuDuration = sfDurationFixup(mSfEarlyGpuDuration);
+ const auto appEarlyGpuDuration = appDurationFixup(mAppEarlyGpuDuration);
+ const auto sfDuration = sfDurationFixup(mSfDuration);
+ const auto appDuration = appDurationFixup(mAppDuration);
+
+ return {
+ .early =
+ {
+
+ .sfOffset = sfEarlyDuration.count() < vsyncDuration
+ ? sfDurationToOffset(sfEarlyDuration, vsyncDuration)
+ : sfDurationToOffset(sfEarlyDuration, vsyncDuration) -
+ vsyncDuration,
+
+ .appOffset = appDurationToOffset(appEarlyDuration, sfEarlyDuration,
+ vsyncDuration),
+
+ .sfWorkDuration = sfEarlyDuration,
+ .appWorkDuration = appEarlyDuration,
+ },
+ .earlyGpu =
+ {
+
+ .sfOffset = sfEarlyGpuDuration.count() < vsyncDuration
+
+ ? sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration)
+ : sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration) -
+ vsyncDuration,
+
+ .appOffset = appDurationToOffset(appEarlyGpuDuration,
+ sfEarlyGpuDuration, vsyncDuration),
+ .sfWorkDuration = sfEarlyGpuDuration,
+ .appWorkDuration = appEarlyGpuDuration,
+ },
+ .late =
+ {
+
+ .sfOffset = sfDuration.count() < vsyncDuration
+
+ ? sfDurationToOffset(sfDuration, vsyncDuration)
+ : sfDurationToOffset(sfDuration, vsyncDuration) - vsyncDuration,
+
+ .appOffset =
+ appDurationToOffset(appDuration, sfDuration, vsyncDuration),
+
+ .sfWorkDuration = sfDuration,
+ .appWorkDuration = appDuration,
+ },
+ };
+}
+
+WorkDuration::WorkDuration(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : WorkDuration(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().getFps(),
+ getProperty("debug.sf.late.sf.duration").value_or(-1),
+ getProperty("debug.sf.late.app.duration").value_or(-1),
+ getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
+ getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
+ validateSysprops();
+}
+
+WorkDuration::WorkDuration(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+ nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration,
+ nsecs_t appEarlyGpuDuration)
+ : VsyncConfiguration(currentFps),
+ mSfDuration(sfDuration),
+ mAppDuration(appDuration),
+ mSfEarlyDuration(sfEarlyDuration),
+ mAppEarlyDuration(appEarlyDuration),
+ mSfEarlyGpuDuration(sfEarlyGpuDuration),
+ mAppEarlyGpuDuration(appEarlyGpuDuration) {
+ initializeOffsets(refreshRates);
+}
+
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
new file mode 100644
index 0000000..c27a25d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unordered_map>
+
+#include "RefreshRateConfigs.h"
+#include "VsyncModulator.h"
+
+namespace android::scheduler {
+
+/*
+ * This class encapsulates vsync configurations for different refresh rates. Depending
+ * on what refresh rate we are using, and wheter we are composing in GL,
+ * different offsets will help us with latency. This class keeps track of
+ * which mode the device is on, and returns approprate offsets when needed.
+ */
+class VsyncConfiguration {
+public:
+ using VsyncConfigSet = VsyncModulator::VsyncConfigSet;
+
+ virtual ~VsyncConfiguration() = default;
+ virtual VsyncConfigSet getCurrentConfigs() const = 0;
+ virtual VsyncConfigSet getConfigsForRefreshRate(float fps) const = 0;
+
+ virtual void setRefreshRateFps(float fps) = 0;
+
+ virtual void dump(std::string& result) const = 0;
+};
+
+namespace impl {
+
+/*
+ * This is a common implementation for both phase offsets and durations.
+ * PhaseOffsets and WorkDuration derive from this class and implement the
+ * constructOffsets method
+ */
+class VsyncConfiguration : public scheduler::VsyncConfiguration {
+public:
+ explicit VsyncConfiguration(float currentFps);
+
+ // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
+ VsyncConfigSet getConfigsForRefreshRate(float fps) const override;
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ VsyncConfigSet getCurrentConfigs() const override {
+ return getConfigsForRefreshRate(mRefreshRateFps);
+ }
+
+ // This function should be called when the device is switching between different
+ // refresh rates, to properly update the offsets.
+ void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& result) const override;
+
+protected:
+ void initializeOffsets(const std::vector<float>& refreshRates);
+ virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
+
+ std::unordered_map<float, VsyncConfigSet> mOffsets;
+ std::atomic<float> mRefreshRateFps;
+};
+
+/*
+ * This is the old implementation of phase offsets and considered as deprecated.
+ * WorkDuration is the new implementation.
+ */
+class PhaseOffsets : public VsyncConfiguration {
+public:
+ explicit PhaseOffsets(const scheduler::RefreshRateConfigs&);
+
+protected:
+ // Used for unit tests
+ PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
+ nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync);
+
+private:
+ VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+
+ VsyncConfigSet getDefaultOffsets(nsecs_t vsyncPeriod) const;
+ VsyncConfigSet getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+
+ const nsecs_t mVSyncPhaseOffsetNs;
+ const nsecs_t mSfVSyncPhaseOffsetNs;
+ const std::optional<nsecs_t> mEarlySfOffsetNs;
+ const std::optional<nsecs_t> mEarlyGpuSfOffsetNs;
+ const std::optional<nsecs_t> mEarlyAppOffsetNs;
+ const std::optional<nsecs_t> mEarlyGpuAppOffsetNs;
+
+ const nsecs_t mHighFpsVSyncPhaseOffsetNs;
+ const nsecs_t mHighFpsSfVSyncPhaseOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlySfOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyGpuSfOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyAppOffsetNs;
+ const std::optional<nsecs_t> mHighFpsEarlyGpuAppOffsetNs;
+
+ const nsecs_t mThresholdForNextVsync;
+};
+
+/*
+ * Class that encapsulates the phase offsets for SurfaceFlinger and App.
+ * The offsets are calculated from durations for each one of the (late, early, earlyGpu)
+ * offset types.
+ */
+class WorkDuration : public VsyncConfiguration {
+public:
+ explicit WorkDuration(const scheduler::RefreshRateConfigs&);
+
+protected:
+ // Used for unit tests
+ WorkDuration(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
+ nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
+
+private:
+ VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+
+ const nsecs_t mSfDuration;
+ const nsecs_t mAppDuration;
+
+ const nsecs_t mSfEarlyDuration;
+ const nsecs_t mAppEarlyDuration;
+
+ const nsecs_t mSfEarlyGpuDuration;
+ const nsecs_t mAppEarlyGpuDuration;
+};
+
+} // namespace impl
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
new file mode 100644
index 0000000..1f821be
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#undef LOG_TAG
+#define LOG_TAG "VsyncModulator"
+
+#include "VsyncModulator.h"
+
+#include <android-base/properties.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <chrono>
+#include <cinttypes>
+#include <mutex>
+
+using namespace std::chrono_literals;
+
+namespace android::scheduler {
+
+const std::chrono::nanoseconds VsyncModulator::MIN_EARLY_TRANSACTION_TIME = 1ms;
+
+VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now)
+ : mVsyncConfigSet(config),
+ mNow(now),
+ mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
+
+VsyncModulator::VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mVsyncConfigSet = config;
+ return updateVsyncConfigLocked();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(
+ TransactionSchedule schedule) {
+ switch (schedule) {
+ case Schedule::EarlyStart:
+ ALOGW_IF(mExplicitEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
+ mExplicitEarlyWakeup = true;
+ break;
+ case Schedule::EarlyEnd:
+ ALOGW_IF(!mExplicitEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__);
+ mExplicitEarlyWakeup = false;
+ break;
+ case Schedule::Early:
+ case Schedule::Late:
+ // No change to mExplicitEarlyWakeup for non-explicit states.
+ break;
+ }
+
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
+ }
+
+ if (!mExplicitEarlyWakeup && (schedule == Schedule::Early || schedule == Schedule::EarlyEnd)) {
+ mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
+ mEarlyTransactionStartTime = mNow();
+ }
+
+ // An early transaction stays an early transaction.
+ if (schedule == mTransactionSchedule || mTransactionSchedule == Schedule::EarlyEnd) {
+ return std::nullopt;
+ }
+ mTransactionSchedule = schedule;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onTransactionCommit() {
+ mLastTransactionCommitTime = mNow();
+ if (mTransactionSchedule == Schedule::Late) return std::nullopt;
+ mTransactionSchedule = Schedule::Late;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeInitiated() {
+ if (mRefreshRateChangePending) return std::nullopt;
+ mRefreshRateChangePending = true;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeCompleted() {
+ if (!mRefreshRateChangePending) return std::nullopt;
+ mRefreshRateChangePending = false;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfigOpt VsyncModulator::onDisplayRefresh(bool usedGpuComposition) {
+ bool updateOffsetsNeeded = false;
+
+ if (mEarlyTransactionStartTime.load() + MIN_EARLY_TRANSACTION_TIME <=
+ mLastTransactionCommitTime.load()) {
+ if (mEarlyTransactionFrames > 0) {
+ mEarlyTransactionFrames--;
+ updateOffsetsNeeded = true;
+ }
+ }
+ if (usedGpuComposition) {
+ mEarlyGpuFrames = MIN_EARLY_GPU_FRAMES;
+ updateOffsetsNeeded = true;
+ } else if (mEarlyGpuFrames > 0) {
+ mEarlyGpuFrames--;
+ updateOffsetsNeeded = true;
+ }
+
+ if (!updateOffsetsNeeded) return std::nullopt;
+ return updateVsyncConfig();
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::getVsyncConfig() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mVsyncConfig;
+}
+
+const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
+ // Early offsets are used if we're in the middle of a refresh rate
+ // change, or if we recently begin a transaction.
+ if (mExplicitEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd ||
+ mEarlyTransactionFrames > 0 || mRefreshRateChangePending) {
+ return mVsyncConfigSet.early;
+ } else if (mEarlyGpuFrames > 0) {
+ return mVsyncConfigSet.earlyGpu;
+ } else {
+ return mVsyncConfigSet.late;
+ }
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfig() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return updateVsyncConfigLocked();
+}
+
+VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
+ const VsyncConfig& offsets = getNextVsyncConfig();
+ mVsyncConfig = offsets;
+
+ if (mTraceDetailedInfo) {
+ const bool isEarly = &offsets == &mVsyncConfigSet.early;
+ const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu;
+ const bool isLate = &offsets == &mVsyncConfigSet.late;
+
+ ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
+ ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu);
+ ATRACE_INT("Vsync-LateOffsetsOn", isLate);
+ }
+
+ return offsets;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
new file mode 100644
index 0000000..355a14a
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <mutex>
+#include <optional>
+
+#include <android-base/thread_annotations.h>
+#include <utils/Timers.h>
+
+namespace android::scheduler {
+
+// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
+// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
+// fixed number of frames, respectively.
+enum class TransactionSchedule {
+ Late, // Default.
+ Early, // Deprecated.
+ EarlyStart,
+ EarlyEnd
+};
+
+// Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
+class VsyncModulator {
+public:
+ // Number of frames to keep early offsets after an early transaction or GPU composition.
+ // This acts as a low-pass filter in case subsequent transactions are delayed, or if the
+ // composition strategy alternates on subsequent frames.
+ static constexpr int MIN_EARLY_TRANSACTION_FRAMES = 2;
+ static constexpr int MIN_EARLY_GPU_FRAMES = 2;
+
+ // Duration to delay the MIN_EARLY_TRANSACTION_FRAMES countdown after an early transaction.
+ // This may keep early offsets for an extra frame, but avoids a race with transaction commit.
+ static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;
+
+ // Phase offsets and work durations for SF and app deadlines from VSYNC.
+ struct VsyncConfig {
+ nsecs_t sfOffset;
+ nsecs_t appOffset;
+ std::chrono::nanoseconds sfWorkDuration;
+ std::chrono::nanoseconds appWorkDuration;
+
+ bool operator==(const VsyncConfig& other) const {
+ return sfOffset == other.sfOffset && appOffset == other.appOffset &&
+ sfWorkDuration == other.sfWorkDuration &&
+ appWorkDuration == other.appWorkDuration;
+ }
+
+ bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
+ };
+
+ using VsyncConfigOpt = std::optional<VsyncConfig>;
+
+ struct VsyncConfigSet {
+ VsyncConfig early; // Used for early transactions, and during refresh rate change.
+ VsyncConfig earlyGpu; // Used during GPU composition.
+ VsyncConfig late; // Default.
+
+ bool operator==(const VsyncConfigSet& other) const {
+ return early == other.early && earlyGpu == other.earlyGpu && late == other.late;
+ }
+
+ bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
+ };
+
+ using Clock = std::chrono::steady_clock;
+ using TimePoint = Clock::time_point;
+ using Now = TimePoint (*)();
+
+ explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now);
+
+ VsyncConfig getVsyncConfig() const EXCLUDES(mMutex);
+
+ [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);
+
+ // Changes offsets in response to transaction flags or commit.
+ [[nodiscard]] VsyncConfigOpt setTransactionSchedule(TransactionSchedule);
+ [[nodiscard]] VsyncConfigOpt onTransactionCommit();
+
+ // Called when we send a refresh rate change to hardware composer, so that
+ // we can move into early offsets.
+ [[nodiscard]] VsyncConfigOpt onRefreshRateChangeInitiated();
+
+ // Called when we detect from VSYNC signals that the refresh rate changed.
+ // This way we can move out of early offsets if no longer necessary.
+ [[nodiscard]] VsyncConfigOpt onRefreshRateChangeCompleted();
+
+ [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
+
+private:
+ const VsyncConfig& getNextVsyncConfig() const REQUIRES(mMutex);
+ [[nodiscard]] VsyncConfig updateVsyncConfig() EXCLUDES(mMutex);
+ [[nodiscard]] VsyncConfig updateVsyncConfigLocked() REQUIRES(mMutex);
+
+ mutable std::mutex mMutex;
+ VsyncConfigSet mVsyncConfigSet GUARDED_BY(mMutex);
+
+ VsyncConfig mVsyncConfig GUARDED_BY(mMutex){mVsyncConfigSet.late};
+
+ using Schedule = TransactionSchedule;
+ std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
+ std::atomic<bool> mExplicitEarlyWakeup = false;
+
+ std::atomic<bool> mRefreshRateChangePending = false;
+
+ std::atomic<int> mEarlyTransactionFrames = 0;
+ std::atomic<int> mEarlyGpuFrames = 0;
+ std::atomic<TimePoint> mEarlyTransactionStartTime = TimePoint();
+ std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint();
+
+ const Now mNow;
+ const bool mTraceDetailedInfo;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4e0d375..f6028d5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -122,8 +122,8 @@
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
+#include "Scheduler/VsyncConfiguration.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerProperties.h"
#include "SurfaceInterceptor.h"
@@ -862,8 +862,9 @@
state->layerStack = display->getLayerStack();
state->orientation = display->getOrientation();
- const Rect viewport = display->getViewport();
- state->viewport = viewport.isValid() ? viewport.getSize() : display->getSize();
+ const Rect layerStackRect = display->getLayerStackSpaceRect();
+ state->layerStackSpaceRect =
+ layerStackRect.isValid() ? layerStackRect.getSize() : display->getSize();
return NO_ERROR;
}
@@ -947,9 +948,10 @@
const nsecs_t period = hwConfig->getVsyncPeriod();
config.refreshRate = 1e9f / period;
- const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
- config.appVsyncOffset = offsets.late.app;
- config.sfVsyncOffset = offsets.late.sf;
+ const auto vsyncConfigSet =
+ mVsyncConfiguration->getConfigsForRefreshRate(config.refreshRate);
+ config.appVsyncOffset = vsyncConfigSet.late.appOffset;
+ config.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
config.configGroup = hwConfig->getConfigGroup();
// This is how far in advance a buffer must be queued for
@@ -1036,10 +1038,9 @@
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// DispSync model is locked.
- mVSyncModulator->onRefreshRateChangeInitiated();
+ modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ updatePhaseConfiguration(refreshRate);
mScheduler->setConfigChangePending(true);
}
@@ -1098,8 +1099,7 @@
if (refreshRate.getVsyncPeriod() != oldRefreshRate.getVsyncPeriod()) {
mTimeStats->incrementRefreshRateSwitches();
}
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ updatePhaseConfiguration(refreshRate);
ATRACE_INT("ActiveConfigFPS", refreshRate.getFps());
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
@@ -1119,9 +1119,9 @@
const auto& refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
+
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
- mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
- mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ updatePhaseConfiguration(refreshRate);
mScheduler->setConfigChangePending(false);
}
@@ -1600,7 +1600,7 @@
bool periodFlushed = false;
mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
if (periodFlushed) {
- mVSyncModulator->onRefreshRateChangeCompleted();
+ modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
}
}
@@ -1790,8 +1790,8 @@
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
}
bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
@@ -1823,7 +1823,8 @@
mScheduler->getDisplayStatInfo(&stats);
const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(now);
// Inflate the expected present time if we're targetting the next vsync.
- return mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod;
+ return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? presentTime
+ : presentTime + stats.vsyncPeriod;
}
void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
@@ -2120,7 +2121,8 @@
}
// TODO: b/160583065 Enable skip validation when SF caches all client composition layers
- mVSyncModulator->onRefreshed(mHadClientComposition || mReusedClientComposition);
+ const bool usedGpuComposition = mHadClientComposition || mReusedClientComposition;
+ modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
mLayersWithQueuedFrames.clear();
if (mVisibleRegionsDirty) {
@@ -2182,12 +2184,12 @@
nsecs_t compositeToPresentLatency) {
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
- nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+ nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0)
? (stats.vsyncPeriod -
- (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
- : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
+ (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % stats.vsyncPeriod))
+ : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % stats.vsyncPeriod);
- // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
+ // Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval.
if (idealLatency <= 0) {
idealLatency = stats.vsyncPeriod;
}
@@ -2197,7 +2199,7 @@
// Reducing jitter is important if an app attempts to extrapolate
// something (such as user input) to an accurate diasplay time.
// Snapping also allows an app to precisely calculate
- // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
+ // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval).
nsecs_t bias = stats.vsyncPeriod / 2;
int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
nsecs_t snappedCompositeToPresentLatency =
@@ -2368,7 +2370,7 @@
}
FloatRect SurfaceFlinger::getLayerClipBoundsForDisplay(const DisplayDevice& displayDevice) const {
- return displayDevice.getViewport().toFloatRect();
+ return displayDevice.getLayerStackSpaceRect().toFloatRect();
}
void SurfaceFlinger::computeLayerBounds() {
@@ -2416,7 +2418,7 @@
// with mStateLock held to guarantee that mCurrentState won't change
// until the transaction is committed.
- mVSyncModulator->onTransactionHandled();
+ modulateVsync(&VsyncModulator::onTransactionCommit);
transactionFlags = getTransactionFlags(eTransactionMask);
handleTransactionLocked(transactionFlags);
@@ -2569,7 +2571,8 @@
}
display->setLayerStack(state.layerStack);
- display->setProjection(state.orientation, state.viewport, state.frame);
+ display->setProjection(state.orientation, state.layerStackSpaceRect,
+ state.orientedDisplaySpaceRect);
display->setDisplayName(state.displayName);
return display;
@@ -2698,10 +2701,10 @@
display->setLayerStack(currentState.layerStack);
}
if ((currentState.orientation != drawingState.orientation) ||
- (currentState.viewport != drawingState.viewport) ||
- (currentState.frame != drawingState.frame)) {
- display->setProjection(currentState.orientation, currentState.viewport,
- currentState.frame);
+ (currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) ||
+ (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
+ display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
+ currentState.orientedDisplaySpaceRect);
}
if (currentState.width != drawingState.width ||
currentState.height != drawingState.height) {
@@ -2976,22 +2979,23 @@
currentConfig, hal::PowerMode::OFF);
mRefreshRateStats->setConfigMode(currentConfig);
- mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+ mVsyncConfiguration = getFactory().createVsyncConfiguration(*mRefreshRateConfigs);
+ mVsyncModulator.emplace(mVsyncConfiguration->getCurrentConfigs());
// start the EventThread
mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
mAppConnectionHandle =
- mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
+ mScheduler->createConnection("app",
+ mVsyncConfiguration->getCurrentConfigs().late.appOffset,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
+ mScheduler->createConnection("sf",
+ mVsyncConfiguration->getCurrentConfigs().late.sfOffset,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
- mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
- mPhaseConfiguration->getCurrentOffsets());
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
@@ -3007,10 +3011,24 @@
mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId, currentConfig,
vsyncPeriod);
+ static auto ignorePresentFences =
+ base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false);
+ mScheduler->setIgnorePresentFences(
+ ignorePresentFences ||
+ getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE));
}
-void SurfaceFlinger::commitTransaction()
-{
+void SurfaceFlinger::updatePhaseConfiguration(const RefreshRate& refreshRate) {
+ mVsyncConfiguration->setRefreshRateFps(refreshRate.getFps());
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()));
+}
+
+void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config) {
+ mScheduler->setPhaseOffset(mAppConnectionHandle, config.appOffset);
+ mScheduler->setPhaseOffset(mSfConnectionHandle, config.sfOffset);
+}
+
+void SurfaceFlinger::commitTransaction() {
commitTransactionLocked();
mTransactionPending = false;
mAnimTransactionPending = false;
@@ -3046,15 +3064,19 @@
// clear the "changed" flags in current state
mCurrentState.colorMatrixChanged = false;
- mDrawingState.traverse([&](Layer* layer) {
- layer->commitChildList();
-
- // If the layer can be reached when traversing mDrawingState, then the layer is no
- // longer offscreen. Remove the layer from the offscreenLayer set.
- if (mOffscreenLayers.count(layer)) {
- mOffscreenLayers.erase(layer);
- }
- });
+ for (const auto& rootLayer : mDrawingState.layersSortedByZ) {
+ rootLayer->commitChildList();
+ }
+ // TODO(b/163019109): See if this traversal is needed at all...
+ if (!mOffscreenLayers.empty()) {
+ mDrawingState.traverse([&](Layer* layer) {
+ // If the layer can be reached when traversing mDrawingState, then the layer is no
+ // longer offscreen. Remove the layer from the offscreenLayer set.
+ if (mOffscreenLayers.count(layer)) {
+ mOffscreenLayers.erase(layer);
+ }
+ });
+ }
commitOffscreenLayers();
mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
@@ -3250,16 +3272,13 @@
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
- return setTransactionFlags(flags, Scheduler::TransactionStart::Normal);
+ return setTransactionFlags(flags, TransactionSchedule::Late);
}
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
- Scheduler::TransactionStart transactionStart) {
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule) {
uint32_t old = mTransactionFlags.fetch_or(flags);
- mVSyncModulator->setTransactionStart(transactionStart);
- if ((old & flags)==0) { // wake the server up
- signalTransaction();
- }
+ modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
+ if ((old & flags) == 0) signalTransaction();
return old;
}
@@ -3292,7 +3311,8 @@
mPendingInputWindowCommands, transaction.desiredPresentTime,
transaction.buffer, transaction.postTime,
transaction.privileged, transaction.hasListenerCallbacks,
- transaction.listenerCallbacks, /*isMainThread*/ true);
+ transaction.listenerCallbacks, transaction.originPID,
+ transaction.originUID, /*isMainThread*/ true);
transactionQueue.pop();
flushedATransaction = true;
}
@@ -3372,17 +3392,22 @@
mExpectedPresentTime = calculateExpectedPresentTime(systemTime());
}
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int originPID = ipc->getCallingPid();
+ const int originUID = ipc->getCallingUid();
+
if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
uncacheBuffer, postTime, privileged,
- hasListenerCallbacks, listenerCallbacks);
+ hasListenerCallbacks, listenerCallbacks, originPID,
+ originUID);
setTransactionFlags(eTransactionFlushNeeded);
return;
}
applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
uncacheBuffer, postTime, privileged, hasListenerCallbacks,
- listenerCallbacks);
+ listenerCallbacks, originPID, originUID, /*isMainThread*/ false);
}
void SurfaceFlinger::applyTransactionState(
@@ -3390,7 +3415,7 @@
const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- bool isMainThread) {
+ int originPID, int originUID, bool isMainThread) {
uint32_t transactionFlags = 0;
if (flags & eAnimation) {
@@ -3467,22 +3492,17 @@
mForceTraversal = true;
}
- const auto transactionStart = [](uint32_t flags) {
- if (flags & eEarlyWakeup) {
- return Scheduler::TransactionStart::Early;
- }
- if (flags & eExplicitEarlyWakeupEnd) {
- return Scheduler::TransactionStart::EarlyEnd;
- }
- if (flags & eExplicitEarlyWakeupStart) {
- return Scheduler::TransactionStart::EarlyStart;
- }
- return Scheduler::TransactionStart::Normal;
+ const auto schedule = [](uint32_t flags) {
+ if (flags & eEarlyWakeup) return TransactionSchedule::Early;
+ if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+ if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+ return TransactionSchedule::Late;
}(flags);
if (transactionFlags) {
if (mInterceptor->isEnabled()) {
- mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
+ mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
+ originPID, originUID);
}
// TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
@@ -3496,7 +3516,7 @@
}
// this triggers the transaction
- setTransactionFlags(transactionFlags, transactionStart);
+ setTransactionFlags(transactionFlags, schedule);
if (flags & eAnimation) {
mAnimTransactionPending = true;
@@ -3534,11 +3554,10 @@
}
}
} else {
- // even if a transaction is not needed, we need to update VsyncModulator
- // about explicit early indications
- if (transactionStart == Scheduler::TransactionStart::EarlyStart ||
- transactionStart == Scheduler::TransactionStart::EarlyEnd) {
- mVSyncModulator->setTransactionStart(transactionStart);
+ // Update VsyncModulator state machine even if transaction is not needed.
+ if (schedule == TransactionSchedule::EarlyStart ||
+ schedule == TransactionSchedule::EarlyEnd) {
+ modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
}
}
}
@@ -3568,12 +3587,12 @@
state.orientation = s.orientation;
flags |= eDisplayTransactionNeeded;
}
- if (state.frame != s.frame) {
- state.frame = s.frame;
+ if (state.orientedDisplaySpaceRect != s.orientedDisplaySpaceRect) {
+ state.orientedDisplaySpaceRect = s.orientedDisplaySpaceRect;
flags |= eDisplayTransactionNeeded;
}
- if (state.viewport != s.viewport) {
- state.viewport = s.viewport;
+ if (state.layerStackSpaceRect != s.layerStackSpaceRect) {
+ state.layerStackSpaceRect = s.layerStackSpaceRect;
flags |= eDisplayTransactionNeeded;
}
}
@@ -3808,7 +3827,7 @@
if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eFrameChanged) {
- if (layer->setFrame(s.frame)) flags |= eTraversalNeeded;
+ if (layer->setFrame(s.orientedDisplaySpaceRect)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eAcquireFenceChanged) {
if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
@@ -4191,8 +4210,8 @@
d.token = token;
d.layerStack = 0;
d.orientation = ui::ROTATION_0;
- d.frame.makeInvalid();
- d.viewport.makeInvalid();
+ d.orientedDisplaySpaceRect.makeInvalid();
+ d.layerStackSpaceRect.makeInvalid();
d.width = 0;
d.height = 0;
displays.add(d);
@@ -4446,7 +4465,7 @@
mRefreshRateStats->dump(result);
result.append("\n");
- mPhaseConfiguration->dump(result);
+ mVsyncConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
@@ -4958,11 +4977,13 @@
// special permissions.
case SET_FRAME_RATE:
case GET_DISPLAY_BRIGHTNESS_SUPPORT:
+ // captureLayers and captureDisplay will handle the permission check in the function
+ case CAPTURE_LAYERS:
+ case CAPTURE_DISPLAY:
case SET_DISPLAY_BRIGHTNESS: {
return OK;
}
- case CAPTURE_LAYERS:
- case CAPTURE_DISPLAY:
+
case ADD_REGION_SAMPLING_LISTENER:
case REMOVE_REGION_SAMPLING_LISTENER: {
// codes that require permission check
@@ -5434,18 +5455,35 @@
}
}
+static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if (uid == AID_GRAPHICS || PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
+ return OK;
+ }
+
+ // If the caller doesn't have the correct permissions but is only attempting to screenshot
+ // itself, we allow it to continue.
+ if (captureArgs.uid == uid) {
+ return OK;
+ }
+
+ ALOGE("Permission Denial: can't take screenshot pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+}
+
status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
ScreenCaptureResults& captureResults) {
ATRACE_CALL();
- if (!args.displayToken) return BAD_VALUE;
-
- auto renderAreaRotation = ui::Transform::toRotationFlags(args.rotation);
- if (renderAreaRotation == ui::Transform::ROT_INVALID) {
- ALOGE("%s: Invalid rotation: %s", __FUNCTION__, toCString(args.rotation));
- renderAreaRotation = ui::Transform::ROT_0;
+ status_t validate = validateScreenshotPermissions(args);
+ if (validate != OK) {
+ return validate;
}
+ if (!args.displayToken) return BAD_VALUE;
+
wp<DisplayDevice> displayWeak;
ui::LayerStack layerStack;
ui::Size reqSize(args.width, args.height);
@@ -5457,10 +5495,9 @@
displayWeak = display;
layerStack = display->getLayerStack();
- // set the requested width/height to the logical display viewport size
- // by default
+ // set the requested width/height to the logical display layer stack rect size by default
if (args.width == 0 || args.height == 0) {
- reqSize = display->getViewport().getSize();
+ reqSize = display->getLayerStackSpaceRect().getSize();
}
const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
@@ -5469,14 +5506,14 @@
RenderAreaFuture renderAreaFuture = promise::defer([=] {
return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, dataspace,
- renderAreaRotation, args.captureSecureLayers);
+ args.useIdentityTransform, args.captureSecureLayers);
});
- auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
- traverseLayersInLayerStack(layerStack, visitor);
+ auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, args.uid, visitor);
};
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
- args.pixelFormat, args.useIdentityTransform, captureResults);
+ args.pixelFormat, captureResults);
}
status_t SurfaceFlinger::setSchedFifo(bool enabled) {
@@ -5523,7 +5560,6 @@
ui::LayerStack layerStack;
wp<DisplayDevice> displayWeak;
ui::Size size;
- ui::Transform::RotationFlags captureOrientation;
ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
@@ -5534,57 +5570,43 @@
layerStack = display->getLayerStack();
displayWeak = display;
- size = display->getViewport().getSize();
+ size = display->getLayerStackSpaceRect().getSize();
- const auto orientation = display->getOrientation();
- captureOrientation = ui::Transform::toRotationFlags(orientation);
-
- switch (captureOrientation) {
- case ui::Transform::ROT_90:
- captureOrientation = ui::Transform::ROT_270;
- break;
-
- case ui::Transform::ROT_270:
- captureOrientation = ui::Transform::ROT_90;
- break;
-
- case ui::Transform::ROT_INVALID:
- ALOGE("%s: Invalid orientation: %s", __FUNCTION__, toCString(orientation));
- captureOrientation = ui::Transform::ROT_0;
- break;
-
- default:
- break;
- }
dataspace =
pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
}
RenderAreaFuture renderAreaFuture = promise::defer([=] {
return DisplayRenderArea::create(displayWeak, Rect(), size,
- captureResults.capturedDataspace, captureOrientation,
+ captureResults.capturedDataspace,
+ false /* useIdentityTransform */,
false /* captureSecureLayers */);
});
auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
- traverseLayersInLayerStack(layerStack, visitor);
+ traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
};
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
- ui::PixelFormat::RGBA_8888, false /* useIdentityTransform */,
- captureResults);
+ ui::PixelFormat::RGBA_8888, captureResults);
}
status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
ScreenCaptureResults& captureResults) {
ATRACE_CALL();
+ status_t validate = validateScreenshotPermissions(args);
+ if (validate != OK) {
+ return validate;
+ }
+
ui::Size reqSize;
sp<Layer> parent;
Rect crop(args.sourceCrop);
std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
- Rect displayViewport;
+ Rect layerStackSpaceRect;
ui::Dataspace dataspace;
+ bool captureSecureLayers;
{
Mutex::Autolock lock(mStateLock);
@@ -5634,10 +5656,12 @@
return NAME_NOT_FOUND;
}
- displayViewport = display->getViewport();
+ layerStackSpaceRect = display->getLayerStackSpaceRect();
const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
dataspace = pickDataspaceFromColorMode(colorMode);
+
+ captureSecureLayers = args.captureSecureLayers && display->isSecure();
} // mStateLock
// really small crop or frameScale
@@ -5649,18 +5673,19 @@
}
bool childrenOnly = args.childrenOnly;
-
RenderAreaFuture renderAreaFuture = promise::defer([=]() -> std::unique_ptr<RenderArea> {
return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
- childrenOnly, displayViewport);
+ childrenOnly, layerStackSpaceRect,
+ captureSecureLayers);
});
- auto traverseLayers = [parent, childrenOnly,
- &excludeLayers](const LayerVector::Visitor& visitor) {
+ auto traverseLayers = [parent, args, &excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
if (!layer->isVisible()) {
return;
- } else if (childrenOnly && layer == parent.get()) {
+ } else if (args.childrenOnly && layer == parent.get()) {
+ return;
+ } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
return;
}
@@ -5677,13 +5702,12 @@
};
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
- args.pixelFormat, false, captureResults);
+ args.pixelFormat, captureResults);
}
status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
- bool useIdentityTransform,
ScreenCaptureResults& captureResults) {
ATRACE_CALL();
@@ -5696,13 +5720,12 @@
1 /* layerCount */, usage, "screenshot");
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
- useIdentityTransform, false /* regionSampling */, captureResults);
+ false /* regionSampling */, captureResults);
}
status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer,
- bool useIdentityTransform, bool regionSampling,
+ const sp<GraphicBuffer>& buffer, bool regionSampling,
ScreenCaptureResults& captureResults) {
const int uid = IPCThreadState::self()->getCallingUid();
const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
@@ -5729,8 +5752,8 @@
Mutex::Autolock lock(mStateLock);
renderArea->render([&] {
result = renderScreenImplLocked(*renderArea, traverseLayers, buffer.get(),
- useIdentityTransform, forSystem, &fd,
- regionSampling, captureResults);
+ forSystem, &fd, regionSampling,
+ captureResults);
});
return {result, fd};
@@ -5747,8 +5770,7 @@
status_t SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer,
- bool useIdentityTransform, bool forSystem,
+ const sp<GraphicBuffer>& buffer, bool forSystem,
int* outSyncFd, bool regionSampling,
ScreenCaptureResults& captureResults) {
ATRACE_CALL();
@@ -5774,7 +5796,7 @@
const auto sourceCrop = renderArea.getSourceCrop();
const auto transform = renderArea.getTransform();
const auto rotation = renderArea.getRotationFlags();
- const auto& displayViewport = renderArea.getDisplayViewport();
+ const auto& layerStackSpaceRect = renderArea.getLayerStackSpaceRect();
renderengine::DisplaySettings clientCompositionDisplay;
std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers;
@@ -5806,13 +5828,12 @@
Region clip(renderArea.getBounds());
compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
clip,
- useIdentityTransform,
layer->needsFilteringForScreenshots(display.get(), transform) ||
renderArea.needsFiltering(),
renderArea.isSecure(),
supportProtectedContent,
clearRegion,
- displayViewport,
+ layerStackSpaceRect,
clientCompositionDisplay.outputDataspace,
true, /* realContentIsVisible */
false, /* clearContent */
@@ -5886,22 +5907,25 @@
layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
}
-void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack,
+void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const int32_t uid,
const LayerVector::Visitor& visitor) {
// We loop through the first level of layers without traversing,
// as we need to determine which layers belong to the requested display.
for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (!layer->belongsToDisplay(layerStack, false)) {
+ if (!layer->belongsToDisplay(layerStack)) {
continue;
}
// relative layers are traversed in Layer::traverseInZOrder
layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->belongsToDisplay(layerStack, false)) {
+ if (layer->getPrimaryDisplayOnly()) {
return;
}
if (!layer->isVisible()) {
return;
}
+ if (uid != CaptureArgs::UNSET_UID && layer->getOwnerUid() != uid) {
+ return;
+ }
visitor(layer);
});
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6c00931..24837b8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -58,7 +58,7 @@
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
-#include "Scheduler/VSyncModulator.h"
+#include "Scheduler/VsyncModulator.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceTracing.h"
#include "TracedOrdinal.h"
@@ -549,8 +549,6 @@
void signalLayerUpdate();
void signalRefresh();
- using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
-
struct ActiveConfigInfo {
HwcConfigIndexType configId;
Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
@@ -604,7 +602,13 @@
void updateInputWindowInfo();
void commitInputWindowCommands() REQUIRES(mStateLock);
void updateCursorAsync();
+
+ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
+ using VsyncModulator = scheduler::VsyncModulator;
+
void initScheduler(PhysicalDisplayId primaryDisplayId);
+ void updatePhaseConfiguration(const RefreshRate&);
+ void setVsyncConfig(const VsyncModulator::VsyncConfig&);
/* handlePageFlip - latch a new buffer if available and compute the dirty
* region. Returns whether a new buffer has been latched, i.e., whether it
@@ -615,6 +619,8 @@
/* ------------------------------------------------------------------------
* Transactions
*/
+ using TransactionSchedule = scheduler::TransactionSchedule;
+
void applyTransactionState(const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
@@ -622,7 +628,8 @@
const client_cache_t& uncacheBuffer, const int64_t postTime,
bool privileged, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
- bool isMainThread = false) REQUIRES(mStateLock);
+ int originPID, int originUID, bool isMainThread = false)
+ REQUIRES(mStateLock);
// Returns true if at least one transaction was flushed
bool flushTransactionQueues();
// Returns true if there is at least one transaction that needs to be flushed
@@ -637,7 +644,7 @@
// but there is no need to try and wake up immediately to do it. Rather we rely on
// onFrameAvailable or another layer update to wake us up.
void setTraversalNeeded();
- uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
+ uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule);
void commitTransaction() REQUIRES(mStateLock);
void commitOffscreenLayers();
bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
@@ -719,19 +726,18 @@
status_t renderScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
- bool forSystem, int* outSyncFd, bool regionSampling,
- ScreenCaptureResults& captureResults);
+ const sp<GraphicBuffer>& buffer, bool forSystem, int* outSyncFd,
+ bool regionSampling, ScreenCaptureResults& captureResults);
status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
- ui::PixelFormat, bool useIdentityTransform,
- ScreenCaptureResults& captureResults);
+ ui::PixelFormat, ScreenCaptureResults& captureResults);
status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, const sp<GraphicBuffer>&,
- bool useIdentityTransform, bool regionSampling,
- ScreenCaptureResults& captureResults);
+ bool regionSampling, ScreenCaptureResults& captureResults);
sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
- void traverseLayersInLayerStack(ui::LayerStack, const LayerVector::Visitor&);
+ // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
+ // matching ownerUid
+ void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, const LayerVector::Visitor&);
sp<StartPropertySetThread> mStartPropertySetThread;
@@ -843,14 +849,13 @@
void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
/* ------------------------------------------------------------------------
- * VSync
+ * VSYNC
*/
nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
// Sets the refresh rate by switching active configs, if they are available for
// the desired refresh rate.
- void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent event)
- REQUIRES(mStateLock);
+ void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent) REQUIRES(mStateLock);
bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
@@ -1130,7 +1135,8 @@
const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
int64_t postTime, bool privileged, bool hasListenerCallbacks,
- std::vector<ListenerCallbacks> listenerCallbacks)
+ std::vector<ListenerCallbacks> listenerCallbacks, int originPID,
+ int originUID)
: states(composerStates),
displays(displayStates),
flags(transactionFlags),
@@ -1139,7 +1145,9 @@
postTime(postTime),
privileged(privileged),
hasListenerCallbacks(hasListenerCallbacks),
- listenerCallbacks(listenerCallbacks) {}
+ listenerCallbacks(listenerCallbacks),
+ originPID(originPID),
+ originUID(originUID) {}
Vector<ComposerState> states;
Vector<DisplayState> displays;
@@ -1150,6 +1158,8 @@
bool privileged;
bool hasListenerCallbacks;
std::vector<ListenerCallbacks> listenerCallbacks;
+ int originPID;
+ int originUID;
};
std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
@@ -1204,10 +1214,10 @@
scheduler::ConnectionHandle mSfConnectionHandle;
// Stores phase offsets configured per refresh rate.
- std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
+ std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
- // Optional to defer construction until scheduler connections are created.
- std::optional<scheduler::VSyncModulator> mVSyncModulator;
+ // Optional to defer construction until PhaseConfiguration is created.
+ std::optional<scheduler::VsyncModulator> mVsyncModulator;
std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
@@ -1215,6 +1225,14 @@
std::atomic<nsecs_t> mExpectedPresentTime = 0;
hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
+ template <typename... Args,
+ typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
+ void modulateVsync(Handler handler, Args... args) {
+ if (const auto config = (*mVsyncModulator.*handler)(args...)) {
+ setVsyncConfig(*config);
+ }
+ }
+
/* ------------------------------------------------------------------------
* Generic Layer Metadata
*/
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 025d1a4..54ce60e 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -39,8 +39,8 @@
#include "DisplayHardware/ComposerHal.h"
#include "Scheduler/DispSync.h"
#include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
+#include "Scheduler/VsyncConfiguration.h"
namespace android::surfaceflinger {
@@ -58,10 +58,10 @@
return std::make_unique<android::impl::MessageQueue>();
}
-std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration(
+std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
const scheduler::RefreshRateConfigs& refreshRateConfigs) {
if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
- return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs);
+ return std::make_unique<scheduler::impl::WorkDuration>(refreshRateConfigs);
} else {
return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
}
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 1757fa8..04af8b0 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -29,7 +29,7 @@
std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) override;
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
std::unique_ptr<MessageQueue> createMessageQueue() override;
- std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
const scheduler::RefreshRateConfigs&) override;
std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 1d710c4..784a77b 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -55,7 +55,7 @@
} // namespace compositionengine
namespace scheduler {
-class PhaseConfiguration;
+class VsyncConfiguration;
class RefreshRateConfigs;
} // namespace scheduler
@@ -70,7 +70,7 @@
virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) = 0;
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
- virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
const scheduler::RefreshRateConfigs&) = 0;
virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) = 0;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 80102bd..c15d0df 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -143,7 +143,7 @@
addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
- display.viewport, display.frame);
+ display.layerStackSpaceRect, display.orientedDisplaySpaceRect);
}
status_t SurfaceInterceptor::writeProtoFileLocked() {
@@ -221,6 +221,13 @@
protoRect->set_bottom(rect.bottom);
}
+void SurfaceInterceptor::setTransactionOriginLocked(Transaction* transaction, int32_t pid,
+ int32_t uid) {
+ Origin* origin(transaction->mutable_origin());
+ origin->set_pid(pid);
+ origin->set_uid(uid);
+}
+
void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
float x, float y)
{
@@ -483,18 +490,20 @@
}
if (state.what & DisplayState::eDisplayProjectionChanged) {
addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation),
- state.viewport, state.frame);
+ state.layerStackSpaceRect, state.orientedDisplaySpaceRect);
}
}
-void SurfaceInterceptor::addTransactionLocked(Increment* increment,
- const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags)
-{
+void SurfaceInterceptor::addTransactionLocked(
+ Increment* increment, const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags, int originPID,
+ int originUID) {
Transaction* transaction(increment->mutable_transaction());
transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
+ setTransactionOriginLocked(transaction, originPID, originUID);
+
for (const auto& compState: stateUpdates) {
addSurfaceChangesLocked(transaction, compState.state);
}
@@ -613,17 +622,17 @@
powerModeUpdate->set_mode(mode);
}
-void SurfaceInterceptor::saveTransaction(const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags)
-{
+void SurfaceInterceptor::saveTransaction(
+ const Vector<ComposerState>& stateUpdates,
+ const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPID, int originUID) {
if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
return;
}
ATRACE_CALL();
std::lock_guard<std::mutex> protoGuard(mTraceMutex);
addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
- flags);
+ flags, originPID, originUID);
}
void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 896bdcc..1798b5a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -62,7 +62,8 @@
virtual void saveTransaction(
const Vector<ComposerState>& stateUpdates,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags) = 0;
+ const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPID,
+ int originUID) = 0;
// Intercept surface data
virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
@@ -97,7 +98,8 @@
// Intercept display and surface transactions
void saveTransaction(const Vector<ComposerState>& stateUpdates,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags) override;
+ const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPID,
+ int originUID) override;
// Intercept surface data
void saveSurfaceCreation(const sp<const Layer>& layer) override;
@@ -160,8 +162,9 @@
int32_t overrideScalingMode);
void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
+ const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+ const Vector<DisplayState>& changedDisplays,
+ uint32_t transactionFlags, int originPID, int originUID);
void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached);
@@ -182,6 +185,8 @@
void addDisplayChangesLocked(Transaction* transaction,
const DisplayState& state, int32_t sequenceId);
+ // Add transaction origin to trace
+ void setTransactionOriginLocked(Transaction* transaction, int32_t pid, int32_t uid);
bool mEnabled {false};
std::string mOutputFileName {DEFAULT_FILENAME};
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index 8fce0c9..aef670d 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -115,6 +115,7 @@
}
layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop());
layer.shadowRadius = layerProto.shadow_radius();
+ layer.ownerUid = layerProto.owner_uid();
return layer;
}
@@ -276,7 +277,7 @@
std::string LayerProtoParser::Layer::to_string() const {
std::string result;
- StringAppendF(&result, "+ %s (%s)\n", type.c_str(), name.c_str());
+ StringAppendF(&result, "+ %s (%s) uid=%d\n", type.c_str(), name.c_str(), ownerUid);
result.append(transparentRegion.to_string("TransparentRegion").c_str());
result.append(visibleRegion.to_string("VisibleRegion").c_str());
result.append(damageRegion.to_string("SurfaceDamageRegion").c_str());
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 52b9165..c48354f 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -114,6 +114,7 @@
LayerMetadata metadata;
LayerProtoParser::FloatRect cornerRadiusCrop;
float shadowRadius;
+ uid_t ownerUid;
std::string to_string() const;
};
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 8458d54..41d6d08 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -123,6 +123,8 @@
bool is_relative_of = 51;
// Layer's background blur radius in pixels.
int32 background_blur_radius = 52;
+
+ uint32 owner_uid = 53;
}
message PositionProto {
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index 8a49e77..785c2c3 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -34,7 +34,6 @@
args.width = 10;
args.height = 20;
args.useIdentityTransform = true;
- args.rotation = ui::ROTATION_90;
Parcel p;
args.write(p);
@@ -51,7 +50,6 @@
ASSERT_EQ(args.width, args2.width);
ASSERT_EQ(args.height, args2.height);
ASSERT_EQ(args.useIdentityTransform, args2.useIdentityTransform);
- ASSERT_EQ(args.rotation, args2.rotation);
}
TEST(LayerStateTest, ParcellingLayerCaptureArgs) {
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index 0ef4150..8d715e1 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -18,7 +18,6 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-#include <private/android_filesystem_config.h>
#include <thread>
#include "LayerTransactionTest.h"
@@ -26,40 +25,6 @@
using android::hardware::graphics::common::V1_1::BufferUsage;
-TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
- Transaction()
- .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
- .apply(true);
- ASSERT_EQ(PERMISSION_DENIED, composer->captureDisplay(mCaptureArgs, mCaptureResults));
-
- UIDFaker f(AID_SYSTEM);
-
- // By default the system can capture screenshots with secure layers but they
- // will be blacked out
- ASSERT_EQ(NO_ERROR, composer->captureDisplay(mCaptureArgs, mCaptureResults));
-
- {
- SCOPED_TRACE("as system");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
- }
-
- // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
- // to receive them...we are expected to take care with the results.
- DisplayCaptureArgs args;
- args.displayToken = mDisplay;
- args.captureSecureLayers = true;
- ASSERT_EQ(NO_ERROR, composer->captureDisplay(args, mCaptureResults));
- ASSERT_EQ(true, mCaptureResults.capturedSecureLayers);
- ScreenCapture sc(mCaptureResults.buffer);
- sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index 2ec4ea4..e3b9489 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -61,8 +61,10 @@
std::unique_ptr<ScreenCapture> screenshot;
// only layerB is in this range
- sp<IBinder> parentHandle = parent->getHandle();
- ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = parent->getHandle();
+ captureArgs.sourceCrop = {0, 0, 32, 32};
+ ScreenCapture::captureLayers(&screenshot, captureArgs);
screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
}
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 06e8761..db0c56f 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -90,7 +90,7 @@
};
TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
- createDisplay(mMainDisplayState.viewport, 1 /* layerStack */);
+ createDisplay(mMainDisplayState.layerStackSpaceRect, 1 /* layerStack */);
createColorLayer(1 /* layerStack */);
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
@@ -111,9 +111,9 @@
// Create a display and set its layer stack to the main display's layer stack so
// the contents of the main display are mirrored on to the virtual display.
- // Assumption here is that the new mirrored display has the same viewport as the
+ // Assumption here is that the new mirrored display has the same layer stack rect as the
// primary display that it is mirroring.
- createDisplay(mMainDisplayState.viewport, 0 /* layerStack */);
+ createDisplay(mMainDisplayState.layerStackSpaceRect, 0 /* layerStack */);
createColorLayer(0 /* layerStack */);
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 2a250ff..690f758 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -18,6 +18,8 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#include <private/android_filesystem_config.h>
+
#include "LayerTransactionTest.h"
namespace android {
@@ -72,17 +74,52 @@
std::unique_ptr<ScreenCapture> mCapture;
};
+TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32,
+ ISurfaceComposerClient::eSecure |
+ ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
+
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ ASSERT_EQ(PERMISSION_DENIED, composer->captureDisplay(mCaptureArgs, mCaptureResults));
+
+ UIDFaker f(AID_SYSTEM);
+
+ // By default the system can capture screenshots with secure layers but they
+ // will be blacked out
+ ASSERT_EQ(NO_ERROR, composer->captureDisplay(mCaptureArgs, mCaptureResults));
+
+ {
+ SCOPED_TRACE("as system");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
+ // to receive them...we are expected to take care with the results.
+ DisplayCaptureArgs args;
+ args.displayToken = mDisplay;
+ args.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, composer->captureDisplay(args, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer);
+ sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
- auto bgHandle = mBGSurfaceControl->getHandle();
- ScreenCapture::captureLayers(&mCapture, bgHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectBGColor(0, 0);
// Doesn't capture FG layer which is at 64, 64
mCapture->expectBGColor(64, 64);
}
TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
@@ -90,7 +127,9 @@
SurfaceComposerClient::Transaction().show(child).apply(true);
// Captures mFGSurfaceControl layer and its child.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectFGColor(10, 10);
mCapture->expectChildColor(0, 0);
}
@@ -105,7 +144,10 @@
SurfaceComposerClient::Transaction().show(child).apply(true);
// Captures mFGSurfaceControl's child
- ScreenCapture::captureChildLayers(&mCapture, fgHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = fgHandle;
+ captureArgs.childrenOnly = true;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->checkPixel(10, 10, 0, 0, 0);
mCapture->expectChildColor(0, 0);
}
@@ -128,7 +170,11 @@
.apply(true);
// Child2 would be visible but its excluded, so we should see child1 color instead.
- ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = fgHandle;
+ captureArgs.childrenOnly = true;
+ captureArgs.excludeHandles = {child2->getHandle()};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->checkPixel(10, 10, 0, 0, 0);
mCapture->checkPixel(0, 0, 200, 200, 200);
}
@@ -156,7 +202,11 @@
.apply(true);
// Child2 would be visible but its excluded, so we should see child1 color instead.
- ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = fgHandle;
+ captureArgs.childrenOnly = true;
+ captureArgs.excludeHandles = {child2->getHandle()};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->checkPixel(10, 10, 0, 0, 0);
mCapture->checkPixel(0, 0, 200, 200, 200);
}
@@ -169,18 +219,17 @@
SurfaceComposerClient::Transaction().show(child).apply(true);
- auto childHandle = child->getHandle();
-
// Captures child
- ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20});
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ captureArgs.sourceCrop = {0, 0, 10, 20};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
// Area outside of child's bounds is transparent.
mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
}
TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
ASSERT_NE(nullptr, child.get()) << "failed to create surface";
@@ -191,19 +240,19 @@
SurfaceComposerClient::Transaction()
.show(child)
// Set relative layer above fg layer so should be shown above when computing all layers.
- .setRelativeLayer(relative, fgHandle, 1)
+ .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
.show(relative)
.apply(true);
// Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectFGColor(10, 10);
mCapture->expectChildColor(0, 0);
}
TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
@@ -222,7 +271,9 @@
// Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
// relative value should be taken into account, placing it above child layer.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectFGColor(10, 10);
// Relative layer is showing on top of child layer
mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
@@ -232,10 +283,10 @@
sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
SurfaceComposerClient::Transaction().show(child).apply(true);
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- Rect sourceCrop(0, 0, 10, 10);
- sp<IBinder> childHandle = child->getHandle();
- ScreenCapture::captureLayers(&mCapture, childHandle, sourceCrop);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ captureArgs.sourceCrop = {0, 0, 10, 10};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED);
}
@@ -245,10 +296,9 @@
Rect layerCrop(0, 0, 10, 10);
SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<GraphicBuffer> outBuffer;
- sp<IBinder> childHandle = child->getHandle();
- ScreenCapture::captureLayers(&mCapture, childHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED);
}
@@ -289,8 +339,6 @@
}
TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
- auto fgHandle = mFGSurfaceControl->getHandle();
-
sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
@@ -306,7 +354,9 @@
.apply(true);
// Captures mFGSurfaceControl, its child, and the grandchild.
- ScreenCapture::captureLayers(&mCapture, fgHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectFGColor(10, 10);
mCapture->expectChildColor(0, 0);
mCapture->checkPixel(5, 5, 50, 50, 50);
@@ -316,12 +366,13 @@
sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
- auto childHandle = child->getHandle();
SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
// Captures only the child layer, and not the parent.
- ScreenCapture::captureLayers(&mCapture, childHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = child->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectChildColor(0, 0);
mCapture->expectChildColor(9, 9);
}
@@ -342,10 +393,10 @@
.show(grandchild)
.apply(true);
- auto grandchildHandle = grandchild->getHandle();
-
// Captures only the grandchild.
- ScreenCapture::captureLayers(&mCapture, grandchildHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = grandchild->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->checkPixel(0, 0, 50, 50, 50);
mCapture->checkPixel(4, 4, 50, 50, 50);
}
@@ -364,18 +415,18 @@
.show(blueLayer)
.apply(true);
- auto redLayerHandle = redLayer->getHandle();
-
// Capturing full screen should have both red and blue are visible.
- ScreenCapture::captureLayers(&mCapture, redLayerHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = redLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
// red area below the blue area
mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
// red area to the right of the blue area
mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
- const Rect crop = Rect(0, 0, 30, 30);
- ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop);
+ captureArgs.sourceCrop = {0, 0, 30, 30};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
// Capturing the cropped screen, cropping out the shown red area, should leave only the blue
// area visible.
mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
@@ -396,17 +447,18 @@
.show(blueLayer)
.apply(true);
- auto redLayerHandle = redLayer->getHandle();
-
// Capturing full screen should have both red and blue are visible.
- ScreenCapture::captureLayers(&mCapture, redLayerHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = redLayer->getHandle();
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
// red area below the blue area
mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
// red area to the right of the blue area
mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
- ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5);
+ captureArgs.frameScale = 0.5f;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
// Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
// red area below the blue area
@@ -435,6 +487,171 @@
ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(args, captureResults));
}
+TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+ sp<SurfaceControl> secureLayer =
+ createLayer(String8("Secure surface"), 30, 30,
+ ISurfaceComposerClient::eSecure |
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ redLayer.get());
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(secureLayer, Color::BLUE, 30, 30));
+
+ auto redLayerHandle = redLayer->getHandle();
+ Transaction()
+ .show(redLayer)
+ .show(secureLayer)
+ .setLayerStack(redLayer, 0)
+ .setLayer(redLayer, INT32_MAX)
+ .apply();
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+
+ LayerCaptureArgs args;
+ args.layerHandle = redLayerHandle;
+ args.childrenOnly = false;
+ ScreenCaptureResults captureResults;
+
+ // Call from outside system with secure layers will result in permission denied
+ ASSERT_EQ(PERMISSION_DENIED, sf->captureLayers(args, captureResults));
+
+ UIDFaker f(AID_SYSTEM);
+
+ // From system request, only red layer will be screenshot since the blue layer is secure.
+ // Black will be present where the secure layer is.
+ ScreenCapture::captureLayers(&mCapture, args);
+ mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLACK);
+ mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
+
+ // Passing flag secure so the blue layer should be screenshot too.
+ args.captureSecureLayers = true;
+ ScreenCapture::captureLayers(&mCapture, args);
+ mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLUE);
+ mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayWithUid) {
+ uid_t fakeUid = 12345;
+
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+
+ // Make sure red layer with the background layer is screenshot.
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+
+ // From non system uid, can't request screenshot without a specified uid.
+ UIDFaker f(fakeUid);
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ ASSERT_EQ(PERMISSION_DENIED, composer->captureDisplay(captureArgs, mCaptureResults));
+
+ // Make screenshot request with current uid set. No layers were created with the current
+ // uid so screenshot will be black.
+ captureArgs.uid = fakeUid;
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+
+ sp<SurfaceControl> layerWithFakeUid;
+ // Create a new layer with the current uid
+ ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
+ createLayer("new test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
+ Transaction()
+ .show(layerWithFakeUid)
+ .setLayer(layerWithFakeUid, INT32_MAX)
+ .setPosition(layerWithFakeUid, 128, 128)
+ .apply();
+
+ // Screenshot from the fakeUid caller with the uid requested allows the layer
+ // with that uid to be screenshotted. Everything else is black
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), Color::BLACK);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithUid) {
+ uid_t fakeUid = 12345;
+
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mBGSurfaceControl->getHandle();
+ captureArgs.childrenOnly = false;
+
+ // Make sure red layer with the background layer is screenshot.
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+
+ // From non system uid, can't request screenshot without a specified uid.
+ std::unique_ptr<UIDFaker> uidFaker = std::make_unique<UIDFaker>(fakeUid);
+
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ ASSERT_EQ(PERMISSION_DENIED, composer->captureLayers(captureArgs, mCaptureResults));
+
+ // Make screenshot request with current uid set. No layers were created with the current
+ // uid so screenshot will be black.
+ captureArgs.uid = fakeUid;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::TRANSPARENT);
+ mCapture->expectBorder(Rect(0, 0, 32, 32), Color::TRANSPARENT);
+
+ sp<SurfaceControl> layerWithFakeUid;
+ // Create a new layer with the current uid
+ ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
+ createLayer("new test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
+ Transaction()
+ .show(layerWithFakeUid)
+ .setLayer(layerWithFakeUid, INT32_MAX)
+ .setPosition(layerWithFakeUid, 128, 128)
+ // reparent a layer that was created with a different uid to the new layer.
+ .reparent(layer, layerWithFakeUid->getHandle())
+ .apply();
+
+ // Screenshot from the fakeUid caller with the uid requested allows the layer
+ // with that uid to be screenshotted. The child layer is skipped since it was created
+ // from a different uid.
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT);
+
+ // Clear fake calling uid so it's back to system.
+ uidFaker = nullptr;
+ // Screenshot from the test caller with the uid requested allows the layer
+ // with that uid to be screenshotted. The child layer is skipped since it was created
+ // from a different uid.
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT);
+
+ // Screenshot from the fakeUid caller with no uid requested allows everything to be screenshot.
+ captureArgs.uid = -1;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectColor(Rect(128, 128, 160, 160), Color::RED);
+ mCapture->expectBorder(Rect(128, 128, 160, 160), {63, 63, 195, 255});
+}
+
// In the following tests we verify successful skipping of a parent layer,
// so we use the same verification logic and only change how we mutate
// the parent layer to verify that various properties are ignored.
@@ -456,8 +673,10 @@
// Verify child layer does not inherit any of the properties of its
// parent when its screenshot is captured.
- auto fgHandle = mFGSurfaceControl->getHandle();
- ScreenCapture::captureChildLayers(&mCapture, fgHandle);
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mFGSurfaceControl->getHandle();
+ captureArgs.childrenOnly = true;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->checkPixel(10, 10, 0, 0, 0);
mCapture->expectChildColor(0, 0);
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index f0af363..01badf4 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -65,7 +65,7 @@
t.setDisplaySurface(vDisplay, producer);
t.setDisplayLayerStack(vDisplay, 0);
t.setDisplayProjection(vDisplay, displayState.orientation,
- Rect(displayState.viewport), Rect(resolution));
+ Rect(displayState.layerStackSpaceRect), Rect(resolution));
t.apply();
SurfaceComposerClient::Transaction().apply(true);
BufferItem item;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index c46c914..7cc9cef 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -46,7 +46,6 @@
"LayerHistoryTest.cpp",
"LayerHistoryTestV2.cpp",
"LayerMetadataTest.cpp",
- "PhaseOffsetsTest.cpp",
"PromiseTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
@@ -61,9 +60,10 @@
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
- "VSyncModulatorTest.cpp",
+ "VsyncModulatorTest.cpp",
"VSyncPredictorTest.cpp",
"VSyncReactorTest.cpp",
+ "VsyncConfigurationTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 48c4e18..4843f05 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -233,7 +233,6 @@
LayerCase::setupForScreenCapture(this);
const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
- constexpr bool useIdentityTransform = true;
constexpr bool forSystem = true;
constexpr bool regionSampling = false;
@@ -241,7 +240,8 @@
ui::Dataspace::V0_SRGB, ui::Transform::ROT_0);
auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
- return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(), visitor);
+ return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(),
+ CaptureArgs::UNSET_UID, visitor);
};
// TODO: Eliminate expensive/real allocation if possible.
@@ -253,7 +253,7 @@
int fd = -1;
status_t result =
mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer.get(),
- useIdentityTransform, forSystem, &fd, regionSampling);
+ forSystem, &fd, regionSampling);
if (fd >= 0) {
close(fd);
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 6086a05..7fade0d 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -1523,9 +1523,9 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -1535,10 +1535,12 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- // For 90, the frame and viewport have the hardware display size width and height swapped
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+ // size width and height swapped
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)),
+ compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -1548,8 +1550,8 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -1559,10 +1561,12 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
- // For 270, the frame and viewport have the hardware display size width and height swapped
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+ // size width and height swapped
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)),
+ compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -2484,11 +2488,11 @@
EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
}
-TEST_F(HandleTransactionLockedTest, processesDisplayViewportChanges) {
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
using Case = NonHwcVirtualDisplayCase;
- const Rect oldViewport(0, 0, 0, 0);
- const Rect newViewport(0, 0, 123, 456);
+ const Rect oldLayerStackRect(0, 0, 0, 0);
+ const Rect newLayerStackRect(0, 0, 123, 456);
// --------------------------------------------------------------------
// Preconditions
@@ -2497,9 +2501,9 @@
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.inject();
- // There is a change to the viewport state
- display.mutableDrawingDisplayState().viewport = oldViewport;
- display.mutableCurrentDisplayState().viewport = newViewport;
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect;
// --------------------------------------------------------------------
// Invocation
@@ -2509,7 +2513,7 @@
// --------------------------------------------------------------------
// Postconditions
- EXPECT_EQ(newViewport, display.mutableDisplayDevice()->getViewport());
+ EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
}
TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
@@ -2525,9 +2529,9 @@
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.inject();
- // There is a change to the viewport state
- display.mutableDrawingDisplayState().frame = oldFrame;
- display.mutableCurrentDisplayState().frame = newFrame;
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame;
// --------------------------------------------------------------------
// Invocation
@@ -2537,7 +2541,7 @@
// --------------------------------------------------------------------
// Postconditions
- EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getFrame());
+ EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
}
TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
@@ -2568,7 +2572,7 @@
EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
display.inject();
- // There is a change to the viewport state
+ // There is a change to the layerStackSpaceRect state
display.mutableDrawingDisplayState().width = oldWidth;
display.mutableDrawingDisplayState().height = oldHeight;
display.mutableCurrentDisplayState().width = newWidth;
@@ -2613,7 +2617,7 @@
EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
display.inject();
- // There is a change to the viewport state
+ // There is a change to the layerStackSpaceRect state
display.mutableDrawingDisplayState().width = oldWidth;
display.mutableDrawingDisplayState().height = oldHeight;
display.mutableCurrentDisplayState().width = oldWidth;
@@ -2834,8 +2838,8 @@
TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
using Case = SimplePrimaryDisplayCase;
constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
- const Rect initialFrame = {1, 2, 3, 4};
- const Rect initialViewport = {5, 6, 7, 8};
+ const Rect initialOrientedDisplayRect = {1, 2, 3, 4};
+ const Rect initialLayerStackRect = {5, 6, 7, 8};
// --------------------------------------------------------------------
// Preconditions
@@ -2846,16 +2850,16 @@
// The current display state projection state is all set
display.mutableCurrentDisplayState().orientation = initialOrientation;
- display.mutableCurrentDisplayState().frame = initialFrame;
- display.mutableCurrentDisplayState().viewport = initialViewport;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
// The incoming request sets the same projection state
DisplayState state;
state.what = DisplayState::eDisplayProjectionChanged;
state.token = display.token();
state.orientation = initialOrientation;
- state.frame = initialFrame;
- state.viewport = initialViewport;
+ state.orientedDisplaySpaceRect = initialOrientedDisplayRect;
+ state.layerStackSpaceRect = initialLayerStackRect;
// --------------------------------------------------------------------
// Invocation
@@ -2871,8 +2875,9 @@
// The current display state is unchanged
EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
- EXPECT_EQ(initialFrame, display.getCurrentDisplayState().frame);
- EXPECT_EQ(initialViewport, display.getCurrentDisplayState().viewport);
+ EXPECT_EQ(initialOrientedDisplayRect,
+ display.getCurrentDisplayState().orientedDisplaySpaceRect);
+ EXPECT_EQ(initialLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
}
TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
@@ -2913,8 +2918,8 @@
TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
using Case = SimplePrimaryDisplayCase;
- const Rect initialFrame = {0, 0, 0, 0};
- const Rect desiredFrame = {5, 6, 7, 8};
+ const Rect initialOrientedDisplayRect = {0, 0, 0, 0};
+ const Rect desiredOrientedDisplayRect = {5, 6, 7, 8};
// --------------------------------------------------------------------
// Preconditions
@@ -2923,14 +2928,14 @@
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.inject();
- // The current display state does not have a frame
- display.mutableCurrentDisplayState().frame = initialFrame;
+ // The current display state does not have a orientedDisplaySpaceRect
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
- // The incoming request sets a frame
+ // The incoming request sets a orientedDisplaySpaceRect
DisplayState state;
state.what = DisplayState::eDisplayProjectionChanged;
state.token = display.token();
- state.frame = desiredFrame;
+ state.orientedDisplaySpaceRect = desiredOrientedDisplayRect;
// --------------------------------------------------------------------
// Invocation
@@ -2944,13 +2949,14 @@
EXPECT_EQ(eDisplayTransactionNeeded, flags);
// The current display state has the new value.
- EXPECT_EQ(desiredFrame, display.getCurrentDisplayState().frame);
+ EXPECT_EQ(desiredOrientedDisplayRect,
+ display.getCurrentDisplayState().orientedDisplaySpaceRect);
}
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfViewportChanged) {
+TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackRectChanged) {
using Case = SimplePrimaryDisplayCase;
- const Rect initialViewport = {0, 0, 0, 0};
- const Rect desiredViewport = {5, 6, 7, 8};
+ const Rect initialLayerStackRect = {0, 0, 0, 0};
+ const Rect desiredLayerStackRect = {5, 6, 7, 8};
// --------------------------------------------------------------------
// Preconditions
@@ -2959,14 +2965,14 @@
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.inject();
- // The current display state does not have a viewport
- display.mutableCurrentDisplayState().viewport = initialViewport;
+ // The current display state does not have a layerStackSpaceRect
+ display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
- // The incoming request sets a viewport
+ // The incoming request sets a layerStackSpaceRect
DisplayState state;
state.what = DisplayState::eDisplayProjectionChanged;
state.token = display.token();
- state.viewport = desiredViewport;
+ state.layerStackSpaceRect = desiredLayerStackRect;
// --------------------------------------------------------------------
// Invocation
@@ -2980,7 +2986,7 @@
EXPECT_EQ(eDisplayTransactionNeeded, flags);
// The current display state has the new value.
- EXPECT_EQ(desiredViewport, display.getCurrentDisplayState().viewport);
+ EXPECT_EQ(desiredLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
}
TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
@@ -3142,11 +3148,11 @@
// The orientation state should be set to zero
EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
- // The frame state should be set to INVALID
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.frame);
+ // The orientedDisplaySpaceRect state should be set to INVALID
+ EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
- // The viewport state should be set to INVALID
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.viewport);
+ // The layerStackSpaceRect state should be set to INVALID
+ EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
// The width and height should both be zero
EXPECT_EQ(0u, primaryDisplayState.width);
@@ -3157,7 +3163,7 @@
auto displayDevice = primaryDisplay.mutableDisplayDevice();
EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
- // The display refresh period should be set in the frame tracker.
+ // The display refresh period should be set in the orientedDisplaySpaceRect tracker.
FrameStats stats;
mFlinger.getAnimFrameTracker().getStats(&stats);
EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano);
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
deleted file mode 100644
index b50ddf5..0000000
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "Scheduler/PhaseOffsets.h"
-
-namespace android::scheduler {
-
-struct FakePhaseOffsets : PhaseConfiguration {
- static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
-
- Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
-
- Offsets getCurrentOffsets() const override {
- return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
- }
-
- void setRefreshRateFps(float) override {}
- void dump(std::string&) const override {}
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
new file mode 100644
index 0000000..4cd1e0a
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VsyncConfiguration.h"
+
+namespace android::scheduler {
+
+struct FakePhaseOffsets : VsyncConfiguration {
+ static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+ static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0);
+
+ VsyncConfigSet getConfigsForRefreshRate(float) const override { return getCurrentConfigs(); }
+
+ VsyncConfigSet getCurrentConfigs() const override {
+ return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+ FAKE_DURATION_OFFSET_NS}};
+ }
+
+ void setRefreshRateFps(float) override {}
+ void dump(std::string&) const override {}
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
deleted file mode 100644
index 0b74682..0000000
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#undef LOG_TAG
-#define LOG_TAG "SchedulerUnittests"
-
-#include <gmock/gmock.h>
-#include <log/log.h>
-#include <thread>
-
-#include "Scheduler/PhaseOffsets.h"
-
-using namespace testing;
-
-namespace android {
-namespace scheduler {
-
-class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
-public:
- TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
- nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
- nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
- : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
- sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
- appEarlyGlDuration) {}
-};
-
-class PhaseDurationTest : public testing::Test {
-protected:
- PhaseDurationTest()
- : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
- 21'000'000) {}
-
- ~PhaseDurationTest() = default;
-
- TestablePhaseOffsetsAsDurations mPhaseDurations;
-};
-
-namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
- mPhaseDurations.setRefreshRateFps(60.0f);
- auto currentOffsets = mPhaseDurations.getCurrentOffsets();
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
-
- EXPECT_EQ(currentOffsets, offsets);
- EXPECT_EQ(offsets.late.sf, 6'166'667);
-
- EXPECT_EQ(offsets.late.app, 2'333'334);
-
- EXPECT_EQ(offsets.early.sf, 666'667);
-
- EXPECT_EQ(offsets.early.app, 833'334);
-
- EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
-
- EXPECT_EQ(offsets.earlyGl.app, 15'500'001);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
- mPhaseDurations.setRefreshRateFps(90.0f);
- auto currentOffsets = mPhaseDurations.getCurrentOffsets();
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
-
- EXPECT_EQ(currentOffsets, offsets);
- EXPECT_EQ(offsets.late.sf, 611'111);
-
- EXPECT_EQ(offsets.late.app, 2'333'333);
-
- EXPECT_EQ(offsets.early.sf, -4'888'889);
-
- EXPECT_EQ(offsets.early.app, 833'333);
-
- EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
-
- EXPECT_EQ(offsets.earlyGl.app, 9'944'444);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
- TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
-
- auto validateOffsets = [](auto& offsets) {
- EXPECT_EQ(offsets.late.sf, 1'000'000);
-
- EXPECT_EQ(offsets.late.app, 1'000'000);
-
- EXPECT_EQ(offsets.early.sf, 1'000'000);
-
- EXPECT_EQ(offsets.early.app, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
- };
-
- phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
- auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
- auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
- EXPECT_EQ(currentOffsets, offsets);
- validateOffsets(offsets);
-
- phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
- currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
- offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
- EXPECT_EQ(currentOffsets, offsets);
- validateOffsets(offsets);
-}
-
-TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
- auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
-
- EXPECT_EQ(offsets.late.sf, 57'527'208);
-
- EXPECT_EQ(offsets.late.app, 37'027'208);
-
- EXPECT_EQ(offsets.early.sf, 52'027'208);
-
- EXPECT_EQ(offsets.early.app, 35'527'208);
-
- EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
-
- EXPECT_EQ(offsets.earlyGl.app, 33'527'208);
-}
-
-} // namespace
-
-class TestablePhaseOffsets : public impl::PhaseOffsets {
-public:
- TestablePhaseOffsets()
- : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 1'000'000, 1'000'000, {}, {}, {}, {},
- 10'000'000) {}
-};
-
-class PhaseOffsetsTest : public testing::Test {
-protected:
- PhaseOffsetsTest() = default;
- ~PhaseOffsetsTest() = default;
-
- TestablePhaseOffsets mPhaseOffsets;
-};
-
-namespace {
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
-
- EXPECT_EQ(offsets.late.sf, 1'000'000);
-
- EXPECT_EQ(offsets.late.app, 1'000'000);
-
- EXPECT_EQ(offsets.early.sf, 1'000'000);
-
- EXPECT_EQ(offsets.early.app, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
-
- EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
-}
-
-} // namespace
-} // namespace scheduler
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3941d42..a1049d8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -29,7 +29,7 @@
#include "ContainerLayer.h"
#include "DisplayDevice.h"
#include "EffectLayer.h"
-#include "FakePhaseOffsets.h"
+#include "FakeVsyncConfiguration.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "Scheduler/MessageQueue.h"
@@ -77,7 +77,7 @@
return std::make_unique<android::impl::MessageQueue>();
}
- std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
@@ -216,8 +216,9 @@
scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
/*currentConfig=*/HwcConfigIndexType(0),
/*powerMode=*/hal::PowerMode::OFF);
- mFlinger->mPhaseConfiguration =
- mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
+ mFlinger->mVsyncConfiguration =
+ mFactory.createVsyncConfiguration(*mFlinger->mRefreshRateConfigs);
+ mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
constexpr bool kUseContentDetectionV2 = false;
mScheduler =
@@ -227,10 +228,6 @@
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
resetScheduler(mScheduler);
-
- mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
- mFlinger->mSfConnectionHandle,
- mFlinger->mPhaseConfiguration->getCurrentOffsets());
}
void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
@@ -335,17 +332,16 @@
auto renderScreenImplLocked(const RenderArea& renderArea,
SurfaceFlinger::TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
- bool forSystem, int* outSyncFd, bool regionSampling) {
+ const sp<GraphicBuffer>& buffer, bool forSystem, int* outSyncFd,
+ bool regionSampling) {
ScreenCaptureResults captureResults;
- return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer,
- useIdentityTransform, forSystem, outSyncFd,
- regionSampling, captureResults);
+ return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer, forSystem,
+ outSyncFd, regionSampling, captureResults);
}
- auto traverseLayersInLayerStack(ui::LayerStack layerStack,
+ auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
const LayerVector::Visitor& visitor) {
- return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, visitor);
+ return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, uid, visitor);
}
auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
diff --git a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
deleted file mode 100644
index af1e84b..0000000
--- a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-#define LOG_NDEBUG 0
-
-#include "Scheduler/VSyncModulator.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-using namespace testing;
-
-namespace android::scheduler {
-
-class MockScheduler final : public IPhaseOffsetControl {
-public:
- nsecs_t getOffset(ConnectionHandle handle) { return mPhaseOffset[handle]; }
-
-private:
- void setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) override {
- mPhaseOffset[handle] = phaseOffset;
- }
-
- std::unordered_map<ConnectionHandle, nsecs_t> mPhaseOffset;
-};
-
-class VSyncModulatorTest : public testing::Test {
-protected:
- static constexpr auto MIN_EARLY_FRAME_COUNT_TRANSACTION =
- VSyncModulator::MIN_EARLY_FRAME_COUNT_TRANSACTION;
- // Add a 1ms slack to avoid strange timer race conditions.
- static constexpr auto MARGIN_FOR_TX_APPLY = VSyncModulator::MARGIN_FOR_TX_APPLY + 1ms;
-
- // Used to enumerate the different offsets we have
- enum {
- SF_LATE,
- APP_LATE,
- SF_EARLY,
- APP_EARLY,
- SF_EARLY_GL,
- APP_EARLY_GL,
- };
-
- std::unique_ptr<VSyncModulator> mVSyncModulator;
- MockScheduler mMockScheduler;
- ConnectionHandle mAppConnection{1};
- ConnectionHandle mSfConnection{2};
- VSyncModulator::OffsetsConfig mOffsets = {{SF_EARLY, APP_EARLY},
- {SF_EARLY_GL, APP_EARLY_GL},
- {SF_LATE, APP_LATE}};
-
- void SetUp() override {
- mVSyncModulator = std::make_unique<VSyncModulator>(mMockScheduler, mAppConnection,
- mSfConnection, mOffsets);
- mVSyncModulator->setPhaseOffsets(mOffsets);
-
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
- };
-
- void TearDown() override { mVSyncModulator.reset(); }
-};
-
-TEST_F(VSyncModulatorTest, Normal) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
- }
-}
-
-TEST_F(VSyncModulatorTest, EarlyEnd) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStart) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartWithEarly) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Early);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartWithMoreTransactions) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEnd) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
- std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
- mVSyncModulator->onTransactionHandled();
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
-
- for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
- }
-
- mVSyncModulator->onRefreshed(false);
- EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
- EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
-}
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
new file mode 100644
index 0000000..72ee6db
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/VsyncConfiguration.h"
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class TestableWorkDuration : public impl::WorkDuration {
+public:
+ TestableWorkDuration(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+ nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+ : impl::WorkDuration({60.0f, 90.0f}, currentFps, sfDuration, appDuration, sfEarlyDuration,
+ appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration) {}
+};
+
+class WorkDurationTest : public testing::Test {
+protected:
+ WorkDurationTest()
+ : mWorkDuration(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
+ 21'000'000) {}
+
+ ~WorkDurationTest() = default;
+
+ TestableWorkDuration mWorkDuration;
+};
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_60Hz) {
+ mWorkDuration.setRefreshRateFps(60.0f);
+ auto currentOffsets = mWorkDuration.getCurrentConfigs();
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(60.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sfOffset, 6'166'667);
+ EXPECT_EQ(offsets.late.appOffset, 2'333'334);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 666'667);
+ EXPECT_EQ(offsets.early.appOffset, 833'334);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'166'667);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 15'500'001);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_90Hz) {
+ mWorkDuration.setRefreshRateFps(90.0f);
+ auto currentOffsets = mWorkDuration.getCurrentConfigs();
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(90.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sfOffset, 611'111);
+ EXPECT_EQ(offsets.late.appOffset, 2'333'333);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, -4'888'889);
+ EXPECT_EQ(offsets.early.appOffset, 833'333);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, -2'388'889);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 9'944'444);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_DefaultOffsets) {
+ TestableWorkDuration phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
+
+ auto validateOffsets = [](const auto& offsets, std::chrono::nanoseconds vsyncPeriod) {
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, vsyncPeriod);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, vsyncPeriod);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, vsyncPeriod - 1'000'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, vsyncPeriod);
+ };
+
+ const auto testForRefreshRate = [&](float refreshRate) {
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(refreshRate);
+ auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentConfigs();
+ auto offsets = phaseOffsetsWithDefaultValues.getConfigsForRefreshRate(refreshRate);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets,
+ std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / refreshRate)));
+ };
+
+ testForRefreshRate(90.0f);
+ testForRefreshRate(60.0f);
+}
+
+TEST_F(WorkDurationTest, getConfigsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 57'527'208);
+ EXPECT_EQ(offsets.late.appOffset, 37'027'208);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 52'027'208);
+ EXPECT_EQ(offsets.early.appOffset, 35'527'208);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 54'527'208);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 33'527'208);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns);
+}
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+ TestablePhaseOffsets(nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs,
+ std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs,
+ nsecs_t highFpsVsyncPhaseOffsetNs, nsecs_t highFpsSfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
+ nsecs_t thresholdForNextVsync)
+ : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs,
+ earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs,
+ earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
+ highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs,
+ highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs,
+ highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest() = default;
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsets mPhaseOffsets{2'000'000, 6'000'000, 7'000'000, 8'000'000, 3'000'000,
+ 4'000'000, 2'000'000, 1'000'000, 2'000'000, 3'000'000,
+ 3'000'000, 4'000'000, 10'000'000};
+};
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 62'027'208ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 72'027'208ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 7'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 61'027'208ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 72'027'208ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 60'027'208ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 72'027'208ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_60Hz) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(60.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'666'667ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 20'666'667ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 7'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 9'666'667ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 20'666'667ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'666'667ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 20'666'667ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_90Hz) {
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(90.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 2'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 3'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 9'111'111ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'111'111ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_60Hz) {
+ TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000,
+ 1'000'000, {}, {}, {}, {}, 10'000'000};
+ auto offsets = phaseOffsets.getConfigsForRefreshRate(60.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 16'666'667ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'666'667ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 15'666'667ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 16'666'667ns);
+}
+
+TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_90Hz) {
+ TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000,
+ 1'000'000, {}, {}, {}, {}, 10'000'000};
+ auto offsets = phaseOffsets.getConfigsForRefreshRate(90.0f);
+
+ EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.late.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.early.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 2'000'000);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 10'111'111ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
new file mode 100644
index 0000000..106da81
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Scheduler/VsyncModulator.h"
+
+namespace android::scheduler {
+
+class VsyncModulatorTest : public testing::Test {
+ enum {
+ SF_OFFSET_LATE,
+ APP_OFFSET_LATE,
+ SF_DURATION_LATE,
+ APP_DURATION_LATE,
+ SF_OFFSET_EARLY,
+ APP_OFFSET_EARLY,
+ SF_DURATION_EARLY,
+ APP_DURATION_EARLY,
+ SF_OFFSET_EARLY_GPU,
+ APP_OFFSET_EARLY_GPU,
+ SF_DURATION_EARLY_GPU,
+ APP_DURATION_EARLY_GPU,
+ };
+
+ static VsyncModulator::TimePoint Now() {
+ static VsyncModulator::TimePoint now;
+ return now += VsyncModulator::MIN_EARLY_TRANSACTION_TIME;
+ }
+
+protected:
+ static constexpr auto MIN_EARLY_TRANSACTION_FRAMES =
+ VsyncModulator::MIN_EARLY_TRANSACTION_FRAMES;
+
+ using Schedule = scheduler::TransactionSchedule;
+ using nanos = std::chrono::nanoseconds;
+ const VsyncModulator::VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
+ nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
+ const VsyncModulator::VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
+ nanos(SF_DURATION_EARLY),
+ nanos(APP_DURATION_EARLY)};
+ const VsyncModulator::VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE,
+ nanos(SF_DURATION_EARLY_GPU),
+ nanos(APP_DURATION_EARLY_GPU)};
+
+ const VsyncModulator::VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate};
+ VsyncModulator mVsyncModulator{mOffsets, Now};
+
+ void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator.setVsyncConfigSet(mOffsets)); }
+};
+
+#define CHECK_COMMIT(result, configs) \
+ EXPECT_EQ(result, mVsyncModulator.onTransactionCommit()); \
+ EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig());
+
+#define CHECK_REFRESH(N, result, configs) \
+ for (int i = 0; i < N; i++) { \
+ EXPECT_EQ(result, mVsyncModulator.onDisplayRefresh(false)); \
+ EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig()); \
+ }
+
+TEST_F(VsyncModulatorTest, Late) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+
+ CHECK_COMMIT(std::nullopt, kLate);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyEnd) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStart) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartWithEarly) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::Early));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartWithMoreTransactions) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+
+ for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+ CHECK_REFRESH(1, std::nullopt, kEarly);
+ }
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEnd) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(1, kEarly, kEarly);
+ CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(1, kEarly, kEarly);
+
+ for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) {
+ EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late));
+ CHECK_REFRESH(1, std::nullopt, kEarly);
+ }
+
+ EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
+
+ CHECK_COMMIT(kEarly, kEarly);
+ CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
+ CHECK_REFRESH(1, kLate, kLate);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
index 5beee1c..ccb7bb9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
@@ -33,10 +33,10 @@
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
MOCK_METHOD0(disable, void());
MOCK_METHOD0(isEnabled, bool());
- MOCK_METHOD4(saveTransaction,
+ MOCK_METHOD6(saveTransaction,
void(const Vector<ComposerState>&,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&,
- const Vector<DisplayState>&, uint32_t));
+ const Vector<DisplayState>&, uint32_t, int, int));
MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index d73506b..081d18b 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -31,66 +31,29 @@
captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
}
- static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
+ static void captureDisplay(std::unique_ptr<ScreenCapture>* sc,
+ const DisplayCaptureArgs& captureArgs) {
const auto sf = ComposerService::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, sf->captureDisplay(captureArgs, captureResults));
+ *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
+ }
+
+ static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
DisplayCaptureArgs args;
args.displayToken = displayToken;
-
- ScreenCaptureResults captureResults;
- ASSERT_EQ(NO_ERROR, sf->captureDisplay(args, captureResults));
- *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
+ captureDisplay(sc, args);
}
- static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
+ static void captureLayers(std::unique_ptr<ScreenCapture>* sc,
+ const LayerCaptureArgs& captureArgs) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
SurfaceComposerClient::Transaction().apply(true);
- LayerCaptureArgs args;
- args.layerHandle = parentHandle;
- args.sourceCrop = crop;
- args.frameScale = frameScale;
- args.childrenOnly = false;
-
ScreenCaptureResults captureResults;
- ASSERT_EQ(NO_ERROR, sf->captureLayers(args, captureResults));
- *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
- }
-
- static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- SurfaceComposerClient::Transaction().apply(true);
-
- LayerCaptureArgs args;
- args.layerHandle = parentHandle;
- args.sourceCrop = crop;
- args.frameScale = frameScale;
- args.childrenOnly = true;
-
- ScreenCaptureResults captureResults;
- ASSERT_EQ(NO_ERROR, sf->captureLayers(args, captureResults));
- *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
- }
-
- static void captureChildLayersExcluding(
- std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
- std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- SurfaceComposerClient::Transaction().apply(true);
-
- LayerCaptureArgs args;
- args.layerHandle = parentHandle;
- args.pixelFormat = ui::PixelFormat::RGBA_8888;
- args.sourceCrop = Rect::EMPTY_RECT;
- args.excludeHandles = excludeLayers;
- args.frameScale = 1.0f;
- args.childrenOnly = true;
-
- ScreenCaptureResults captureResults;
- ASSERT_EQ(NO_ERROR, sf->captureLayers(args, captureResults));
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(captureArgs, captureResults));
*sc = std::make_unique<ScreenCapture>(captureResults.buffer);
}
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 03e51ae..46175ad 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -28,6 +28,7 @@
#include <vibratorservice/VibratorHalWrapper.h>
using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
using android::hardware::vibrator::Effect;
using android::hardware::vibrator::EffectStrength;
@@ -91,7 +92,7 @@
template <typename T>
HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) {
if (result.isFailed()) {
- ALOGE("%s failed: Vibrator HAL not available", functionName);
+ ALOGE("%s failed: %s", functionName, result.errorMessage());
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
mConnectedHal->tryReconnect();
}
@@ -201,6 +202,12 @@
return apply(getSupportedEffectsFn, "getSupportedEffects");
}
+HalResult<std::vector<CompositePrimitive>> HalController::getSupportedPrimitives() {
+ hal_fn<std::vector<CompositePrimitive>> getSupportedPrimitivesFn =
+ [](std::shared_ptr<HalWrapper> hal) { return hal->getSupportedPrimitives(); };
+ return apply(getSupportedPrimitivesFn, "getSupportedPrimitives");
+}
+
HalResult<milliseconds> HalController::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 9d219ff..ce20aeb 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -27,6 +27,7 @@
#include <vibratorservice/VibratorHalWrapper.h>
using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
using android::hardware::vibrator::Effect;
using android::hardware::vibrator::EffectStrength;
@@ -67,6 +68,10 @@
// -------------------------------------------------------------------------------------------------
+const constexpr char* STATUS_T_ERROR_MESSAGE_PREFIX = "status_t = ";
+const constexpr char* STATUS_V_1_0_ERROR_MESSAGE_PREFIX =
+ "android::hardware::vibrator::V1_0::Status = ";
+
template <typename T>
HalResult<T> HalResult<T>::fromStatus(binder::Status status, T data) {
if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
@@ -75,7 +80,7 @@
if (status.isOk()) {
return HalResult<T>::ok(data);
}
- return HalResult<T>::failed();
+ return HalResult<T>::failed(std::string(status.toString8().c_str()));
}
template <typename T>
@@ -86,24 +91,32 @@
case V1_0::Status::UNSUPPORTED_OPERATION:
return HalResult<T>::unsupported();
default:
- return HalResult<T>::failed();
+ return HalResult<T>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status));
}
}
template <typename T>
template <typename R>
HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
- return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed();
+ return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
}
template <typename T>
template <typename R>
HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
- return ret.isOk() ? HalResult<T>::fromStatus(status, data) : HalResult<T>::failed();
+ return ret.isOk() ? HalResult<T>::fromStatus(status, data)
+ : HalResult<T>::failed(ret.description());
}
// -------------------------------------------------------------------------------------------------
+HalResult<void> HalResult<void>::fromStatus(status_t status) {
+ if (status == android::OK) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(STATUS_T_ERROR_MESSAGE_PREFIX + statusToString(status));
+}
+
HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
return HalResult<void>::unsupported();
@@ -111,7 +124,7 @@
if (status.isOk()) {
return HalResult<void>::ok();
}
- return HalResult<void>::failed();
+ return HalResult<void>::failed(std::string(status.toString8().c_str()));
}
HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) {
@@ -121,13 +134,13 @@
case V1_0::Status::UNSUPPORTED_OPERATION:
return HalResult<void>::unsupported();
default:
- return HalResult<void>::failed();
+ return HalResult<void>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status));
}
}
template <typename R>
HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
- return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed();
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
}
// -------------------------------------------------------------------------------------------------
@@ -149,8 +162,7 @@
// -------------------------------------------------------------------------------------------------
HalResult<void> AidlHalWrapper::ping() {
- return IInterface::asBinder(getHal())->pingBinder() ? HalResult<void>::ok()
- : HalResult<void>::failed();
+ return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder());
}
void AidlHalWrapper::tryReconnect() {
@@ -210,6 +222,13 @@
mSupportedEffects);
}
+HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitives() {
+ std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex);
+ return loadCached<std::vector<
+ CompositePrimitive>>(std::bind(&AidlHalWrapper::getSupportedPrimitivesInternal, this),
+ mSupportedPrimitives);
+}
+
HalResult<milliseconds> AidlHalWrapper::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
HalResult<Capabilities> capabilities = getCapabilities();
@@ -249,6 +268,12 @@
return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
}
+HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() {
+ std::vector<CompositePrimitive> supportedPrimitives;
+ auto result = getHal()->getSupportedPrimitives(&supportedPrimitives);
+ return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
+}
+
sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
@@ -326,6 +351,12 @@
}
template <typename I>
+HalResult<std::vector<CompositePrimitive>> HidlHalWrapper<I>::getSupportedPrimitives() {
+ ALOGV("Skipped getSupportedPrimitives because Vibrator HAL AIDL is not available");
+ return HalResult<std::vector<CompositePrimitive>>::unsupported();
+}
+
+template <typename I>
HalResult<void> HidlHalWrapper<I>::performComposedEffect(const std::vector<CompositeEffect>&,
const std::function<void()>&) {
ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
@@ -456,15 +487,15 @@
}
HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
+ Capabilities capabilities = Capabilities::NONE;
+
sp<V1_3::IVibrator> hal = getHal();
auto amplitudeResult = hal->supportsAmplitudeControl();
if (!amplitudeResult.isOk()) {
- return HalResult<Capabilities>::failed();
+ return HalResult<Capabilities>::fromReturn(amplitudeResult, capabilities);
}
auto externalControlResult = hal->supportsExternalControl();
- Capabilities capabilities = Capabilities::NONE;
-
if (amplitudeResult.withDefault(false)) {
capabilities |= Capabilities::AMPLITUDE_CONTROL;
}
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index daf2c8c..3b61f42 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -69,6 +69,8 @@
HalResult<Capabilities> getCapabilities() final override;
HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() final override;
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+ final override;
HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index a4fa869..7b99bbb 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -35,8 +35,10 @@
class HalResult {
public:
static HalResult<T> ok(T value) { return HalResult(value); }
- static HalResult<T> failed() { return HalResult(/* unsupported= */ false); }
- static HalResult<T> unsupported() { return HalResult(/* unsupported= */ true); }
+ static HalResult<T> failed(std::string msg) {
+ return HalResult(std::move(msg), /* unsupported= */ false);
+ }
+ static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
static HalResult<T> fromStatus(binder::Status status, T data);
static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
@@ -53,13 +55,17 @@
bool isOk() const { return !mUnsupported && mValue.has_value(); }
bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
private:
std::optional<T> mValue;
+ std::string mErrorMessage;
bool mUnsupported;
- explicit HalResult(T value) : mValue(std::make_optional(value)), mUnsupported(false) {}
- explicit HalResult(bool unsupported) : mValue(), mUnsupported(unsupported) {}
+ explicit HalResult(T value)
+ : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {}
+ explicit HalResult(std::string errorMessage, bool unsupported)
+ : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
};
// Empty result of a call to the Vibrator HAL wrapper.
@@ -67,11 +73,10 @@
class HalResult<void> {
public:
static HalResult<void> ok() { return HalResult(); }
- static HalResult<void> failed() { return HalResult(/* failed= */ true); }
- static HalResult<void> unsupported() {
- return HalResult(/* failed= */ false, /* unsupported= */ true);
- }
+ static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
+ static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
+ static HalResult<void> fromStatus(status_t status);
static HalResult<void> fromStatus(binder::Status status);
static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status);
@@ -81,13 +86,17 @@
bool isOk() const { return !mUnsupported && !mFailed; }
bool isFailed() const { return !mUnsupported && mFailed; }
bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
private:
+ std::string mErrorMessage;
bool mFailed;
bool mUnsupported;
- explicit HalResult(bool failed = false, bool unsupported = false)
- : mFailed(failed), mUnsupported(unsupported) {}
+ explicit HalResult(bool unsupported = false)
+ : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+ explicit HalResult(std::string errorMessage)
+ : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
};
// -------------------------------------------------------------------------------------------------
@@ -147,6 +156,8 @@
virtual HalResult<Capabilities> getCapabilities() = 0;
virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() = 0;
+ virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
+ getSupportedPrimitives() = 0;
virtual HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
@@ -185,6 +196,8 @@
HalResult<Capabilities> getCapabilities() override final;
HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+ override final;
HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
@@ -198,14 +211,18 @@
std::mutex mHandleMutex;
std::mutex mCapabilitiesMutex;
std::mutex mSupportedEffectsMutex;
+ std::mutex mSupportedPrimitivesMutex;
sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
GUARDED_BY(mSupportedEffectsMutex);
+ std::optional<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives
+ GUARDED_BY(mSupportedPrimitivesMutex);
// Loads directly from IVibrator handle, skipping caches.
HalResult<Capabilities> getCapabilitiesInternal();
HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
sp<hardware::vibrator::IVibrator> getHal();
};
@@ -233,6 +250,8 @@
HalResult<Capabilities> getCapabilities() override final;
HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
+ HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
+ override final;
HalResult<void> performComposedEffect(
const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index 2d55549..f04e016 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -65,6 +65,8 @@
MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override));
MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilities, (), (override));
MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
+ MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (),
+ (override));
MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
(Effect effect, EffectStrength strength,
const std::function<void()>& completionCallback),
@@ -132,6 +134,7 @@
vibrator::HalResult<void> voidResult,
vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
vibrator::HalResult<std::vector<Effect>> effectsResult,
+ vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult,
vibrator::HalResult<milliseconds> durationResult) {
EXPECT_CALL(*mMockHal.get(), ping())
.Times(Exactly(cardinality))
@@ -161,6 +164,9 @@
EXPECT_CALL(*mMockHal.get(), getSupportedEffects())
.Times(Exactly(cardinality))
.WillRepeatedly(Return(effectsResult));
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(primitivesResult));
EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
.Times(Exactly(cardinality))
.WillRepeatedly(Return(durationResult));
@@ -170,7 +176,7 @@
if (cardinality > 1) {
// One reconnection call after each failure.
- EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(11 * cardinality));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(12 * cardinality));
}
}
};
@@ -187,9 +193,12 @@
}
TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
- std::vector<Effect> supportedEffects;
- supportedEffects.push_back(Effect::CLICK);
- supportedEffects.push_back(Effect::TICK);
+ std::vector<Effect> effects;
+ effects.push_back(Effect::CLICK);
+ effects.push_back(Effect::TICK);
+ std::vector<CompositePrimitive> primitives;
+ primitives.push_back(CompositePrimitive::CLICK);
+ primitives.push_back(CompositePrimitive::THUD);
std::vector<CompositeEffect> compositeEffects;
compositeEffects.push_back(
vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
@@ -199,7 +208,8 @@
setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
vibrator::HalResult<vibrator::Capabilities>::ok(
vibrator::Capabilities::ON_CALLBACK),
- vibrator::HalResult<std::vector<Effect>>::ok(supportedEffects),
+ vibrator::HalResult<std::vector<Effect>>::ok(effects),
+ vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives),
vibrator::HalResult<milliseconds>::ok(100ms));
ASSERT_TRUE(mController->ping().isOk());
@@ -216,7 +226,11 @@
auto getSupportedEffectsResult = mController->getSupportedEffects();
ASSERT_TRUE(getSupportedEffectsResult.isOk());
- ASSERT_EQ(supportedEffects, getSupportedEffectsResult.value());
+ ASSERT_EQ(effects, getSupportedEffectsResult.value());
+
+ auto getSupportedPrimitivesResult = mController->getSupportedPrimitives();
+ ASSERT_TRUE(getSupportedPrimitivesResult.isOk());
+ ASSERT_EQ(primitives, getSupportedPrimitivesResult.value());
auto performEffectResult =
mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
@@ -233,6 +247,7 @@
vibrator::HalResult<void>::unsupported(),
vibrator::HalResult<vibrator::Capabilities>::unsupported(),
vibrator::HalResult<std::vector<Effect>>::unsupported(),
+ vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(),
vibrator::HalResult<milliseconds>::unsupported());
ASSERT_EQ(0, mConnectCounter);
@@ -247,6 +262,7 @@
ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
ASSERT_TRUE(mController->getCapabilities().isUnsupported());
ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
.isUnsupported());
ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
@@ -257,10 +273,11 @@
TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) {
setHalExpectations(MAX_ATTEMPTS, std::vector<CompositeEffect>(),
- vibrator::HalResult<void>::failed(),
- vibrator::HalResult<vibrator::Capabilities>::failed(),
- vibrator::HalResult<std::vector<Effect>>::failed(),
- vibrator::HalResult<milliseconds>::failed());
+ vibrator::HalResult<void>::failed("message"),
+ vibrator::HalResult<vibrator::Capabilities>::failed("message"),
+ vibrator::HalResult<std::vector<Effect>>::failed("message"),
+ vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"),
+ vibrator::HalResult<milliseconds>::failed("message"));
ASSERT_EQ(0, mConnectCounter);
@@ -273,6 +290,7 @@
ASSERT_TRUE(mController->alwaysOnDisable(1).isFailed());
ASSERT_TRUE(mController->getCapabilities().isFailed());
ASSERT_TRUE(mController->getSupportedEffects().isFailed());
+ ASSERT_TRUE(mController->getSupportedPrimitives().isFailed());
ASSERT_TRUE(
mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
ASSERT_TRUE(
@@ -286,7 +304,7 @@
InSequence seq;
EXPECT_CALL(*mMockHal.get(), ping())
.Times(Exactly(1))
- .WillRepeatedly(Return(vibrator::HalResult<void>::failed()));
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
EXPECT_CALL(*mMockHal.get(), ping())
.Times(Exactly(1))
@@ -331,13 +349,14 @@
ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
ASSERT_TRUE(mController->getCapabilities().isUnsupported());
ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
.isUnsupported());
ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
.isUnsupported());
// One connection attempt per api call.
- ASSERT_EQ(11, mConnectCounter);
+ ASSERT_EQ(12, mConnectCounter);
}
TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
@@ -351,11 +370,11 @@
});
EXPECT_CALL(*mMockHal.get(), ping())
.Times(Exactly(1))
- .WillRepeatedly(Return(vibrator::HalResult<void>::failed()));
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
EXPECT_CALL(*mMockHal.get(), ping())
.Times(Exactly(1))
- .WillRepeatedly(Return(vibrator::HalResult<void>::failed()));
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
}
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 0f2d7bc..96b76ba 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -116,14 +116,15 @@
}
TEST_F(VibratorHalWrapperAidlTest, TestPing) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), onAsBinder())
- .Times(Exactly(1))
- .WillRepeatedly(Return(mMockBinder.get()));
- EXPECT_CALL(*mMockBinder.get(), pingBinder()).Times(Exactly(1));
- }
+ EXPECT_CALL(*mMockHal.get(), onAsBinder())
+ .Times(Exactly(2))
+ .WillRepeatedly(Return(mMockBinder.get()));
+ EXPECT_CALL(*mMockBinder.get(), pingBinder())
+ .Times(Exactly(2))
+ .WillOnce(Return(android::OK))
+ .WillRepeatedly(Return(android::DEAD_OBJECT));
+ ASSERT_TRUE(mWrapper->ping().isOk());
ASSERT_TRUE(mWrapper->ping().isFailed());
}
@@ -366,6 +367,50 @@
ASSERT_EQ(supportedEffects, result.value());
}
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesDoesNotCacheFailedResult) {
+ std::vector<CompositePrimitive> supportedPrimitives;
+ supportedPrimitives.push_back(CompositePrimitive::CLICK);
+ supportedPrimitives.push_back(CompositePrimitive::THUD);
+
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
+ ASSERT_TRUE(mWrapper->getSupportedPrimitives().isFailed());
+
+ auto result = mWrapper->getSupportedPrimitives();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedPrimitives, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesCachesResult) {
+ std::vector<CompositePrimitive> supportedPrimitives;
+ supportedPrimitives.push_back(CompositePrimitive::CLICK);
+ supportedPrimitives.push_back(CompositePrimitive::THUD);
+
+ EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getSupportedPrimitives();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedPrimitives, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getSupportedPrimitives();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedPrimitives, result.value());
+}
+
TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
{
InSequence seq;
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index 7eb4059..06aa36f 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -236,6 +236,10 @@
ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
}
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedPrimitivesUnsupported) {
+ ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported());
+}
+
TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
{
InSequence seq;
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 8deca47..4068a16 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -120,6 +120,8 @@
const char** names;
uint32_t name_count;
+ ExtensionFilter()
+ : exts(nullptr), ext_count(0), names(nullptr), name_count(0) {}
};
VkResult SanitizeApiVersion();
@@ -607,6 +609,10 @@
} else {
count = std::min(filter.ext_count, dev_info_.enabledExtensionCount);
}
+
+ if (!count)
+ return VK_SUCCESS;
+
filter.names = reinterpret_cast<const char**>(allocator_.pfnAllocation(
allocator_.pUserData, sizeof(const char*) * count, alignof(const char*),
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
@@ -1023,9 +1029,7 @@
// conditionally add VK_GOOGLE_display_timing if present timestamps are
// supported by the driver:
- const std::string timestamp_property("service.sf.present_timestamp");
- android::base::WaitForPropertyCreation(timestamp_property);
- if (android::base::GetBoolProperty(timestamp_property, true)) {
+ if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
loader_extensions.push_back({
VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION});