Merge "Fix coordinate error after rotation and scaling in TouchInputMapper"
diff --git a/cmds/bugreport/bugreport.cpp b/cmds/bugreport/bugreport.cpp
index e5c52d8..f81994b 100644
--- a/cmds/bugreport/bugreport.cpp
+++ b/cmds/bugreport/bugreport.cpp
@@ -14,91 +14,19 @@
* limitations under the License.
*/
-#include <errno.h>
#include <stdio.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <cutils/properties.h>
-#include <cutils/sockets.h>
+// Only prints a warning redirecting to bugreportz.
+int main() {
+ fprintf(stderr,
+ "=============================================================================\n");
+ fprintf(stderr, "WARNING: Flat (text file, non-zipped) bugreports are deprecated.\n");
+ fprintf(stderr, "WARNING: Please generate zipped bugreports instead.\n");
+ fprintf(stderr, "WARNING: On the host use: adb bugreport filename.zip\n");
+ fprintf(stderr, "WARNING: On the device use: bugreportz\n");
+ fprintf(stderr, "WARNING: bugreportz will output the filename to use with adb pull.\n");
+ fprintf(stderr,
+ "=============================================================================\n\n\n");
-// This program will trigger the dumpstate service to start a call to
-// dumpstate, then connect to the dumpstate local client to read the
-// output. All of the dumpstate output is written to stdout, including
-// any errors encountered while reading/writing the output.
-int main(int argc, char* /*argv*/[]) {
- fprintf(stderr, "=============================================================================\n");
- fprintf(stderr, "WARNING: Flat (text file, non-zipped) bugreports are deprecated.\n");
- fprintf(stderr, "WARNING: Please generate zipped bugreports instead.\n");
- fprintf(stderr, "WARNING: On the host use: adb bugreport filename.zip\n");
- fprintf(stderr, "WARNING: On the device use: bugreportz\n");
- fprintf(stderr, "WARNING: bugreportz will output the filename to use with adb pull.\n");
- fprintf(stderr, "=============================================================================\n\n\n");
-
- if (argc != 1) {
- fprintf(stderr, "usage: bugreport\n");
- exit(1);
- }
-
- // Start the dumpstate service.
- property_set("ctl.start", "dumpstate");
-
- // Socket will not be available until service starts.
- int s = -1;
- for (int i = 0; i < 20; i++) {
- s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM);
- if (s >= 0)
- break;
- // Try again in 1 second.
- sleep(1);
- }
-
- if (s == -1) {
- printf("Failed to connect to dumpstate service: %s\n", strerror(errno));
- return 1;
- }
-
- // Set a timeout so that if nothing is read in 3 minutes, we'll stop
- // reading and quit. No timeout in dumpstate is longer than 60 seconds,
- // so this gives lots of leeway in case of unforeseen time outs.
- struct timeval tv;
- tv.tv_sec = 3 * 60;
- tv.tv_usec = 0;
- if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
- printf("WARNING: Cannot set socket timeout: %s\n", strerror(errno));
- }
-
- while (1) {
- char buffer[65536];
- ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)));
- if (bytes_read == 0) {
- break;
- } else if (bytes_read == -1) {
- // EAGAIN really means time out, so change the errno.
- if (errno == EAGAIN) {
- errno = ETIMEDOUT;
- }
- printf("\nBugreport read terminated abnormally (%s).\n", strerror(errno));
- break;
- }
-
- ssize_t bytes_to_send = bytes_read;
- ssize_t bytes_written;
- do {
- bytes_written = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
- buffer + bytes_read - bytes_to_send,
- bytes_to_send));
- if (bytes_written == -1) {
- printf("Failed to write data to stdout: read %zd, trying to send %zd (%s)\n",
- bytes_read, bytes_to_send, strerror(errno));
- return 1;
- }
- bytes_to_send -= bytes_written;
- } while (bytes_written != 0 && bytes_to_send > 0);
- }
-
- close(s);
- return 0;
+ return 0;
}
diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp
index ded0ed3..203d748 100644
--- a/cmds/bugreportz/bugreportz.cpp
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#include "bugreportz.h"
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -22,11 +26,6 @@
#include <string>
-#include <android-base/file.h>
-#include <android-base/strings.h>
-
-#include "bugreportz.h"
-
static constexpr char BEGIN_PREFIX[] = "BEGIN:";
static constexpr char PROGRESS_PREFIX[] = "PROGRESS:";
@@ -70,6 +69,30 @@
}
// Process final line, in case it didn't finish with newline
write_line(line, show_progress);
+ return EXIT_SUCCESS;
+}
+int bugreportz_stream(int s) {
+ while (1) {
+ char buffer[65536];
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)));
+ if (bytes_read == 0) {
+ break;
+ } else if (bytes_read == -1) {
+ // EAGAIN really means time out, so change the errno.
+ if (errno == EAGAIN) {
+ errno = ETIMEDOUT;
+ }
+ printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ if (!android::base::WriteFully(android::base::borrowed_fd(STDOUT_FILENO), buffer,
+ bytes_read)) {
+ printf("Failed to write data to stdout: trying to send %zd bytes (%s)\n", bytes_read,
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+ }
return EXIT_SUCCESS;
}
diff --git a/cmds/bugreportz/bugreportz.h b/cmds/bugreportz/bugreportz.h
index 7af289b..cdeceae 100644
--- a/cmds/bugreportz/bugreportz.h
+++ b/cmds/bugreportz/bugreportz.h
@@ -19,4 +19,8 @@
// Ownership of the socket is not transferred.
int bugreportz(int s, bool show_progress);
+// Calls dumpstate using the given socket and write the file content to stdout
+// instead of file location. Ownership of the socket is not transferred.
+int bugreportz_stream(int s);
+
#endif // BUGREPORTZ_H
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp
index 1d48e08..cd2652c 100644
--- a/cmds/bugreportz/main.cpp
+++ b/cmds/bugreportz/main.cpp
@@ -26,13 +26,14 @@
#include "bugreportz.h"
-static constexpr char VERSION[] = "1.1";
+static constexpr char VERSION[] = "1.2";
static void show_usage() {
fprintf(stderr,
- "usage: bugreportz [-hpv]\n"
+ "usage: bugreportz [-hpsv]\n"
" -h: to display this help message\n"
" -p: display progress\n"
+ " -s: stream content to standard output\n"
" -v: to display the version\n"
" or no arguments to generate a zipped bugreport\n");
}
@@ -43,10 +44,11 @@
int main(int argc, char* argv[]) {
bool show_progress = false;
+ bool stream_data = false;
if (argc > 1) {
/* parse arguments */
int c;
- while ((c = getopt(argc, argv, "hpv")) != -1) {
+ while ((c = getopt(argc, argv, "hpsv")) != -1) {
switch (c) {
case 'h':
show_usage();
@@ -54,6 +56,9 @@
case 'p':
show_progress = true;
break;
+ case 's':
+ stream_data = true;
+ break;
case 'v':
show_version();
return EXIT_SUCCESS;
@@ -75,7 +80,11 @@
// should be reused instead.
// Start the dumpstatez service.
- property_set("ctl.start", "dumpstatez");
+ if (stream_data) {
+ property_set("ctl.start", "dumpstate");
+ } else {
+ property_set("ctl.start", "dumpstatez");
+ }
// Socket will not be available until service starts.
int s = -1;
@@ -103,7 +112,12 @@
strerror(errno));
}
- int ret = bugreportz(s, show_progress);
+ int ret;
+ if (stream_data) {
+ ret = bugreportz_stream(s);
+ } else {
+ ret = bugreportz(s, show_progress);
+ }
if (close(s) == -1) {
fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno));
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index ead491e..80d14ac 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -105,6 +105,8 @@
name: "dumpstate",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"main.cpp",
],
@@ -132,6 +134,8 @@
name: "dumpstate_test",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"tests/dumpstate_test.cpp",
],
@@ -148,10 +152,14 @@
name: "dumpstate_smoke_test",
defaults: ["dumpstate_defaults"],
srcs: [
+ "DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"tests/dumpstate_smoke_test.cpp",
],
static_libs: ["libgmock"],
+ test_config: "dumpstate_smoke_test.xml",
+ test_suites: ["device-tests"],
}
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
new file mode 100644
index 0000000..e15ac3f
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpPool.h"
+
+#include <array>
+#include <thread>
+
+#include <log/log.h>
+
+#include "dumpstate.h"
+#include "DumpstateInternal.h"
+#include "DumpstateUtil.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+const std::string DumpPool::PREFIX_TMPFILE_NAME = "dump-tmp.";
+
+DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false),
+ log_duration_(true) {
+ assert(!tmp_root.empty());
+ deleteTempFiles(tmp_root_);
+}
+
+DumpPool::~DumpPool() {
+ shutdown();
+}
+
+void DumpPool::start(int thread_counts) {
+ assert(thread_counts > 0);
+ assert(threads_.empty());
+ if (thread_counts > MAX_THREAD_COUNT) {
+ thread_counts = MAX_THREAD_COUNT;
+ }
+ MYLOGI("Start thread pool:%d", thread_counts);
+ shutdown_ = false;
+ for (int i = 0; i < thread_counts; i++) {
+ threads_.emplace_back(std::thread([=]() {
+ setThreadName(pthread_self(), i + 1);
+ loop();
+ }));
+ }
+}
+
+void DumpPool::shutdown() {
+ std::unique_lock lock(lock_);
+ if (shutdown_ || threads_.empty()) {
+ return;
+ }
+ while (!tasks_.empty()) tasks_.pop();
+ futures_map_.clear();
+
+ shutdown_ = true;
+ condition_variable_.notify_all();
+ lock.unlock();
+
+ for (auto& thread : threads_) {
+ thread.join();
+ }
+ threads_.clear();
+ deleteTempFiles(tmp_root_);
+ MYLOGI("shutdown thread pool");
+}
+
+void DumpPool::waitForTask(const std::string& task_name, const std::string& title,
+ int out_fd) {
+ DurationReporter duration_reporter("Wait for " + task_name, true);
+ auto iterator = futures_map_.find(task_name);
+ if (iterator == futures_map_.end()) {
+ MYLOGW("Task %s does not exist", task_name.c_str());
+ return;
+ }
+ Future future = iterator->second;
+ futures_map_.erase(iterator);
+
+ std::string result = future.get();
+ if (result.empty()) {
+ return;
+ }
+ DumpFileToFd(out_fd, title, result);
+ if (unlink(result.c_str())) {
+ MYLOGE("Failed to unlink (%s): %s\n", result.c_str(), strerror(errno));
+ }
+}
+
+void DumpPool::deleteTempFiles() {
+ deleteTempFiles(tmp_root_);
+}
+
+void DumpPool::setLogDuration(bool log_duration) {
+ log_duration_ = log_duration;
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void()>>(std::function<void()> dump_func,
+ const std::string& duration_title, int out_fd) {
+ DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+ /*verbose =*/false, out_fd);
+ std::invoke(dump_func);
+}
+
+template <>
+void DumpPool::invokeTask<std::function<void(int)>>(std::function<void(int)> dump_func,
+ const std::string& duration_title, int out_fd) {
+ DurationReporter duration_reporter(duration_title, /*logcat_only =*/!log_duration_,
+ /*verbose =*/false, out_fd);
+ std::invoke(dump_func, out_fd);
+}
+
+std::unique_ptr<DumpPool::TmpFile> DumpPool::createTempFile() {
+ auto tmp_file_ptr = std::make_unique<TmpFile>();
+ std::string file_name_format = "%s/" + PREFIX_TMPFILE_NAME + "XXXXXX";
+ snprintf(tmp_file_ptr->path, sizeof(tmp_file_ptr->path), file_name_format.c_str(),
+ tmp_root_.c_str());
+ tmp_file_ptr->fd.reset(TEMP_FAILURE_RETRY(
+ mkostemp(tmp_file_ptr->path, O_CLOEXEC)));
+ if (tmp_file_ptr->fd.get() == -1) {
+ MYLOGE("open(%s, %s)\n", tmp_file_ptr->path, strerror(errno));
+ tmp_file_ptr = nullptr;
+ return tmp_file_ptr;
+ }
+ return tmp_file_ptr;
+}
+
+void DumpPool::deleteTempFiles(const std::string& folder) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
+ &closedir);
+ if (!dir_ptr) {
+ MYLOGE("Failed to opendir (%s): %s\n", folder.c_str(), strerror(errno));
+ return;
+ }
+ int dir_fd = dirfd(dir_ptr.get());
+ if (dir_fd < 0) {
+ MYLOGE("Failed to get fd of dir (%s): %s\n", folder.c_str(),
+ strerror(errno));
+ return;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir_ptr.get()))) {
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+ std::string file_name(de->d_name);
+ if (file_name.find(PREFIX_TMPFILE_NAME) != 0) {
+ continue;
+ }
+ if (unlinkat(dir_fd, file_name.c_str(), 0)) {
+ MYLOGE("Failed to unlink (%s): %s\n", file_name.c_str(),
+ strerror(errno));
+ }
+ }
+}
+
+void DumpPool::setThreadName(const pthread_t thread, int id) {
+ std::array<char, 15> name;
+ snprintf(name.data(), name.size(), "dumpstate_%d", id);
+ pthread_setname_np(thread, name.data());
+}
+
+void DumpPool::loop() {
+ std::unique_lock lock(lock_);
+ while (!shutdown_) {
+ if (tasks_.empty()) {
+ condition_variable_.wait(lock);
+ continue;
+ } else {
+ std::packaged_task<std::string()> task = std::move(tasks_.front());
+ tasks_.pop();
+ lock.unlock();
+ std::invoke(task);
+ lock.lock();
+ }
+ }
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h
new file mode 100644
index 0000000..0c3c2cc
--- /dev/null
+++ b/cmds/dumpstate/DumpPool.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
+
+#include <future>
+#include <map>
+#include <queue>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+class DumpPoolTest;
+
+/*
+ * A thread pool with the fixed number of threads to execute multiple dump tasks
+ * simultaneously for the dumpstate. The dump task is a callable function. It
+ * could include a file descriptor as a parameter to redirect dump results, if
+ * it needs to output results to the bugreport. This can avoid messing up
+ * bugreport's results when multiple dump tasks are running at the same time.
+ * Takes an example below for the usage of the DumpPool:
+ *
+ * void DumpFoo(int out_fd) {
+ * dprintf(out_fd, "Dump result to out_fd ...");
+ * }
+ * ...
+ * DumpPool pool(tmp_root);
+ * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
+ * ...
+ * pool.waitForTask("TaskName");
+ *
+ * DumpFoo is a callable function included a out_fd parameter. Using the
+ * enqueueTaskWithFd method in DumpPool to enqueue the task to the pool. The
+ * std::placeholders::_1 is a placeholder for DumpPool to pass a fd argument.
+ */
+class DumpPool {
+ friend class android::os::dumpstate::DumpPoolTest;
+
+ public:
+ /*
+ * Creates a thread pool.
+ *
+ * |tmp_root| A path to a temporary folder for threads to create temporary
+ * files.
+ */
+ explicit DumpPool(const std::string& tmp_root);
+ ~DumpPool();
+
+ /*
+ * Starts the threads in the pool.
+ *
+ * |thread_counts| the number of threads to start.
+ */
+ void start(int thread_counts = MAX_THREAD_COUNT);
+
+ /*
+ * Requests to shutdown the pool and waits until all threads exit the loop.
+ */
+ void shutdown();
+
+ /*
+ * Adds a task into the queue of the thread pool.
+ *
+ * |task_name| The name of the task. It's also the title of the
+ * DurationReporter log.
+ * |f| Callable function to execute the task.
+ * |args| A list of arguments.
+ *
+ * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
+ */
+ template<class F, class... Args> void enqueueTask(const std::string& task_name, F&& f,
+ Args&&... args) {
+ std::function<void(void)> func = std::bind(std::forward<F>(f),
+ std::forward<Args>(args)...);
+ futures_map_[task_name] = post(task_name, func);
+ if (threads_.empty()) {
+ start();
+ }
+ }
+
+ /*
+ * Adds a task into the queue of the thread pool. The task takes a file
+ * descriptor as a parameter to redirect dump results to a temporary file.
+ *
+ * |task_name| The name of the task. It's also the title of the
+ * DurationReporter log.
+ * |f| Callable function to execute the task.
+ * |args| A list of arguments. A placeholder std::placeholders::_1 as a fd
+ * argument needs to be included here.
+ */
+ template<class F, class... Args> void enqueueTaskWithFd(const std::string& task_name, F&& f,
+ Args&&... args) {
+ std::function<void(int)> func = std::bind(std::forward<F>(f),
+ std::forward<Args>(args)...);
+ futures_map_[task_name] = post(task_name, func);
+ if (threads_.empty()) {
+ start();
+ }
+ }
+
+ /*
+ * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
+ */
+ void waitForTask(const std::string& task_name) {
+ waitForTask(task_name, "", STDOUT_FILENO);
+ }
+
+ /*
+ * Waits until the task is finished. Dumps the task results to the specified
+ * out_fd.
+ *
+ * |task_name| The name of the task.
+ * |title| Dump title string to the out_fd, an empty string for nothing.
+ * |out_fd| The target file to dump the result from the task.
+ */
+ void waitForTask(const std::string& task_name, const std::string& title, int out_fd);
+
+ /*
+ * Deletes temporary files created by DumpPool.
+ */
+ void deleteTempFiles();
+
+ static const std::string PREFIX_TMPFILE_NAME;
+
+ private:
+ using Task = std::packaged_task<std::string()>;
+ using Future = std::shared_future<std::string>;
+
+ template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
+
+ template<class T> Future post(const std::string& task_name, T dump_func) {
+ Task packaged_task([=]() {
+ std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
+ if (!tmp_file_ptr) {
+ return std::string("");
+ }
+ invokeTask(dump_func, task_name, tmp_file_ptr->fd.get());
+ fsync(tmp_file_ptr->fd.get());
+ return std::string(tmp_file_ptr->path);
+ });
+ std::unique_lock lock(lock_);
+ auto future = packaged_task.get_future().share();
+ tasks_.push(std::move(packaged_task));
+ condition_variable_.notify_one();
+ return future;
+ }
+
+ typedef struct {
+ android::base::unique_fd fd;
+ char path[1024];
+ } TmpFile;
+
+ std::unique_ptr<TmpFile> createTempFile();
+ void deleteTempFiles(const std::string& folder);
+ void setThreadName(const pthread_t thread, int id);
+ void loop();
+
+ /*
+ * For test purpose only. Enables or disables logging duration of the task.
+ *
+ * |log_duration| if true, DurationReporter is initiated to log duration of
+ * the task.
+ */
+ void setLogDuration(bool log_duration);
+
+ private:
+ static const int MAX_THREAD_COUNT = 4;
+
+ /* A path to a temporary folder for threads to create temporary files. */
+ std::string tmp_root_;
+ bool shutdown_;
+ bool log_duration_; // For test purpose only, the default value is true.
+ std::mutex lock_; // A lock for the tasks_.
+ std::condition_variable condition_variable_;
+
+ std::vector<std::thread> threads_;
+ std::queue<Task> tasks_;
+ std::map<std::string, Future> futures_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(DumpPool);
+};
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+#endif //FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index 4b69607..eeaa5a3 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -180,6 +180,7 @@
std::string PropertiesHelper::build_type_ = "";
int PropertiesHelper::dry_run_ = -1;
int PropertiesHelper::unroot_ = -1;
+int PropertiesHelper::parallel_run_ = -1;
bool PropertiesHelper::IsUserBuild() {
if (build_type_.empty()) {
@@ -202,6 +203,14 @@
return unroot_ == 1;
}
+bool PropertiesHelper::IsParallelRun() {
+ if (parallel_run_ == -1) {
+ parallel_run_ = android::base::GetBoolProperty("dumpstate.parallel_run",
+ /* default_value = */true) ? 1 : 0;
+ }
+ return parallel_run_ == 1;
+}
+
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
if (fd.get() < 0) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index b7ac25c..b099443 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -176,10 +176,18 @@
*/
static bool IsUnroot();
+ /*
+ * Whether or not the parallel run is enabled. Setting the system property
+ * 'dumpstate.parallel_run' to false to disable it, otherwise it returns
+ * true by default.
+ */
+ static bool IsParallelRun();
+
private:
static std::string build_type_;
static int dry_run_;
static int unroot_;
+ static int parallel_run_;
};
/*
diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING
index 083944f..839a2c3 100644
--- a/cmds/dumpstate/TEST_MAPPING
+++ b/cmds/dumpstate/TEST_MAPPING
@@ -1,7 +1,28 @@
{
"presubmit": [
{
+ "name": "BugreportManagerTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ },
+ {
+ "name": "dumpstate_smoke_test"
+ },
+ {
"name": "dumpstate_test"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "BugreportManagerTestCases"
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/packages/Shell"
+ }
]
-}
\ No newline at end of file
+}
diff --git a/cmds/dumpstate/TaskQueue.cpp b/cmds/dumpstate/TaskQueue.cpp
new file mode 100644
index 0000000..8550aec
--- /dev/null
+++ b/cmds/dumpstate/TaskQueue.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TaskQueue.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+TaskQueue::~TaskQueue() {
+ run(/* do_cancel = */true);
+}
+
+void TaskQueue::run(bool do_cancel) {
+ std::unique_lock lock(lock_);
+ while (!tasks_.empty()) {
+ auto task = tasks_.front();
+ tasks_.pop();
+ lock.unlock();
+ std::invoke(task, do_cancel);
+ lock.lock();
+ }
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/TaskQueue.h b/cmds/dumpstate/TaskQueue.h
new file mode 100644
index 0000000..b7e72f1
--- /dev/null
+++ b/cmds/dumpstate/TaskQueue.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
+#define FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
+
+#include <mutex>
+#include <queue>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+/*
+ * A task queue for dumpstate to collect tasks such as adding file to the zip
+ * which are needed to run in a single thread. The task is a callable function
+ * included a cancel task boolean parameter. The TaskQueue could
+ * cancel the task in the destructor if the task has never been called.
+ */
+class TaskQueue {
+ public:
+ TaskQueue() = default;
+ ~TaskQueue();
+
+ /*
+ * Adds a task into the queue.
+ *
+ * |f| Callable function to execute the task. The function must include a
+ * boolean parameter for TaskQueue to notify whether the task is
+ * cancelled or not.
+ * |args| A list of arguments.
+ */
+ template<class F, class... Args> void add(F&& f, Args&&... args) {
+ auto func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
+ std::unique_lock lock(lock_);
+ tasks_.emplace([=](bool cancelled) {
+ std::invoke(func, cancelled);
+ });
+ }
+
+ /*
+ * Invokes all tasks in the task queue.
+ *
+ * |do_cancel| true to cancel all tasks in the queue.
+ */
+ void run(bool do_cancel);
+
+ private:
+ using Task = std::function<void(bool)>;
+
+ std::mutex lock_;
+ std::queue<Task> tasks_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueue);
+};
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+#endif //FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index a5e6c68..85e6969 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -72,5 +72,5 @@
/**
* Called when ui intensive bugreport dumps are finished.
*/
- oneway void onUiIntensiveBugreportDumpsFinished(String callingPackage);
+ oneway void onUiIntensiveBugreportDumpsFinished();
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 84de9f3..a374c86 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mount.h>
#include <sys/poll.h>
#include <sys/prctl.h>
#include <sys/resource.h>
@@ -95,6 +96,7 @@
using ::android::hardware::dumpstate::V1_1::toString;
using ::std::literals::chrono_literals::operator""ms;
using ::std::literals::chrono_literals::operator""s;
+using ::std::placeholders::_1;
// TODO: remove once moved to namespace
using android::defaultServiceManager;
@@ -113,7 +115,9 @@
using android::os::IDumpstateListener;
using android::os::dumpstate::CommandOptions;
using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::DumpPool;
using android::os::dumpstate::PropertiesHelper;
+using android::os::dumpstate::TaskQueue;
// Keep in sync with
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -126,8 +130,8 @@
static Dumpstate& ds = Dumpstate::GetInstance();
static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options = CommandOptions::DEFAULT,
- bool verbose_duration = false) {
- return ds.RunCommand(title, full_command, options, verbose_duration);
+ bool verbose_duration = false, int out_fd = STDOUT_FILENO) {
+ return ds.RunCommand(title, full_command, options, verbose_duration, out_fd);
}
// Reasonable value for max stats.
@@ -196,8 +200,35 @@
func_ptr(__VA_ARGS__); \
RETURN_IF_USER_DENIED_CONSENT();
+// Runs func_ptr, and logs a duration report after it's finished.
+#define RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, ...) \
+ { \
+ DurationReporter duration_reporter_in_macro(log_title); \
+ func_ptr(__VA_ARGS__); \
+ }
+
+// Similar with RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK, an additional duration report
+// is output after a slow function is finished.
+#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(log_title, func_ptr, ...) \
+ RETURN_IF_USER_DENIED_CONSENT(); \
+ RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, __VA_ARGS__); \
+ RETURN_IF_USER_DENIED_CONSENT();
+
+#define WAIT_TASK_WITH_CONSENT_CHECK(task_name, pool_ptr) \
+ RETURN_IF_USER_DENIED_CONSENT(); \
+ pool_ptr->waitForTask(task_name); \
+ RETURN_IF_USER_DENIED_CONSENT();
+
static const char* WAKE_LOCK_NAME = "dumpstate_wakelock";
+// Names of parallel tasks, they are used for the DumpPool to identify the dump
+// task and the log title of the duration report.
+static const std::string DUMP_TRACES_TASK = "DUMP TRACES";
+static const std::string DUMP_INCIDENT_REPORT_TASK = "INCIDENT REPORT";
+static const std::string DUMP_HALS_TASK = "DUMP HALS";
+static const std::string DUMP_BOARD_TASK = "dumpstate_board()";
+static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
+
namespace android {
namespace os {
namespace {
@@ -306,8 +337,12 @@
static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,
- long dumpsysTimeoutMs = 0) {
- return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs);
+ long dumpsysTimeoutMs = 0, int out_fd = STDOUT_FILENO) {
+ return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs, out_fd);
+}
+static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+ int out_fd) {
+ return ds.RunDumpsys(title, dumpsysArgs, Dumpstate::DEFAULT_DUMPSYS, 0, out_fd);
}
static int DumpFile(const std::string& title, const std::string& path) {
return ds.DumpFile(title, path);
@@ -684,6 +719,9 @@
return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS;
}
+// Opens a socket and returns its file descriptor.
+static int open_socket(const char* service);
+
Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) {
}
@@ -762,8 +800,9 @@
RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
CommandOptions::WithTimeout(1).Always().Build());
printf("Bugreport format version: %s\n", version_.c_str());
- printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s bugreport_mode=%s\n", id_, pid_,
- PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->bugreport_mode.c_str());
+ printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n",
+ id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
+ options_->args.c_str(), options_->bugreport_mode.c_str());
printf("\n");
}
@@ -993,7 +1032,6 @@
MYLOGD("Not dumping incident report because it's not a zipped bugreport\n");
return;
}
- DurationReporter duration_reporter("INCIDENT REPORT");
const std::string path = ds.bugreport_internal_dir_ + "/tmp_incident_report";
auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(),
O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
@@ -1008,9 +1046,11 @@
// Use a different name from "incident.proto"
// /proto/incident.proto is reserved for incident service dump
// i.e. metadata for debugging.
- ds.AddZipEntry(kProtoPath + "incident_report" + kProtoExt, path);
+ ds.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "incident_report" + kProtoExt,
+ path);
+ } else {
+ unlink(path.c_str());
}
- unlink(path.c_str());
}
static void DumpVisibleWindowViews() {
@@ -1305,15 +1345,20 @@
/* timeout= */ 90s, /* service_timeout= */ 10s);
}
-static void DumpHals() {
+/*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpHals(int out_fd = STDOUT_FILENO) {
if (!ds.IsZipping()) {
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
CommandOptions::WithTimeout(60).AsRootIfAvailable().Build());
return;
}
- DurationReporter duration_reporter("DUMP HALS");
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
- CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+ CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(),
+ false, out_fd);
using android::hidl::manager::V1_0::IServiceManager;
using android::hardware::defaultServiceManager;
@@ -1335,6 +1380,7 @@
}, '_');
const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName;
+ bool empty = false;
{
auto fd = android::base::unique_fd(
TEMP_FAILURE_RETRY(open(path.c_str(),
@@ -1349,13 +1395,14 @@
{"lshal", "debug", "-E", interface},
CommandOptions::WithTimeout(2).AsRootIfAvailable().Build());
- bool empty = 0 == lseek(fd, 0, SEEK_END);
- if (!empty) {
- ds.AddZipEntry("lshal-debug/" + cleanName + ".txt", path);
- }
+ empty = 0 == lseek(fd, 0, SEEK_END);
}
-
- unlink(path.c_str());
+ if (!empty) {
+ ds.EnqueueAddZipEntryAndCleanupIfNeeded("lshal-debug/" + cleanName + ".txt",
+ path);
+ } else {
+ unlink(path.c_str());
+ }
}
});
@@ -1427,6 +1474,8 @@
RunDumpsys("DUMPSYS NETWORK_SERVICE_LIMITED", {"wifi", "-a"},
CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS CONNECTIVITY REQUESTS", {"connectivity", "requests"},
+ CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
printf("========================================================\n");
printf("== Dropbox crashes\n");
@@ -1443,6 +1492,73 @@
printf("========================================================\n");
}
+/*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpCheckins(int out_fd = STDOUT_FILENO) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Checkins\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}, out_fd);
+ RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}, out_fd);
+ RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"}, out_fd);
+}
+
+/*
+ * Runs dumpsys on activity service to dump all application activities, services
+ * and providers in the device.
+ *
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpAppInfos(int out_fd = STDOUT_FILENO) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Activities\n");
+ dprintf(out_fd, "========================================================\n");
+
+ // The following dumpsys internally collects output from running apps, so it can take a long
+ // time. So let's extend the timeout.
+
+ const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
+
+ RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Services (platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Services (non-platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Providers (platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Providers (non-platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+}
+
// Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent
// via the consent they are shown. Ignores other errors that occur while running various
// commands. The consent checking is currently done around long running tasks, which happen to
@@ -1450,6 +1566,18 @@
static Dumpstate::RunStatus dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
+ // Enqueue slow functions into the thread pool, if the parallel run is enabled.
+ if (ds.dump_pool_) {
+ // Pool was shutdown in DumpstateDefaultAfterCritical method in order to
+ // drop root user. Restarts it with two threads for the parallel run.
+ ds.dump_pool_->start(/* thread_counts = */2);
+
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+ ds.dump_pool_->enqueueTask(DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
+ }
+
// Dump various things. Note that anything that takes "long" (i.e. several seconds) should
// check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped
// in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK).
@@ -1481,7 +1609,11 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"},
CommandOptions::AS_ROOT);
- DumpHals();
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_HALS_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_HALS_TASK, DumpHals);
+ }
RunCommand("PRINTENV", {"printenv"});
RunCommand("NETSTAT", {"netstat", "-nW"});
@@ -1564,7 +1696,11 @@
ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard);
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_BOARD_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
+ }
/* Migrate the ril_dumpstate to a device specific dumpstate? */
int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
@@ -1586,57 +1722,13 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal);
- printf("========================================================\n");
- printf("== Checkins\n");
- printf("========================================================\n");
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins);
+ }
- RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
-
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"});
-
- RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
- RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
- RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
- RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
-
- printf("========================================================\n");
- printf("== Running Application Activities\n");
- printf("========================================================\n");
-
- // The following dumpsys internally collects output from running apps, so it can take a long
- // time. So let's extend the timeout.
-
- const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
-
- RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Services (platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Services (non-platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Providers (platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Providers (non-platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpAppInfos);
printf("========================================================\n");
printf("== Dropbox crashes\n");
@@ -1661,7 +1753,12 @@
// Add linker configuration directory
ds.AddDir(LINKERCONFIG_DIR, true);
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport);
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_INCIDENT_REPORT_TASK,
+ DumpIncidentReport);
+ }
return Dumpstate::RunStatus::OK;
}
@@ -1682,7 +1779,18 @@
time_t logcat_ts = time(nullptr);
/* collect stack traces from Dalvik and native processes (needs root) */
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path);
+ if (dump_pool_) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ // One thread is enough since we only need to enqueue DumpTraces here.
+ dump_pool_->start(/* thread_counts = */1);
+
+ // DumpTraces takes long time, post it to the another thread in the
+ // pool, if pool is available
+ dump_pool_->enqueueTask(DUMP_TRACES_TASK, &Dumpstate::DumpTraces, &ds, &dump_traces_path);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_TRACES_TASK, ds.DumpTraces,
+ &dump_traces_path);
+ }
/* Run some operations that require root. */
ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
@@ -1725,6 +1833,15 @@
DumpFile("PSI memory", "/proc/pressure/memory");
DumpFile("PSI io", "/proc/pressure/io");
+ if (dump_pool_) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ dump_pool_->waitForTask(DUMP_TRACES_TASK);
+
+ // Current running thread in the pool is the root user also. Shutdown
+ // the pool and restart later to ensure all threads in the pool could
+ // drop the root user.
+ dump_pool_->shutdown();
+ }
if (!DropRootUser()) {
return Dumpstate::RunStatus::ERROR;
}
@@ -1736,31 +1853,39 @@
return status;
}
+// Common states for telephony and wifi which are needed to be collected before
+// dumpstate drop the root user.
+static void DumpstateRadioAsRoot() {
+ DumpIpTablesAsRoot();
+ ds.AddDir(LOGPERSIST_DATA_DIR, false);
+}
+
// This method collects common dumpsys for telephony and wifi. Typically, wifi
// reports are fine to include all information, but telephony reports on user
// builds need to strip some content (see DumpstateTelephonyOnly).
static void DumpstateRadioCommon(bool include_sensitive_info = true) {
- DumpIpTablesAsRoot();
-
- ds.AddDir(LOGPERSIST_DATA_DIR, false);
-
- if (!DropRootUser()) {
- return;
- }
-
// We need to be picky about some stuff for telephony reports on user builds.
if (!include_sensitive_info) {
// Only dump the radio log buffer (other buffers and dumps contain too much unrelated info).
DoRadioLogcat();
} else {
+ // DumpHals takes long time, post it to the another thread in the pool,
+ // if pool is available.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+ }
// Contains various system properties and process startup info.
do_dmesg();
// Logs other than the radio buffer may contain package/component names and potential PII.
DoLogcat();
// Too broad for connectivity problems.
DoKmsg();
- // Contains unrelated hardware info (camera, NFC, biometrics, ...).
- DumpHals();
+ // DumpHals contains unrelated hardware info (camera, NFC, biometrics, ...).
+ if (ds.dump_pool_) {
+ ds.dump_pool_->waitForTask(DUMP_HALS_TASK);
+ } else {
+ RUN_SLOW_FUNCTION_AND_LOG(DUMP_HALS_TASK, DumpHals);
+ }
}
DumpPacketStats();
@@ -1784,6 +1909,21 @@
const bool include_sensitive_info = !PropertiesHelper::IsUserBuild();
+ DumpstateRadioAsRoot();
+ if (!DropRootUser()) {
+ return;
+ }
+
+ // Starts thread pool after the root user is dropped, and two additional threads
+ // are created for DumpHals in the DumpstateRadioCommon and DumpstateBoard.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->start(/*thread_counts =*/2);
+
+ // DumpstateBoard takes long time, post it to the another thread in the pool,
+ // if pool is available.
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+ }
+
DumpstateRadioCommon(include_sensitive_info);
if (include_sensitive_info) {
@@ -1860,12 +2000,29 @@
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
+
+ if (ds.dump_pool_) {
+ ds.dump_pool_->waitForTask(DUMP_BOARD_TASK);
+ } else {
+ RUN_SLOW_FUNCTION_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
+ }
}
// This method collects dumpsys for wifi debugging only
static void DumpstateWifiOnly() {
DurationReporter duration_reporter("DUMPSTATE");
+ DumpstateRadioAsRoot();
+ if (!DropRootUser()) {
+ return;
+ }
+
+ // Starts thread pool after the root user is dropped. Only one additional
+ // thread is needed for DumpHals in the DumpstateRadioCommon.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->start(/*thread_counts =*/1);
+ }
+
DumpstateRadioCommon();
printf("========================================================\n");
@@ -1883,8 +2040,6 @@
}
Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
- DurationReporter duration_reporter("DUMP TRACES");
-
const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
const size_t buf_size = temp_file_pattern.length() + 1;
std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
@@ -1992,17 +2147,32 @@
return RunStatus::OK;
}
-void Dumpstate::DumpstateBoard() {
- DurationReporter duration_reporter("dumpstate_board()");
- printf("========================================================\n");
- printf("== Board\n");
- printf("========================================================\n");
+void Dumpstate::DumpstateBoard(int out_fd) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Board\n");
+ dprintf(out_fd, "========================================================\n");
if (!IsZipping()) {
MYLOGD("Not dumping board info because it's not a zipped bugreport\n");
return;
}
+ /*
+ * mount debugfs for non-user builds which launch with S and unmount it
+ * after invoking dumpstateBoard_* methods. This is to enable debug builds
+ * to not have debugfs mounted during runtime. It will also ensure that
+ * debugfs is only accessed by the dumpstate HAL.
+ */
+ auto api_level = android::base::GetIntProperty("ro.product.first_api_level", 0);
+ bool mount_debugfs = !PropertiesHelper::IsUserBuild() && api_level >= 31;
+
+ if (mount_debugfs) {
+ RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"},
+ AS_ROOT_20);
+ RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"},
+ AS_ROOT_20);
+ }
+
std::vector<std::string> paths;
std::vector<android::base::ScopeGuard<std::function<void()>>> remover;
for (int i = 0; i < NUM_OF_DUMPS; i++) {
@@ -2102,6 +2272,10 @@
"there might be racing in content\n", killing_timeout_sec);
}
+ if (mount_debugfs) {
+ RunCommand("unmount debugfs", {"umount", "/sys/kernel/debug"}, AS_ROOT_20);
+ }
+
auto file_sizes = std::make_unique<ssize_t[]>(paths.size());
for (size_t i = 0; i < paths.size(); i++) {
struct stat s;
@@ -2122,27 +2296,26 @@
MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str());
continue;
}
- AddZipEntry(kDumpstateBoardFiles[i], paths[i]);
- printf("*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
+ remover[i].Disable();
+ EnqueueAddZipEntryAndCleanupIfNeeded(kDumpstateBoardFiles[i], paths[i]);
+ dprintf(out_fd, "*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
}
}
static void ShowUsage() {
fprintf(stderr,
- "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] "
- "[-z] [-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n"
+ "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-p] "
+ "[-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n"
" -h: display this help message\n"
" -b: play sound file instead of vibrate, at beginning of job\n"
" -e: play sound file instead of vibrate, at end of job\n"
" -o: write to custom directory (only in limited mode)\n"
- " -d: append date to filename\n"
" -p: capture screenshot to filename.png\n"
- " -z: generate zipped file\n"
- " -s: write output to control socket (for init)\n"
- " -S: write file location to control socket (for init; requires -z)\n"
+ " -s: write zipped file to control socket (for init)\n"
+ " -S: write file location to control socket (for init)\n"
" -q: disable vibrate\n"
" -P: send broadcast when started and do progress updates\n"
- " -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n"
+ " -R: take bugreport in remote mode (shouldn't be used with -P)\n"
" -w: start binder service and make it wait for a call to startBugreport\n"
" -L: output limited information that is safe for submission in feedback reports\n"
" -v: prints the dumpstate header and exit\n");
@@ -2153,6 +2326,11 @@
}
bool Dumpstate::FinishZipFile() {
+ // Runs all enqueued adding zip entry and cleanup tasks before finishing the zip file.
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/* do_cancel = */false);
+ }
+
std::string entry_name = base_name_ + "-" + name_ + ".txt";
MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
tmp_path_.c_str());
@@ -2236,21 +2414,17 @@
/*
* Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter
- * if we are writing zip files and adds the version file.
+ * and adds the version file. Return false if zip_file could not be open to write.
*/
-static void PrepareToWriteToFile() {
+static bool PrepareToWriteToFile() {
MaybeResolveSymlink(&ds.bugreport_internal_dir_);
std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE");
ds.base_name_ = StringPrintf("bugreport-%s-%s", device_name.c_str(), build_id.c_str());
- if (ds.options_->do_add_date) {
- char date[80];
- strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
- ds.name_ = date;
- } else {
- ds.name_ = "undated";
- }
+ char date[80];
+ strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
+ ds.name_ = date;
if (ds.options_->telephony_only) {
ds.base_name_ += "-telephony";
@@ -2277,18 +2451,17 @@
destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(),
ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
- if (ds.options_->do_zip_file) {
- ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
- MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
- create_parent_dirs(ds.path_.c_str());
- ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
- if (ds.zip_file == nullptr) {
- MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
- } else {
- ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
- }
- ds.AddTextZipEntry("version.txt", ds.version_);
+ ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
+ MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
+ create_parent_dirs(ds.path_.c_str());
+ ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
+ if (ds.zip_file == nullptr) {
+ MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
+ return false;
}
+ ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
+ ds.AddTextZipEntry("version.txt", ds.version_);
+ return true;
}
/*
@@ -2296,14 +2469,9 @@
* printing zipped file status, etc.
*/
static void FinalizeFile() {
- bool do_text_file = true;
- if (ds.options_->do_zip_file) {
- if (!ds.FinishZipFile()) {
- MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
- do_text_file = true;
- } else {
- do_text_file = false;
- }
+ bool do_text_file = !ds.FinishZipFile();
+ if (do_text_file) {
+ MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
}
std::string final_path = ds.path_;
@@ -2312,7 +2480,9 @@
android::os::CopyFileToFile(ds.path_, final_path);
}
- if (ds.options_->use_control_socket) {
+ if (ds.options_->stream_to_socket) {
+ android::os::CopyFileToFd(ds.path_, ds.control_socket_fd_);
+ } else if (ds.options_->progress_updates_to_socket) {
if (do_text_file) {
dprintf(ds.control_socket_fd_,
"FAIL:could not create zip file, check %s "
@@ -2356,7 +2526,6 @@
break;
case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
// Currently, the dumpstate binder is only used by Shell to update progress.
- options->do_start_service = true;
options->do_progress_updates = true;
options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
@@ -2368,9 +2537,7 @@
options->dumpstate_hal_mode = DumpstateMode::REMOTE;
break;
case Dumpstate::BugreportMode::BUGREPORT_WEAR:
- options->do_start_service = true;
options->do_progress_updates = true;
- options->do_zip_file = true;
options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::WEAR;
break;
@@ -2383,7 +2550,6 @@
break;
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
options->wifi_only = true;
- options->do_zip_file = true;
options->do_screenshot = false;
options->dumpstate_hal_mode = DumpstateMode::WIFI;
break;
@@ -2394,13 +2560,13 @@
static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
MYLOGI(
- "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
- "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
+ "do_vibrate: %d stream_to_socket: %d progress_updates_to_socket: %d do_screenshot: %d "
+ "is_remote_mode: %d show_header_only: %d telephony_only: %d "
"wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
"limited_only: %d args: %s\n",
- options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
+ options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket,
options.do_screenshot, options.is_remote_mode, options.show_header_only,
- options.do_start_service, options.telephony_only, options.wifi_only,
+ options.telephony_only, options.wifi_only,
options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(),
toString(options.dumpstate_hal_mode).c_str(), options.limited_only, options.args.c_str());
}
@@ -2409,11 +2575,6 @@
const android::base::unique_fd& bugreport_fd_in,
const android::base::unique_fd& screenshot_fd_in,
bool is_screenshot_requested) {
- // In the new API world, date is always added; output is always a zip file.
- // TODO(111441001): remove these options once they are obsolete.
- do_add_date = true;
- do_zip_file = true;
-
// Duplicate the fds because the passed in fds don't outlive the binder transaction.
bugreport_fd.reset(dup(bugreport_fd_in.get()));
screenshot_fd.reset(dup(screenshot_fd_in.get()));
@@ -2427,18 +2588,20 @@
while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1) {
switch (c) {
// clang-format off
- case 'd': do_add_date = true; break;
- case 'z': do_zip_file = true; break;
case 'o': out_dir = optarg; break;
- case 's': use_socket = true; break;
- case 'S': use_control_socket = true; break;
+ case 's': stream_to_socket = true; break;
+ case 'S': progress_updates_to_socket = true; break;
case 'v': show_header_only = true; break;
case 'q': do_vibrate = false; break;
case 'p': do_screenshot = true; break;
case 'P': do_progress_updates = true; break;
case 'R': is_remote_mode = true; break;
case 'L': limited_only = true; break;
- case 'V': break; // compatibility no-op
+ case 'V':
+ case 'd':
+ case 'z':
+ // compatibility no-op
+ break;
case 'w':
// This was already processed
break;
@@ -2467,19 +2630,15 @@
}
bool Dumpstate::DumpOptions::ValidateOptions() const {
- if (bugreport_fd.get() != -1 && !do_zip_file) {
+ if (bugreport_fd.get() != -1 && stream_to_socket) {
return false;
}
- if ((do_zip_file || do_add_date || do_progress_updates) && !OutputToFile()) {
+ if ((progress_updates_to_socket || do_progress_updates) && stream_to_socket) {
return false;
}
- if (use_control_socket && !do_zip_file) {
- return false;
- }
-
- if (is_remote_mode && (do_progress_updates || !do_zip_file || !do_add_date)) {
+ if (is_remote_mode && (do_progress_updates || stream_to_socket)) {
return false;
}
return true;
@@ -2531,6 +2690,15 @@
}
tombstone_data_.clear();
anr_data_.clear();
+
+ // Instead of shutdown the pool, we delete temporary files directly since
+ // shutdown blocking the call.
+ if (dump_pool_) {
+ dump_pool_->deleteTempFiles();
+ }
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/*do_cancel =*/ true);
+ }
}
/*
@@ -2545,17 +2713,16 @@
* The temporary bugreport is then populated via printfs, dumping contents of files and
* output of commands to stdout.
*
- * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final
- * text file.
+ * A bunch of other files and dumps are added to the zip archive.
*
- * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also
- * gets added to the archive.
+ * The temporary bugreport file and the log file also get added to the archive.
*
* Bugreports are first generated in a local directory and later copied to the caller's fd
* or directory if supplied.
*/
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");
@@ -2596,14 +2763,9 @@
MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n",
calling_uid, calling_package.c_str());
- // Redirect output if needed
- bool is_redirecting = options_->OutputToFile();
-
// TODO: temporarily set progress until it's part of the Dumpstate constructor
std::string stats_path =
- is_redirecting
- ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str())
- : "";
+ android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str());
progress_.reset(new Progress(stats_path));
if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
@@ -2615,15 +2777,6 @@
register_sig_handler();
- // TODO(b/111441001): maybe skip if already started?
- if (options_->do_start_service) {
- MYLOGI("Starting 'dumpstate' service\n");
- android::status_t ret;
- if ((ret = android::os::DumpstateService::Start()) != android::OK) {
- MYLOGE("Unable to start DumpstateService: %d\n", ret);
- }
- }
-
if (PropertiesHelper::IsDryRun()) {
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
@@ -2635,35 +2788,33 @@
// If we are going to use a socket, do it as early as possible
// to avoid timeouts from bugreport.
- if (options_->use_socket) {
- if (!redirect_to_socket(stdout, "dumpstate")) {
- return ERROR;
- }
- }
-
- if (options_->use_control_socket) {
+ if (options_->stream_to_socket || options_->progress_updates_to_socket) {
MYLOGD("Opening control socket\n");
- control_socket_fd_ = open_socket("dumpstate");
+ control_socket_fd_ = open_socket_fn_("dumpstate");
if (control_socket_fd_ == -1) {
return ERROR;
}
- options_->do_progress_updates = 1;
+ if (options_->progress_updates_to_socket) {
+ options_->do_progress_updates = 1;
+ }
}
- if (is_redirecting) {
- PrepareToWriteToFile();
+ if (!PrepareToWriteToFile()) {
+ return ERROR;
+ }
- if (options_->do_progress_updates) {
- // clang-format off
- std::vector<std::string> am_args = {
- "--receiver-permission", "android.permission.DUMP",
- };
- // clang-format on
- // Send STARTED broadcast for apps that listen to bugreport generation events
- SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
- if (options_->use_control_socket) {
- dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str());
- }
+ // Interactive, wear & telephony modes are default to true.
+ // and may enable from cli option or when using control socket
+ if (options_->do_progress_updates) {
+ // clang-format off
+ std::vector<std::string> am_args = {
+ "--receiver-permission", "android.permission.DUMP",
+ };
+ // clang-format on
+ // Send STARTED broadcast for apps that listen to bugreport generation events
+ SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
+ if (options_->progress_updates_to_socket) {
+ dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str());
}
}
@@ -2678,76 +2829,72 @@
Vibrate(150);
}
- if (options_->do_zip_file && zip_file != nullptr) {
+ if (zip_file != nullptr) {
if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(),
- strerror(errno));
+ strerror(errno));
}
}
int dup_stdout_fd;
int dup_stderr_fd;
- if (is_redirecting) {
- // Redirect stderr to log_path_ for debugging.
- TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr)));
- if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) {
- return ERROR;
- }
- if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) {
- MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(),
- strerror(errno));
- }
+ // Redirect stderr to log_path_ for debugging.
+ TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr)));
+ if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) {
+ return ERROR;
+ }
+ if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) {
+ MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(),
+ strerror(errno));
+ }
- // Redirect stdout to tmp_path_. This is the main bugreport entry and will be
- // moved into zip file later, if zipping.
- TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout)));
- // TODO: why not write to a file instead of stdout to overcome this problem?
- /* TODO: rather than generating a text file now and zipping it later,
- it would be more efficient to redirect stdout to the zip entry
- directly, but the libziparchive doesn't support that option yet. */
- if (!redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()))) {
- return ERROR;
- }
- if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
- MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
- tmp_path_.c_str(), strerror(errno));
- }
+ // Redirect stdout to tmp_path_. This is the main bugreport entry and will be
+ // moved into zip file later, if zipping.
+ TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout)));
+ // TODO: why not write to a file instead of stdout to overcome this problem?
+ /* TODO: rather than generating a text file now and zipping it later,
+ it would be more efficient to redirect stdout to the zip entry
+ directly, but the libziparchive doesn't support that option yet. */
+ if (!redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()))) {
+ return ERROR;
+ }
+ if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
+ MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
+ tmp_path_.c_str(), strerror(errno));
}
// 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.
PrintHeader();
- // TODO(b/158737089) reduce code repetition in if branches
- if (options_->telephony_only) {
- MaybeTakeEarlyScreenshot();
- onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
- MaybeCheckUserConsent(calling_uid, calling_package);
- DumpstateTelephonyOnly(calling_package);
- DumpstateBoard();
- } else if (options_->wifi_only) {
- MaybeTakeEarlyScreenshot();
- onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
- MaybeCheckUserConsent(calling_uid, calling_package);
- DumpstateWifiOnly();
- } else if (options_->limited_only) {
- MaybeTakeEarlyScreenshot();
- onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
- MaybeCheckUserConsent(calling_uid, calling_package);
- DumpstateLimitedOnly();
- } else {
+ bool is_dumpstate_restricted = options_->telephony_only
+ || options_->wifi_only
+ || options_->limited_only;
+ if (!is_dumpstate_restricted) {
// Invoke critical dumpsys first to preserve system state, before doing anything else.
RunDumpsysCritical();
-
- // Take screenshot and get consent only after critical dumpsys has finished.
- MaybeTakeEarlyScreenshot();
- onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
- MaybeCheckUserConsent(calling_uid, calling_package);
-
+ }
+ MaybeTakeEarlyScreenshot();
+ onUiIntensiveBugreportDumpsFinished(calling_uid);
+ MaybeCheckUserConsent(calling_uid, calling_package);
+ if (options_->telephony_only) {
+ DumpstateTelephonyOnly(calling_package);
+ } else if (options_->wifi_only) {
+ DumpstateWifiOnly();
+ } else if (options_->limited_only) {
+ DumpstateLimitedOnly();
+ } else {
// Dump state for the default case. This also drops root.
RunStatus s = DumpstateDefaultAfterCritical();
if (s != RunStatus::OK) {
@@ -2759,14 +2906,10 @@
}
/* close output if needed */
- if (is_redirecting) {
- TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
- }
+ TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
// Zip the (now complete) .tmp file within the internal directory.
- if (options_->OutputToFile()) {
- FinalizeFile();
- }
+ FinalizeFile();
// Share the final file with the caller if the user has consented or Shell is the caller.
Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
@@ -2809,11 +2952,9 @@
progress_->Save();
MYLOGI("done (id %d)\n", id_);
- if (is_redirecting) {
- TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr)));
- }
+ TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr)));
- if (options_->use_control_socket && control_socket_fd_ != -1) {
+ if (control_socket_fd_ != -1) {
MYLOGD("Closing control socket\n");
close(control_socket_fd_);
}
@@ -2835,16 +2976,14 @@
TakeScreenshot();
}
-void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid,
- const std::string& calling_package) {
+void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
if (calling_uid == AID_SHELL || !CalledByApi()) {
return;
}
if (listener_ != nullptr) {
// Let listener know ui intensive bugreport dumps are finished, then it can do event
// handling if required.
- android::String16 package(calling_package.c_str());
- listener_->onUiIntensiveBugreportDumpsFinished(package);
+ listener_->onUiIntensiveBugreportDumpsFinished();
}
}
@@ -2883,6 +3022,42 @@
android::os::UnlinkAndLogOnError(path_);
}
+void Dumpstate::EnableParallelRunIfNeeded() {
+ if (!PropertiesHelper::IsParallelRun()) {
+ return;
+ }
+ dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
+ zip_entry_tasks_ = std::make_unique<TaskQueue>();
+}
+
+void Dumpstate::ShutdownDumpPool() {
+ if (dump_pool_) {
+ dump_pool_->shutdown();
+ dump_pool_ = nullptr;
+ }
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/* do_cancel = */true);
+ zip_entry_tasks_ = nullptr;
+ }
+}
+
+void Dumpstate::EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name,
+ const std::string& entry_path) {
+ auto func_add_zip_entry_and_cleanup = [=](bool task_cancelled) {
+ if (!task_cancelled) {
+ AddZipEntry(entry_name, entry_path);
+ }
+ android::os::UnlinkAndLogOnError(entry_path);
+ };
+ if (zip_entry_tasks_) {
+ // Enqueues AddZipEntryAndCleanup function if the parallel run is enabled.
+ zip_entry_tasks_->add(func_add_zip_entry_and_cleanup, _1);
+ } else {
+ // Invokes AddZipEntryAndCleanup immediately
+ std::invoke(func_add_zip_entry_and_cleanup, /* task_cancelled = */false);
+ }
+}
+
Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
MYLOGD("User denied consent; deleting files and returning\n");
CleanupTmpFiles();
@@ -2993,7 +3168,8 @@
options_(new Dumpstate::DumpOptions()),
last_reported_percent_progress_(0),
version_(version),
- now_(time(nullptr)) {
+ now_(time(nullptr)),
+ open_socket_fn_(open_socket) {
}
Dumpstate& Dumpstate::GetInstance() {
@@ -3001,8 +3177,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();
}
@@ -3016,7 +3193,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());
}
}
}
@@ -3602,10 +3780,11 @@
}
int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
- const CommandOptions& options, bool verbose_duration) {
- DurationReporter duration_reporter(title, false /* logcat_only */, verbose_duration);
+ const CommandOptions& options, bool verbose_duration, int out_fd) {
+ DurationReporter duration_reporter(title, false /* logcat_only */,
+ verbose_duration, out_fd);
- int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options);
+ int status = RunCommandToFd(out_fd, title, full_command, options);
/* TODO: for now we're simplifying the progress calculation by using the
* timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
@@ -3617,14 +3796,14 @@
}
void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
- const CommandOptions& options, long dumpsysTimeoutMs) {
+ const CommandOptions& options, long dumpsysTimeoutMs, int out_fd) {
long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs();
std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)};
dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());
- RunCommand(title, dumpsys, options);
+ RunCommand(title, dumpsys, options, false, out_fd);
}
-int open_socket(const char *service) {
+static int open_socket(const char* service) {
int s = android_get_control_socket(service);
if (s < 0) {
MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno));
@@ -3659,19 +3838,6 @@
return fd;
}
-/* redirect output to a service control socket */
-bool redirect_to_socket(FILE* redirect, const char* service) {
- int fd = open_socket(service);
- if (fd == -1) {
- return false;
- }
- fflush(redirect);
- // TODO: handle dup2 failure
- TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect)));
- close(fd);
- return true;
-}
-
// TODO: should call is_valid_output_file and/or be merged into it.
void create_parent_dirs(const char *path) {
char *chp = const_cast<char *> (path);
@@ -3744,12 +3910,16 @@
fclose(fp);
}
-// TODO: make this function thread safe if sections are generated in parallel.
void Dumpstate::UpdateProgress(int32_t delta_sec) {
if (progress_ == nullptr) {
MYLOGE("UpdateProgress: progress_ not set\n");
return;
}
+ // This function updates progress related members of the dumpstate and reports
+ // progress percentage to the bugreport client. Since it could be called by
+ // different dump tasks at the same time if the parallel run is enabled, a
+ // mutex lock is necessary here to synchronize the call.
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
// Always update progess so stats can be tuned...
progress_->Inc(delta_sec);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0d25d30..255243f 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -35,6 +35,8 @@
#include <ziparchive/zip_writer.h>
#include "DumpstateUtil.h"
+#include "DumpPool.h"
+#include "TaskQueue.h"
// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
// std::vector<std::string>
@@ -52,6 +54,7 @@
class DumpstateTest;
class ProgressTest;
+class ZippedBugReportStreamTest;
} // namespace dumpstate
} // namespace os
@@ -75,7 +78,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 +87,7 @@
bool logcat_only_;
bool verbose_;
uint64_t started_;
+ int duration_fd_;
DISALLOW_COPY_AND_ASSIGN(DurationReporter);
};
@@ -193,7 +197,8 @@
* 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;
+ friend class android::os::dumpstate::ZippedBugReportStreamTest;
public:
enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
@@ -227,11 +232,13 @@
* |full_command| array containing the command (first entry) and its arguments.
* Must contain at least one element.
* |options| optional argument defining the command's behavior.
+ * |out_fd| A fd to support the DumpPool to output results to a temporary
+ * file. Using STDOUT_FILENO if it's not running in the parallel task.
*/
int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
const android::os::dumpstate::CommandOptions& options =
android::os::dumpstate::CommandOptions::DEFAULT,
- bool verbose_duration = false);
+ bool verbose_duration = false, int out_fd = STDOUT_FILENO);
/*
* Runs `dumpsys` with the given arguments, automatically setting its timeout
@@ -244,10 +251,12 @@
* |options| optional argument defining the command's behavior.
* |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -T` (otherwise it uses the
* timeout from `options`)
+ * |out_fd| A fd to support the DumpPool to output results to a temporary
+ * file. Using STDOUT_FILENO if it's not running in the parallel task.
*/
void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS,
- long dumpsys_timeout_ms = 0);
+ long dumpsys_timeout_ms = 0, int out_fd = STDOUT_FILENO);
/*
* Prints the contents of a file.
@@ -304,7 +313,12 @@
// Returns OK in all other cases.
RunStatus DumpTraces(const char** path);
- void DumpstateBoard();
+ /*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+ void DumpstateBoard(int out_fd = STDOUT_FILENO);
/*
* Updates the overall progress of the bugreport generation by the given weight increment.
@@ -361,20 +375,30 @@
bool CalledByApi() const;
/*
+ * Enqueues a task to the dumpstate's TaskQueue if the parallel run is enabled,
+ * otherwise invokes it immediately. The task adds file at path entry_path
+ * as a zip file entry with name entry_name. Unlinks entry_path when done.
+ *
+ * All enqueued tasks will be executed in the dumpstate's FinishZipFile method
+ * before the zip file is finished. Tasks will be cancelled in dumpstate's
+ * ShutdownDumpPool method if they have never been called.
+ */
+ void EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name,
+ const std::string& entry_path);
+
+ /*
* Structure to hold options that determine the behavior of dumpstate.
*/
struct DumpOptions {
- bool do_add_date = false;
- bool do_zip_file = false;
bool do_vibrate = true;
- // Writes bugreport content to a socket; only flatfile format is supported.
- bool use_socket = false;
- bool use_control_socket = false;
+ // Writes bugreport zipped file to a socket.
+ bool stream_to_socket = false;
+ // Writes generation progress updates to a socket.
+ bool progress_updates_to_socket = false;
bool do_screenshot = false;
bool is_screenshot_copied = false;
bool is_remote_mode = false;
bool show_header_only = false;
- bool do_start_service = false;
bool telephony_only = false;
bool wifi_only = false;
// Trimmed-down version of dumpstate to only include whitelisted logs.
@@ -411,13 +435,6 @@
/* Returns true if the options set so far are consistent. */
bool ValidateOptions() const;
- /* Returns if options specified require writing bugreport to a file */
- bool OutputToFile() const {
- // If we are not writing to socket, we will write to a file. If bugreport_fd is
- // specified, it is preferred. If not bugreport is written to /bugreports.
- return !use_socket;
- }
-
/* Returns if options specified require writing to custom file location */
bool OutputToCustomFile() {
// Custom location is only honored in limited mode.
@@ -443,7 +460,8 @@
std::unique_ptr<Progress> progress_;
- // When set, defines a socket file-descriptor use to report progress to bugreportz.
+ // When set, defines a socket file-descriptor use to report progress to bugreportz
+ // or to stream the zipped file to.
int control_socket_fd_ = -1;
// Bugreport format version;
@@ -455,8 +473,8 @@
// `bugreport-BUILD_ID`.
std::string base_name_;
- // Name is the suffix part of the bugreport files - it's typically the date (when invoked with
- // `-d`), but it could be changed by the user..
+ // Name is the suffix part of the bugreport files - it's typically the date,
+ // but it could be changed by the user..
std::string name_;
std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY;
@@ -490,6 +508,13 @@
// List of open ANR dump files.
std::vector<DumpData> anr_data_;
+ // A thread pool to execute dump tasks simultaneously if the parallel run is enabled.
+ std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_;
+
+ // A task queue to collect adding zip entry tasks inside dump tasks if the
+ // parallel run is enabled.
+ std::unique_ptr<android::os::dumpstate::TaskQueue> zip_entry_tasks_;
+
// A callback to IncidentCompanion service, which checks user consent for sharing the
// bugreport with the calling app. If the user has not responded yet to the dialog it will
// be neither confirmed nor denied.
@@ -519,8 +544,7 @@
void MaybeTakeEarlyScreenshot();
- void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid,
- const std::string& calling_package);
+ void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
void MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package);
@@ -528,17 +552,25 @@
// 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
// called by Shell.
RunStatus CopyBugreportIfUserConsented(int32_t calling_uid);
+ std::function<int(const char *)> open_socket_fn_;
+
// Used by GetInstance() only.
explicit Dumpstate(const std::string& version = VERSION_CURRENT);
android::sp<ConsentCallback> consent_callback_;
+ std::recursive_mutex mutex_;
+
DISALLOW_COPY_AND_ASSIGN(Dumpstate);
};
@@ -565,16 +597,6 @@
int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
int (*dump_from_fd)(const char* title, const char* path, int fd));
-/** opens a socket and returns its file descriptor */
-int open_socket(const char *service);
-
-/*
- * Redirects 'redirect' to a service control socket.
- *
- * Returns true if redirect succeeds.
- */
-bool redirect_to_socket(FILE* redirect, const char* service);
-
/*
* Redirects 'redirect' to a file indicated by 'path', truncating it.
*
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index e491a4b..a80da4e 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -11,7 +11,7 @@
# dumpstatez generates a zipped bugreport but also uses a socket to print the file location once
# it is finished.
-service dumpstatez /system/bin/dumpstate -S -d -z
+service dumpstatez /system/bin/dumpstate -S
socket dumpstate stream 0660 shell log
class main
disabled
diff --git a/cmds/dumpstate/dumpstate_smoke_test.xml b/cmds/dumpstate/dumpstate_smoke_test.xml
new file mode 100644
index 0000000..0aff200
--- /dev/null
+++ b/cmds/dumpstate/dumpstate_smoke_test.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for dumpstate_smoke_test">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="dumpstate_smoke_test->/data/local/tmp/dumpstate_smoke_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="dumpstate_smoke_test" />
+ <option name="native-test-timeout" value="600000" />
+ </test>
+</configuration>
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 6f2d754..0e366cb 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -173,12 +173,9 @@
return binder::Status::ok();
}
- binder::Status onUiIntensiveBugreportDumpsFinished(const android::String16& callingpackage)
- override {
+ binder::Status onUiIntensiveBugreportDumpsFinished() override {
std::lock_guard <std::mutex> lock(lock_);
- std::string callingpackageUtf8 = std::string(String8(callingpackage).string());
- dprintf(out_fd_, "\rCalling package of ui intensive bugreport dumps finished: %s",
- callingpackageUtf8.c_str());
+ dprintf(out_fd_, "\rUi intensive bugreport dumps finished");
return binder::Status::ok();
}
@@ -209,13 +206,10 @@
static std::shared_ptr<std::vector<SectionInfo>> sections;
static Dumpstate& ds;
static std::chrono::milliseconds duration;
- static void SetUpTestCase() {
+ static void GenerateBugreport() {
// clang-format off
char* argv[] = {
- (char*)"dumpstate",
- (char*)"-d",
- (char*)"-z",
- (char*)"-B"
+ (char*)"dumpstate"
};
// clang-format on
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
@@ -236,20 +230,20 @@
std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
+ GenerateBugreport();
EXPECT_EQ(access(getZipFilePath().c_str(), F_OK), 0);
}
-TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) {
+TEST_F(ZippedBugreportGenerationTest, Is1MBMBinSize) {
struct stat st;
EXPECT_EQ(stat(getZipFilePath().c_str(), &st), 0);
- EXPECT_GE(st.st_size, 3000000 /* 3MB */);
- EXPECT_LE(st.st_size, 30000000 /* 30MB */);
+ EXPECT_GE(st.st_size, 1000000 /* 1MB */);
}
-TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) {
+TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) {
EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
<< duration.count() << " s.";
- EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time "
+ EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
<< duration.count() << " s.";
}
@@ -266,7 +260,8 @@
CloseArchive(handle);
}
- void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
+ void FileExists(const char* filename, uint32_t minsize,
+ uint32_t maxsize = std::numeric_limits<uint32_t>::max()) {
ZipEntry entry;
GetEntry(handle, filename, &entry);
EXPECT_GT(entry.uncompressed_length, minsize);
@@ -285,7 +280,7 @@
main_entry.uncompressed_length);
// contains main entry file
- FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U);
+ FileExists(bugreport_txt_name.c_str(), 1000000U);
}
TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
@@ -301,8 +296,9 @@
}
TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
- FileExists("dumpstate_board.bin", 1000000U, 80000000U);
- FileExists("dumpstate_board.txt", 100000U, 1000000U);
+ // TODO(b/160109027): cf_x86_phone-userdebug does not dump them.
+ // FileExists("dumpstate_board.bin", 1000000U, 80000000U);
+ // FileExists("dumpstate_board.txt", 100000U, 1000000U);
}
TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
@@ -323,6 +319,16 @@
*/
class BugreportSectionTest : public Test {
public:
+ ZipArchiveHandle handle;
+
+ void SetUp() {
+ ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath().c_str(), &handle), 0);
+ }
+
+ void TearDown() {
+ CloseArchive(handle);
+ }
+
static void SetUpTestCase() {
ParseSections(ZippedBugreportGenerationTest::getZipFilePath().c_str(),
ZippedBugreportGenerationTest::sections.get());
@@ -347,6 +353,19 @@
}
FAIL() << sectionName << " not found.";
}
+
+ /**
+ * Whether or not the content of the section is injected by other commands.
+ */
+ bool IsContentInjectedByOthers(const std::string& line) {
+ // Command header such as `------ APP ACTIVITIES (/system/bin/dumpsys activity -v) ------`.
+ static const std::regex kCommandHeader = std::regex{"------ .+ \\(.+\\) ------"};
+ std::smatch match;
+ if (std::regex_match(line, match, kCommandHeader)) {
+ return true;
+ }
+ return false;
+ }
};
TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
@@ -400,10 +419,32 @@
SectionExists("batterystats", /* bytes= */ 1000);
}
-TEST_F(BugreportSectionTest, WifiSectionGenerated) {
+TEST_F(BugreportSectionTest, DISABLED_WifiSectionGenerated) {
SectionExists("wifi", /* bytes= */ 100000);
}
+TEST_F(BugreportSectionTest, NoInjectedContentByOtherCommand) {
+ // Extract the main entry to a temp file
+ TemporaryFile tmp_binary;
+ ASSERT_NE(-1, tmp_binary.fd);
+ ExtractBugreport(&handle, tmp_binary.fd);
+
+ // Read line by line and identify sections
+ std::ifstream ifs(tmp_binary.path, std::ifstream::in);
+ std::string line;
+ std::string current_section_name;
+ while (std::getline(ifs, line)) {
+ std::string section_name;
+ if (IsSectionStart(line, §ion_name)) {
+ current_section_name = section_name;
+ } else if (IsSectionEnd(line)) {
+ current_section_name = "";
+ } else if (!current_section_name.empty()) {
+ EXPECT_FALSE(IsContentInjectedByOthers(line));
+ }
+ }
+}
+
class DumpstateBinderTest : public Test {
protected:
void SetUp() override {
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index c7df1bb..b2518ad 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-#define LOG_TAG "dumpstate"
-#include <cutils/log.h>
+#define LOG_TAG "dumpstate_test"
#include "DumpstateInternal.h"
#include "DumpstateService.h"
#include "android/os/BnDumpstate.h"
#include "dumpstate.h"
+#include "DumpPool.h"
#include <gmock/gmock.h>
+#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>
#include <fcntl.h>
@@ -38,7 +39,9 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/hardware/dumpstate/1.1/types.h>
+#include <cutils/log.h>
#include <cutils/properties.h>
+#include <ziparchive/zip_archive.h>
namespace android {
namespace os {
@@ -46,6 +49,7 @@
using ::android::hardware::dumpstate::V1_1::DumpstateMode;
using ::testing::EndsWith;
+using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::IsEmpty;
using ::testing::IsNull;
@@ -66,8 +70,7 @@
MOCK_METHOD1(onError, binder::Status(int32_t error_code));
MOCK_METHOD0(onFinished, binder::Status());
MOCK_METHOD1(onScreenshotTaken, binder::Status(bool success));
- MOCK_METHOD1(onUiIntensiveBugreportDumpsFinished,
- binder::Status(const android::String16& callingpackage));
+ MOCK_METHOD0(onUiIntensiveBugreportDumpsFinished, binder::Status());
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
@@ -95,6 +98,10 @@
PropertiesHelper::unroot_ = unroot;
}
+ void SetParallelRun(bool parallel_run) const {
+ PropertiesHelper::parallel_run_ = parallel_run;
+ }
+
bool IsStandalone() const {
return calls_ == 1;
}
@@ -170,11 +177,9 @@
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
- EXPECT_FALSE(options_.do_add_date);
- EXPECT_FALSE(options_.do_zip_file);
EXPECT_EQ("", options_.out_dir);
- EXPECT_FALSE(options_.use_socket);
- EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.stream_to_socket);
+ EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.show_header_only);
EXPECT_TRUE(options_.do_vibrate);
EXPECT_FALSE(options_.do_screenshot);
@@ -189,17 +194,13 @@
char* argv[] = {
const_cast<char*>("dumpstatez"),
const_cast<char*>("-S"),
- const_cast<char*>("-d"),
- const_cast<char*>("-z"),
};
// clang-format on
Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
- EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_zip_file);
- EXPECT_TRUE(options_.use_control_socket);
+ EXPECT_TRUE(options_.progress_updates_to_socket);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
@@ -207,7 +208,7 @@
EXPECT_FALSE(options_.do_screenshot);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
- EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.stream_to_socket);
EXPECT_FALSE(options_.limited_only);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
@@ -223,13 +224,11 @@
Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
- EXPECT_TRUE(options_.use_socket);
+ EXPECT_TRUE(options_.stream_to_socket);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
- EXPECT_FALSE(options_.do_add_date);
- EXPECT_FALSE(options_.do_zip_file);
- EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_FALSE(options_.do_progress_updates);
@@ -240,108 +239,93 @@
TEST_F(DumpOptionsTest, InitializeFullBugReport) {
options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true);
- EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_screenshot);
- EXPECT_TRUE(options_.do_zip_file);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
- EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
- EXPECT_FALSE(options_.use_socket);
- EXPECT_FALSE(options_.do_start_service);
+ EXPECT_FALSE(options_.stream_to_socket);
EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true);
- EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
- EXPECT_TRUE(options_.do_start_service);
EXPECT_TRUE(options_.do_screenshot);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
- EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.is_remote_mode);
- EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.stream_to_socket);
EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd, false);
- EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.is_remote_mode);
EXPECT_FALSE(options_.do_vibrate);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::REMOTE);
// Other options retain default values
- EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.do_progress_updates);
- EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.stream_to_socket);
EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializeWearBugReport) {
options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true);
- EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_screenshot);
- EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
- EXPECT_TRUE(options_.do_start_service);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
- EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.is_remote_mode);
- EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.stream_to_socket);
EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd, false);
- EXPECT_TRUE(options_.do_add_date);
EXPECT_FALSE(options_.do_screenshot);
- EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.telephony_only);
EXPECT_TRUE(options_.do_progress_updates);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::CONNECTIVITY);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
- EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.is_remote_mode);
- EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.stream_to_socket);
EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false);
- EXPECT_TRUE(options_.do_add_date);
EXPECT_FALSE(options_.do_screenshot);
- EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.wifi_only);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
- EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
- EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.stream_to_socket);
EXPECT_FALSE(options_.limited_only);
}
@@ -350,8 +334,6 @@
char* argv[] = {
const_cast<char*>("dumpstatez"),
const_cast<char*>("-S"),
- const_cast<char*>("-d"),
- const_cast<char*>("-z"),
const_cast<char*>("-q"),
const_cast<char*>("-L"),
const_cast<char*>("-o abc")
@@ -361,9 +343,7 @@
Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
- EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_zip_file);
- EXPECT_TRUE(options_.use_control_socket);
+ EXPECT_TRUE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.do_vibrate);
EXPECT_TRUE(options_.limited_only);
EXPECT_EQ(" abc", std::string(options_.out_dir));
@@ -373,7 +353,7 @@
EXPECT_FALSE(options_.do_screenshot);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
- EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.stream_to_socket);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
@@ -390,18 +370,16 @@
Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
- EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_screenshot);
- EXPECT_TRUE(options_.do_zip_file);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
- EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
- EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.stream_to_socket);
EXPECT_FALSE(options_.wifi_only);
EXPECT_FALSE(options_.limited_only);
}
@@ -410,8 +388,6 @@
// clang-format off
char* argv[] = {
const_cast<char*>("dumpstate"),
- const_cast<char*>("-d"),
- const_cast<char*>("-z"),
const_cast<char*>("-s"),
const_cast<char*>("-S"),
@@ -421,11 +397,9 @@
Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
- EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_zip_file);
// TODO: Maybe we should trim the filename
- EXPECT_TRUE(options_.use_socket);
- EXPECT_TRUE(options_.use_control_socket);
+ EXPECT_TRUE(options_.stream_to_socket);
+ EXPECT_TRUE(options_.progress_updates_to_socket);
// Other options retain default values
EXPECT_FALSE(options_.show_header_only);
@@ -459,10 +433,8 @@
EXPECT_TRUE(options_.is_remote_mode);
// Other options retain default values
- EXPECT_FALSE(options_.do_add_date);
- EXPECT_FALSE(options_.do_zip_file);
- EXPECT_FALSE(options_.use_socket);
- EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.stream_to_socket);
+ EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.limited_only);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
@@ -495,40 +467,31 @@
EXPECT_EQ(status, Dumpstate::RunStatus::INVALID_INPUT);
}
-TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile1) {
- options_.do_zip_file = true;
- // Writing to socket = !writing to file.
- options_.use_socket = true;
+TEST_F(DumpOptionsTest, ValidateOptionsSocketUsage1) {
+ options_.progress_updates_to_socket = true;
+ options_.stream_to_socket = true;
EXPECT_FALSE(options_.ValidateOptions());
- options_.use_socket = false;
+ options_.stream_to_socket = false;
EXPECT_TRUE(options_.ValidateOptions());
}
-TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile2) {
+TEST_F(DumpOptionsTest, ValidateOptionsSocketUsage2) {
options_.do_progress_updates = true;
// Writing to socket = !writing to file.
- options_.use_socket = true;
+ options_.stream_to_socket = true;
EXPECT_FALSE(options_.ValidateOptions());
- options_.use_socket = false;
- EXPECT_TRUE(options_.ValidateOptions());
-}
-
-TEST_F(DumpOptionsTest, ValidateOptionsNeedZipfile) {
- options_.use_control_socket = true;
- EXPECT_FALSE(options_.ValidateOptions());
-
- options_.do_zip_file = true;
+ options_.stream_to_socket = false;
EXPECT_TRUE(options_.ValidateOptions());
}
TEST_F(DumpOptionsTest, ValidateOptionsRemoteMode) {
+ options_.do_progress_updates = true;
options_.is_remote_mode = true;
EXPECT_FALSE(options_.ValidateOptions());
- options_.do_zip_file = true;
- options_.do_add_date = true;
+ options_.do_progress_updates = false;
EXPECT_TRUE(options_.ValidateOptions());
}
@@ -542,6 +505,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 +536,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 +978,83 @@
ds.listener_.clear();
}
+TEST_F(DumpstateTest, DumpPool_withParallelRunEnabled_notNull) {
+ SetParallelRun(true);
+ EnableParallelRunIfNeeded();
+ EXPECT_TRUE(ds.zip_entry_tasks_);
+ EXPECT_TRUE(ds.dump_pool_);
+}
+
+TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) {
+ SetParallelRun(false);
+ EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.zip_entry_tasks_);
+ EXPECT_FALSE(ds.dump_pool_);
+}
+
+class ZippedBugReportStreamTest : public DumpstateBaseTest {
+ public:
+ void SetUp() {
+ DumpstateBaseTest::SetUp();
+ ds_.options_.reset(new Dumpstate::DumpOptions());
+ }
+ void TearDown() {
+ CloseArchive(handle_);
+ }
+
+ // Set bugreport mode and options before here.
+ void GenerateBugreport() {
+ ds_.Initialize();
+ EXPECT_EQ(Dumpstate::RunStatus::OK, ds_.Run(/*calling_uid=*/-1, /*calling_package=*/""));
+ }
+
+ // Most bugreports droproot, ensure the file can be opened by shell to verify file content.
+ void CreateFd(const std::string& path, android::base::unique_fd* out_fd) {
+ out_fd->reset(TEMP_FAILURE_RETRY(open(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 " << path;
+ }
+
+ void VerifyEntry(const ZipArchiveHandle archive, const std::string_view entry_name,
+ ZipEntry* data) {
+ int32_t e = FindEntry(archive, entry_name, data);
+ EXPECT_EQ(0, e) << ErrorCodeString(e) << " entry name: " << entry_name;
+ }
+
+ // While testing dumpstate in process, using STDOUT may get confused about
+ // the internal fd redirection. Redirect to a dedicate fd to save content.
+ void RedirectOutputToFd(android::base::unique_fd& ufd) {
+ ds_.open_socket_fn_ = [&](const char*) -> int { return ufd.release(); };
+ };
+
+ Dumpstate& ds_ = Dumpstate::GetInstance();
+ ZipArchiveHandle handle_;
+};
+
+// Generate a quick wifi report redirected to a file, open it and verify entry exist.
+TEST_F(ZippedBugReportStreamTest, StreamWifiReport) {
+ std::string out_path = kTestDataPath + "out.zip";
+ android::base::unique_fd out_fd;
+ CreateFd(out_path, &out_fd);
+ ds_.options_->wifi_only = true;
+ ds_.options_->stream_to_socket = true;
+ RedirectOutputToFd(out_fd);
+
+ GenerateBugreport();
+ OpenArchive(out_path.c_str(), &handle_);
+
+ ZipEntry entry;
+ VerifyEntry(handle_, "main_entry.txt", &entry);
+ std::string bugreport_txt_name;
+ bugreport_txt_name.resize(entry.uncompressed_length);
+ ExtractToMemory(handle_, &entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
+ entry.uncompressed_length);
+ EXPECT_THAT(bugreport_txt_name,
+ testing::ContainsRegex("(bugreport-.+-wifi(-[[:digit:]]+){6}\\.txt)"));
+ VerifyEntry(handle_, bugreport_txt_name, &entry);
+}
+
class DumpstateServiceTest : public DumpstateBaseTest {
public:
DumpstateService dss;
@@ -1618,6 +1666,154 @@
EXPECT_THAT(out, EndsWith("skipped on dry run\n"));
}
+class DumpPoolTest : public DumpstateBaseTest {
+ public:
+ void SetUp() {
+ dump_pool_ = std::make_unique<DumpPool>(kTestDataPath);
+ DumpstateBaseTest::SetUp();
+ CreateOutputFile();
+ }
+
+ void CreateOutputFile() {
+ out_path_ = kTestDataPath + "out.txt";
+ out_fd_.reset(TEMP_FAILURE_RETRY(open(out_path_.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+ ASSERT_GE(out_fd_.get(), 0) << "could not create FD for path "
+ << out_path_;
+ }
+
+ int getTempFileCounts(const std::string& folder) {
+ int count = 0;
+ std::unique_ptr<DIR, decltype(&closedir)> dir_ptr(opendir(folder.c_str()),
+ &closedir);
+ if (!dir_ptr) {
+ return -1;
+ }
+ int dir_fd = dirfd(dir_ptr.get());
+ if (dir_fd < 0) {
+ return -1;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir_ptr.get()))) {
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+ std::string file_name(de->d_name);
+ if (file_name.find(DumpPool::PREFIX_TMPFILE_NAME) != 0) {
+ continue;
+ }
+ count++;
+ }
+ return count;
+ }
+
+ void setLogDuration(bool log_duration) {
+ dump_pool_->setLogDuration(log_duration);
+ }
+
+ std::unique_ptr<DumpPool> dump_pool_;
+ android::base::unique_fd out_fd_;
+ std::string out_path_;
+};
+
+TEST_F(DumpPoolTest, EnqueueTaskWithFd) {
+ auto dump_func_1 = [](int out_fd) {
+ dprintf(out_fd, "A");
+ };
+ auto dump_func_2 = [](int out_fd) {
+ dprintf(out_fd, "B");
+ sleep(1);
+ };
+ auto dump_func_3 = [](int out_fd) {
+ dprintf(out_fd, "C");
+ };
+ setLogDuration(/* log_duration = */false);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"1", dump_func_1, std::placeholders::_1);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"2", dump_func_2, std::placeholders::_1);
+ dump_pool_->enqueueTaskWithFd(/* task_name = */"3", dump_func_3, std::placeholders::_1);
+
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->waitForTask("2", "", out_fd_.get());
+ dump_pool_->waitForTask("3", "", out_fd_.get());
+ dump_pool_->shutdown();
+
+ std::string result;
+ ReadFileToString(out_path_, &result);
+ EXPECT_THAT(result, StrEq("A\nB\nC\n"));
+ EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
+TEST_F(DumpPoolTest, EnqueueTask_withDurationLog) {
+ bool run_1 = false;
+ auto dump_func_1 = [&]() {
+ run_1 = true;
+ };
+
+ dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
+ dump_pool_->waitForTask("1", "", out_fd_.get());
+ dump_pool_->shutdown();
+
+ std::string result;
+ ReadFileToString(out_path_, &result);
+ EXPECT_TRUE(run_1);
+ EXPECT_THAT(result, StrEq("------ 0.000s was the duration of '1' ------\n"));
+ EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
+}
+
+class TaskQueueTest : public DumpstateBaseTest {
+public:
+ void SetUp() {
+ DumpstateBaseTest::SetUp();
+ }
+
+ TaskQueue task_queue_;
+};
+
+TEST_F(TaskQueueTest, runTask) {
+ bool is_task1_run = false;
+ bool is_task2_run = false;
+ auto task_1 = [&](bool task_cancelled) {
+ if (task_cancelled) {
+ return;
+ }
+ is_task1_run = true;
+ };
+ auto task_2 = [&](bool task_cancelled) {
+ if (task_cancelled) {
+ return;
+ }
+ is_task2_run = true;
+ };
+ task_queue_.add(task_1, std::placeholders::_1);
+ task_queue_.add(task_2, std::placeholders::_1);
+
+ task_queue_.run(/* do_cancel = */false);
+
+ EXPECT_TRUE(is_task1_run);
+ EXPECT_TRUE(is_task2_run);
+}
+
+TEST_F(TaskQueueTest, runTask_withCancelled) {
+ bool is_task1_cancelled = false;
+ bool is_task2_cancelled = false;
+ auto task_1 = [&](bool task_cancelled) {
+ is_task1_cancelled = task_cancelled;
+ };
+ auto task_2 = [&](bool task_cancelled) {
+ is_task2_cancelled = task_cancelled;
+ };
+ task_queue_.add(task_1, std::placeholders::_1);
+ task_queue_.add(task_2, std::placeholders::_1);
+
+ task_queue_.run(/* do_cancel = */true);
+
+ EXPECT_TRUE(is_task1_cancelled);
+ EXPECT_TRUE(is_task2_cancelled);
+}
+
+
} // namespace dumpstate
} // namespace os
} // namespace android
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 1327cfd..a017246 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -427,7 +427,7 @@
<< strerror(errno) << std::endl;
status = -errno;
break;
- } else if (rc == 0) {
+ } else if (rc == 0 || time_left_ms() == 0) {
status = TIMED_OUT;
break;
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 6001a58..c55fc6a 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -398,6 +398,10 @@
PLOG(ERROR) << "Failed to prepare " << profile_dir;
return false;
}
+ if (selinux_android_restorecon(profile_dir.c_str(), 0)) {
+ PLOG(ERROR) << "Failed to restorecon " << profile_dir;
+ return false;
+ }
const std::string ref_profile_path =
create_primary_reference_profile_package_dir_path(packageName);
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 5076ae6..65fc46e 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -450,6 +450,26 @@
AddArg("--boot-image-merge");
}
+ // The percent won't exceed 100, otherwise, don't set it and use the
+ // default one set in profman.
+ uint32_t min_new_classes_percent_change = ::android::base::GetUintProperty<uint32_t>(
+ "dalvik.vm.bgdexopt.new-classes-percent",
+ /*default*/std::numeric_limits<uint32_t>::max());
+ if (min_new_classes_percent_change <= 100) {
+ AddArg("--min-new-classes-percent-change=" +
+ std::to_string(min_new_classes_percent_change));
+ }
+
+ // The percent won't exceed 100, otherwise, don't set it and use the
+ // default one set in profman.
+ uint32_t min_new_methods_percent_change = ::android::base::GetUintProperty<uint32_t>(
+ "dalvik.vm.bgdexopt.new-methods-percent",
+ /*default*/std::numeric_limits<uint32_t>::max());
+ if (min_new_methods_percent_change <= 100) {
+ AddArg("--min-new-methods-percent-change=" +
+ std::to_string(min_new_methods_percent_change));
+ }
+
// Do not add after dex2oat_flags, they should override others for debugging.
PrepareArgs(profman_bin);
}
@@ -1798,10 +1818,13 @@
pid_t pid = fork();
if (pid == 0) {
+ // Need to set schedpolicy before dropping privileges
+ // for cgroup migration. See details at b/175178520.
+ SetDex2OatScheduling(boot_complete);
+
/* child -- drop privileges before continuing */
drop_capabilities(uid);
- SetDex2OatScheduling(boot_complete);
if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) {
PLOG(ERROR) << "flock(" << out_oat.path() << ") failed";
_exit(DexoptReturnCodes::kFlock);
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 6459805..72c03bf 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -68,15 +68,15 @@
apex::kApexPackageVendorDir};
for (const auto& dir : apex_dirs) {
// Cast call to void to suppress warn_unused_result.
- static_cast<void>(apex::scanPackagesDirAndActivate(dir));
+ static_cast<void>(apex::ScanPackagesDirAndActivate(dir));
}
- return apex::getActivePackages();
+ return apex::GetActivePackages();
}
static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) {
for (const apex::ApexFile& apex_file : active_packages) {
const std::string& package_path = apex_file.GetPath();
- base::Result<void> status = apex::deactivatePackage(package_path);
+ base::Result<void> status = apex::DeactivatePackage(package_path);
if (!status.ok()) {
LOG(ERROR) << "Failed to deactivate " << package_path << ": "
<< status.error();
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index bd45005..7c9e3b2 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -41,6 +41,21 @@
"liblogwrap",
],
test_config: "installd_cache_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
@@ -66,6 +81,21 @@
"liblogwrap",
],
test_config: "installd_service_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
@@ -93,6 +123,21 @@
"libz",
],
test_config: "installd_dexopt_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index 987adaf..0cbb80f 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -16,6 +16,7 @@
name: "liblshal",
shared_libs: [
"libbase",
+ "libbinderdebug",
"libcutils",
"libutils",
"libhidlbase",
@@ -47,6 +48,7 @@
name: "lshal_defaults",
shared_libs: [
"libbase",
+ "libbinderdebug",
"libcutils",
"libutils",
"libhidlbase",
@@ -72,15 +74,18 @@
cc_test {
name: "lshal_test",
test_suites: ["device-tests"],
- defaults: ["lshal_defaults"],
+ defaults: [
+ "libvintf_static_user_defaults",
+ "lshal_defaults"
+ ],
gtest: true,
static_libs: [
"android.hardware.tests.inheritance@1.0",
"libgmock",
+ "libvintf",
],
shared_libs: [
"libhidlbase",
- "libvintf",
],
srcs: [
"test.cpp"
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 92958d9..22268ac 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -29,7 +29,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/parseint.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <hidl-hash/Hash.h>
#include <hidl-util/FQName.h>
@@ -203,97 +202,14 @@
lshal::getVintfInfo(getFrameworkMatrix(), fqInstance, ta, FRAMEWORK_MATRIX);
}
-static bool scanBinderContext(pid_t pid,
- const std::string &contextName,
- std::function<void(const std::string&)> eachLine) {
- std::ifstream ifs("/dev/binderfs/binder_logs/proc/" + std::to_string(pid));
- if (!ifs.is_open()) {
- ifs.open("/d/binder/proc/" + std::to_string(pid));
- if (!ifs.is_open()) {
- return false;
- }
- }
-
- static const std::regex kContextLine("^context (\\w+)$");
-
- bool isDesiredContext = false;
- std::string line;
- std::smatch match;
- while(getline(ifs, line)) {
- if (std::regex_search(line, match, kContextLine)) {
- isDesiredContext = match.str(1) == contextName;
- continue;
- }
-
- if (!isDesiredContext) {
- continue;
- }
-
- eachLine(line);
- }
- return true;
-}
-
bool ListCommand::getPidInfo(
- pid_t serverPid, PidInfo *pidInfo) const {
- static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
- static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
-
- std::smatch match;
- return scanBinderContext(serverPid, "hwbinder", [&](const std::string& line) {
- if (std::regex_search(line, match, kReferencePrefix)) {
- const std::string &ptrString = "0x" + match.str(2); // use number after c
- uint64_t ptr;
- if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
- // Should not reach here, but just be tolerant.
- err() << "Could not parse number " << ptrString << std::endl;
- return;
- }
- const std::string proc = " proc ";
- auto pos = line.rfind(proc);
- if (pos != std::string::npos) {
- for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
- int32_t pid;
- if (!::android::base::ParseInt(pidStr, &pid)) {
- err() << "Could not parse number " << pidStr << std::endl;
- return;
- }
- pidInfo->refPids[ptr].push_back(pid);
- }
- }
-
- return;
- }
-
- if (std::regex_search(line, match, kThreadPrefix)) {
- // "1" is waiting in binder driver
- // "2" is poll. It's impossible to tell if these are in use.
- // and HIDL default code doesn't use it.
- bool isInUse = match.str(1) != "1";
- // "0" is a thread that has called into binder
- // "1" is looper thread
- // "2" is main looper thread
- bool isHwbinderThread = match.str(2) != "0";
-
- if (!isHwbinderThread) {
- return;
- }
-
- if (isInUse) {
- pidInfo->threadUsage++;
- }
-
- pidInfo->threadCount++;
- return;
- }
-
- // not reference or thread line
- return;
- });
+ pid_t serverPid, BinderPidInfo *pidInfo) const {
+ const auto& status = getBinderPidInfo(BinderDebugContext::HWBINDER, serverPid, pidInfo);
+ return status == OK;
}
-const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) {
- auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}});
+const BinderPidInfo* ListCommand::getPidInfoCached(pid_t serverPid) {
+ auto pair = mCachedPidInfos.insert({serverPid, BinderPidInfo{}});
if (pair.second /* did insertion take place? */) {
if (!getPidInfo(serverPid, &pair.first->second)) {
return nullptr;
@@ -727,7 +643,7 @@
entry->arch = fromBaseArchitecture(debugInfo.arch);
if (debugInfo.pid != NO_PID) {
- const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
+ const BinderPidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
if (pidInfo == nullptr) {
handleError(IO_ERROR,
"no information for PID " + std::to_string(debugInfo.pid) +
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index 412aadd..561f9cb 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -25,6 +25,7 @@
#include <android-base/macros.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <binderdebug/BinderDebug.h>
#include <hidl-util/FqInstance.h>
#include <vintf/HalManifest.h>
#include <vintf/VintfObject.h>
@@ -40,12 +41,6 @@
class Lshal;
-struct PidInfo {
- std::map<uint64_t, Pids> refPids; // pids that are referenced
- uint32_t threadUsage; // number of threads in use
- uint32_t threadCount; // number of threads total
-};
-
enum class HalType {
BINDERIZED_SERVICES = 0,
PASSTHROUGH_CLIENTS,
@@ -110,9 +105,9 @@
// Get relevant information for a PID by parsing files under
// /dev/binderfs/binder_logs or /d/binder.
// It is a virtual member function so that it can be mocked.
- virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const;
+ virtual bool getPidInfo(pid_t serverPid, BinderPidInfo *info) const;
// Retrieve from mCachedPidInfos and call getPidInfo if necessary.
- const PidInfo* getPidInfoCached(pid_t serverPid);
+ const BinderPidInfo* getPidInfoCached(pid_t serverPid);
void dumpTable(const NullableOStream<std::ostream>& out) const;
void dumpVintf(const NullableOStream<std::ostream>& out) const;
@@ -191,7 +186,7 @@
std::map<pid_t, std::string> mCmdlines;
// Cache for getPidInfo.
- std::map<pid_t, PidInfo> mCachedPidInfos;
+ std::map<pid_t, BinderPidInfo> mCachedPidInfos;
// Cache for getPartition.
std::map<pid_t, Partition> mPartitions;
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index 3c36813..476aa04 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -32,7 +32,7 @@
namespace lshal {
using android::procpartition::Partition;
-using Pids = std::vector<int32_t>;
+using Pids = std::vector<pid_t>;
enum class TableColumnType : unsigned int {
INTERFACE_NAME = 0,
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index ba6cdf1..b6ff28d 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -233,12 +233,12 @@
return ListCommand::dumpVintf(out);
}
void internalPostprocess() { ListCommand::postprocess(); }
- const PidInfo* getPidInfoCached(pid_t serverPid) {
+ const BinderPidInfo* getPidInfoCached(pid_t serverPid) {
return ListCommand::getPidInfoCached(serverPid);
}
MOCK_METHOD0(postprocess, void());
- MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*));
+ MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, BinderPidInfo*));
MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t));
MOCK_METHOD1(getPartition, Partition(pid_t));
@@ -299,8 +299,8 @@
static std::vector<pid_t> getClients(pid_t serverId) {
return {serverId + 1, serverId + 3};
}
-static PidInfo getPidInfoFromId(pid_t serverId) {
- PidInfo info;
+static BinderPidInfo getPidInfoFromId(pid_t serverId) {
+ BinderPidInfo info;
info.refPids[getPtr(serverId)] = getClients(serverId);
info.threadUsage = 10 + serverId;
info.threadCount = 20 + serverId;
@@ -363,7 +363,7 @@
void initMockList() {
mockList = std::make_unique<NiceMock<MockListCommand>>(lshal.get());
ON_CALL(*mockList, getPidInfo(_,_)).WillByDefault(Invoke(
- [](pid_t serverPid, PidInfo* info) {
+ [](pid_t serverPid, BinderPidInfo* info) {
*info = getPidInfoFromId(serverPid);
return true;
}));
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 7aac7da..b21010d 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -494,7 +494,7 @@
sp<BpBinder> bpBinder = binder->remoteBinder();
if (bpBinder == nullptr) return -1;
- return ProcessState::self()->getStrongRefCountForNodeByHandle(bpBinder->handle());
+ return ProcessState::self()->getStrongRefCountForNode(bpBinder);
}
void ServiceManager::handleClientCallbacks() {
diff --git a/data/etc/android.hardware.uwb.xml b/data/etc/android.hardware.uwb.xml
new file mode 100644
index 0000000..794ba27
--- /dev/null
+++ b/data/etc/android.hardware.uwb.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<!-- Adds the feature indicating support for the Ultra Wideband API -->
+<permissions>
+ <feature name="android.hardware.uwb" />
+</permissions>
diff --git a/data/etc/android.software.opengles.deqp.level-2020-03-01.xml b/data/etc/android.software.opengles.deqp.level-2020-03-01.xml
new file mode 100644
index 0000000..f11e0bb
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-2020-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device passes OpenGL ES
+ dEQP tests associated with date 2020-03-01 (0x07E40301). -->
+<permissions>
+ <feature name="android.software.opengles.deqp.level" version="132383489" />
+</permissions>
diff --git a/data/etc/android.software.opengles.deqp.level-2021-03-01.xml b/data/etc/android.software.opengles.deqp.level-2021-03-01.xml
new file mode 100644
index 0000000..b60697d
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-2021-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device passes OpenGL ES
+ dEQP tests associated with date 2021-03-01 (0x07E50301). -->
+<permissions>
+ <feature name="android.software.opengles.deqp.level" version="132449025" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml
index 9c67d4a..d3ad45a 100644
--- a/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml
+++ b/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<!-- This is the standard feature indicating that the device passes Vulkan deQP
+<!-- This is the standard feature indicating that the device passes Vulkan dEQP
tests associated with date 2019-03-01 (0x07E30301). -->
<permissions>
<feature name="android.software.vulkan.deqp.level" version="132317953" />
diff --git a/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml
index 19b269b..84ba389 100644
--- a/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml
+++ b/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<!-- This is the standard feature indicating that the device passes Vulkan deQP
+<!-- This is the standard feature indicating that the device passes Vulkan dEQP
tests associated with date 2020-03-01 (0x07E40301). -->
<permissions>
<feature name="android.software.vulkan.deqp.level" version="132383489" />
diff --git a/data/etc/android.software.vulkan.deqp.level-2021-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2021-03-01.xml
new file mode 100644
index 0000000..ae26269
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2021-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan dEQP
+ tests associated with date 2021-03-01 (0x07E50301). -->
+<permissions>
+ <feature name="android.software.vulkan.deqp.level" version="132449025" />
+</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 50f117d..6ffb947 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -38,7 +38,6 @@
<!-- basic system services -->
<feature name="android.software.connectionservice" />
<feature name="android.software.voice_recognizers" notLowRam="true" />
- <feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
diff --git a/data/etc/pc_core_hardware.xml b/data/etc/pc_core_hardware.xml
new file mode 100644
index 0000000..c62da0a
--- /dev/null
+++ b/data/etc/pc_core_hardware.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- These are the hardware components that all handheld devices
+ must include. Devices with optional hardware must also include extra
+ hardware files, per the comments below.
+
+ Handheld devices include phones, mobile Internet devices (MIDs),
+ Personal Media Players (PMPs), small tablets (7" or less), and similar
+ devices.
+-->
+<permissions>
+ <!-- This is Android and fully CTS compatible. Basically this is for CTS tests to use. -->
+ <feature name="android.software.cts" />
+
+ <feature name="android.hardware.audio.output" />
+ <feature name="android.hardware.bluetooth" />
+ <feature name="android.hardware.microphone" />
+ <feature name="android.hardware.screen.portrait" />
+ <feature name="android.hardware.screen.landscape" />
+ <feature name="android.hardware.location" />
+ <feature name="android.hardware.location.network" />
+
+ <!-- basic system services -->
+ <feature name="android.software.app_widgets" />
+ <feature name="android.software.voice_recognizers" />
+ <feature name="android.software.backup" />
+ <feature name="android.software.home_screen" />
+ <feature name="android.software.input_methods" />
+ <feature name="android.software.picture_in_picture" />
+ <feature name="android.software.activities_on_secondary_displays" />
+ <feature name="android.software.print" />
+ <feature name="android.software.companion_device_setup" />
+ <feature name="android.software.autofill" />
+ <feature name="android.software.cant_save_state" />
+ <feature name="android.software.secure_lock_screen" />
+
+ <!-- Feature to specify if the device supports adding device admins. -->
+ <feature name="android.software.device_admin" />
+
+ <!-- Feature to specify if the device support managed users. -->
+ <feature name="android.software.managed_users" />
+
+ <!-- Feature to specify if the device supports controls. -->
+ <feature name="android.software.controls" />
+
+ <!-- Feature to specify if the device supports freeform. -->
+ <feature name="android.software.freeform_window_management" />
+ <feature name="android.hardware.type.pc" />
+</permissions>
\ No newline at end of file
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index dae6eeb..15bd5c3 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -282,9 +282,8 @@
LOG(FATAL) << "adbd_auth: unhandled packet type?";
}
- output_queue_.pop_front();
-
ssize_t rc = writev(framework_fd_.get(), iovs, iovcnt);
+ output_queue_.pop_front();
if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
PLOG(ERROR) << "adbd_auth: failed to write to framework fd";
ReplaceFrameworkFd(unique_fd());
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
index 09a5f39..cdd7764 100644
--- a/libs/android_runtime_lazy/Android.bp
+++ b/libs/android_runtime_lazy/Android.bp
@@ -35,6 +35,11 @@
vendor_available: true,
double_loadable: true,
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
cflags: [
"-Wall",
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index d363ee9..e754d74 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -31,6 +31,11 @@
"libutils_headers",
],
min_sdk_version: "29",
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
// These interfaces are android-specific implementation unrelated to binder
@@ -107,6 +112,7 @@
"Stability.cpp",
"Status.cpp",
"TextOutput.cpp",
+ "Utils.cpp",
":libbinder_aidl",
],
@@ -122,6 +128,9 @@
vendor: {
exclude_srcs: libbinder_device_interface_sources,
},
+ darwin: {
+ enabled: false,
+ },
},
aidl: {
@@ -135,8 +144,10 @@
cflags: [
"-Wall",
"-Wextra",
+ "-Wextra-semi",
"-Werror",
"-Wzero-as-null-pointer-constant",
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
],
product_variables: {
binder32bit: {
@@ -163,6 +174,34 @@
misc_undefined: ["integer"],
},
min_sdk_version: "29",
+
+ tidy: true,
+ tidy_flags: [
+ // Only check our headers
+ "--header-filter=^.*frameworks/native/libs/binder/.*.h$",
+ ],
+ tidy_checks_as_errors: [
+ // Explicitly list the checks that should not occur in this module.
+ "abseil-*",
+ "android-*",
+ "bugprone-*",
+ "cert-*",
+ "clang-analyzer-*",
+ "-clang-analyzer-core.CallAndMessage",
+ "-clang-analyzer-core.uninitialized.Assign",
+ "-clang-analyzer-unix.Malloc",
+ "-clang-analyzer-deadcode.DeadStores",
+ "-clang-analyzer-optin.cplusplus.UninitializedObject",
+ "google-*",
+ "-google-readability-*",
+ "-google-runtime-references",
+ "misc-*",
+ "-misc-no-recursion",
+ "-misc-redundant-expression",
+ "-misc-unused-using-decls",
+ "performance*",
+ "portability*",
+ ],
}
// AIDL interface between libbinder and framework.jar
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 6ca3b16..d964d25 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -133,6 +133,7 @@
public:
// unlocked objects
bool mRequestingSid = false;
+ bool mInheritRt = false;
sp<IBinder> mExtension;
int mPolicy = SCHED_NORMAL;
int mPriority = 0;
@@ -173,6 +174,10 @@
{
data.setDataPosition(0);
+ if (reply != nullptr && (flags & FLAG_CLEAR_BUF)) {
+ reply->markSensitive();
+ }
+
status_t err = NO_ERROR;
switch (code) {
case PING_TRANSACTION:
@@ -323,6 +328,27 @@
return e->mPriority;
}
+bool BBinder::isInheritRt() {
+ Extras* e = mExtras.load(std::memory_order_acquire);
+
+ return e && e->mInheritRt;
+}
+
+void BBinder::setInheritRt(bool inheritRt) {
+ Extras* e = mExtras.load(std::memory_order_acquire);
+
+ if (!e) {
+ if (!inheritRt) {
+ return;
+ }
+
+ e = getOrCreateExtras();
+ if (!e) return; // out of memory
+ }
+
+ e->mInheritRt = inheritRt;
+}
+
pid_t BBinder::getDebugPid() {
return getpid();
}
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index c183d29..bf4387a 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -137,8 +137,8 @@
}
BpBinder::BpBinder(int32_t handle, int32_t trackedUid)
- : mHandle(handle)
- , mStability(0)
+ : mStability(0)
+ , mHandle(handle)
, mAlive(1)
, mObitsSent(0)
, mObituaries(nullptr)
@@ -223,13 +223,14 @@
if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {
using android::internal::Stability;
- auto stability = Stability::get(this);
- auto required = privateVendor ? Stability::VENDOR : Stability::getLocalStability();
+ auto category = Stability::getCategory(this);
+ Stability::Level required = privateVendor ? Stability::VENDOR
+ : Stability::getLocalLevel();
- if (CC_UNLIKELY(!Stability::check(stability, required))) {
+ if (CC_UNLIKELY(!Stability::check(category, required))) {
ALOGE("Cannot do a user transaction on a %s binder in a %s context.",
- Stability::stabilityString(stability).c_str(),
- Stability::stabilityString(required).c_str());
+ category.debugString().c_str(),
+ Stability::levelString(required).c_str());
return BAD_TYPE;
}
}
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 8cf6097..88c85bf 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -18,7 +18,6 @@
#include <binder/Debug.h>
#include <cutils/atomic.h>
-#include <cutils/threads.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
@@ -91,22 +90,6 @@
static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
-static thread_store_t tls;
-
-BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState()
-{
- ThreadState* ts = (ThreadState*) thread_store_get( &tls );
- if (ts) return ts;
- ts = new ThreadState;
- thread_store_set( &tls, ts, threadDestructor );
- return ts;
-}
-
-void BufferedTextOutput::threadDestructor(void *st)
-{
- delete ((ThreadState*)st);
-}
-
static volatile int32_t gSequence = 0;
static volatile int32_t gFreeBufferIndex = -1;
@@ -266,16 +249,14 @@
BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
{
if ((mFlags&MULTITHREADED) != 0) {
- ThreadState* ts = getThreadState();
- if (ts) {
- while (ts->states.size() <= (size_t)mIndex) ts->states.add(nullptr);
- BufferState* bs = ts->states[mIndex].get();
- if (bs != nullptr && bs->seq == mSeq) return bs;
-
- ts->states.editItemAt(mIndex) = new BufferState(mIndex);
- bs = ts->states[mIndex].get();
- if (bs != nullptr) return bs;
- }
+ thread_local ThreadState ts;
+ while (ts.states.size() <= (size_t)mIndex) ts.states.add(nullptr);
+ BufferState* bs = ts.states[mIndex].get();
+ if (bs != nullptr && bs->seq == mSeq) return bs;
+
+ ts.states.editItemAt(mIndex) = new BufferState(mIndex);
+ bs = ts.states[mIndex].get();
+ if (bs != nullptr) return bs;
}
return mGlobalState;
diff --git a/libs/binder/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h
index 1b27bb2..fdd532a 100644
--- a/libs/binder/BufferedTextOutput.h
+++ b/libs/binder/BufferedTextOutput.h
@@ -47,10 +47,7 @@
private:
struct BufferState;
struct ThreadState;
-
- static ThreadState*getThreadState();
- static void threadDestructor(void *st);
-
+
BufferState*getBuffer() const;
uint32_t mFlags;
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index 64c1ff6..3a62059 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -208,7 +208,7 @@
}
for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) {
- long remain = length;
+ ssize_t remain = length;
char* c = buffer;
if (!oneLine && !cStyle) {
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..b81995c 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -108,6 +108,6 @@
// ------------------------------------------------------------------------------------
-IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager");
+IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager")
} // namespace android
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp
index b9eb281..2b3f462 100644
--- a/libs/binder/IAppOpsCallback.cpp
+++ b/libs/binder/IAppOpsCallback.cpp
@@ -43,7 +43,7 @@
}
};
-IMPLEMENT_META_INTERFACE(AppOpsCallback, "com.android.internal.app.IAppOpsCallback");
+IMPLEMENT_META_INTERFACE(AppOpsCallback, "com.android.internal.app.IAppOpsCallback")
// ----------------------------------------------------------------------
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index cd78866..1af5ab8 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -159,7 +159,7 @@
}
};
-IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService");
+IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService")
// ----------------------------------------------------------------------
diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp
index a47dbac..d0085df 100644
--- a/libs/binder/IBatteryStats.cpp
+++ b/libs/binder/IBatteryStats.cpp
@@ -130,7 +130,7 @@
};
-IMPLEMENT_META_INTERFACE(BatteryStats, "com.android.internal.app.IBatteryStats");
+IMPLEMENT_META_INTERFACE(BatteryStats, "com.android.internal.app.IBatteryStats")
// ----------------------------------------------------------------------
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
index 4198e49..f5fa817 100644
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ b/libs/binder/IMediaResourceMonitor.cpp
@@ -38,10 +38,11 @@
}
};
-IMPLEMENT_META_INTERFACE(MediaResourceMonitor, "android.media.IMediaResourceMonitor");
+IMPLEMENT_META_INTERFACE(MediaResourceMonitor, "android.media.IMediaResourceMonitor")
// ----------------------------------------------------------------------
+// NOLINTNEXTLINE(google-default-arguments)
status_t BnMediaResourceMonitor::onTransact( uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) {
switch(code) {
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index d8b44f9..cca8f81 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -223,7 +223,7 @@
// ---------------------------------------------------------------------------
-IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory");
+IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory")
BnMemory::BnMemory() {
}
@@ -388,7 +388,7 @@
// ---------------------------------------------------------------------------
-IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap");
+IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap")
BnMemoryHeap::BnMemoryHeap() {
}
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 05fcc2b..7d01e0b 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -135,7 +135,7 @@
out << "target.ptr=" << btd->target.ptr;
}
out << " (cookie " << btd->cookie << ")" << endl
- << "code=" << TypeCode(btd->code) << ", flags=" << (void*)(long)btd->flags << endl
+ << "code=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << endl
<< "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size
<< " bytes)" << endl
<< "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size
@@ -150,7 +150,7 @@
uint32_t code = (uint32_t)*cmd++;
size_t cmdIndex = code & 0xff;
if (code == BR_ERROR) {
- out << "BR_ERROR: " << (void*)(long)(*cmd++) << endl;
+ out << "BR_ERROR: " << (void*)(uint64_t)(*cmd++) << endl;
return cmd;
} else if (cmdIndex >= N) {
out << "Unknown reply: " << code << endl;
@@ -177,21 +177,21 @@
case BR_DECREFS: {
const int32_t b = *cmd++;
const int32_t c = *cmd++;
- out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c << ")";
+ out << ": target=" << (void*)(uint64_t)b << " (cookie " << (void*)(uint64_t)c << ")";
} break;
case BR_ATTEMPT_ACQUIRE: {
const int32_t p = *cmd++;
const int32_t b = *cmd++;
const int32_t c = *cmd++;
- out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c
+ out << ": target=" << (void*)(uint64_t)b << " (cookie " << (void*)(uint64_t)c
<< "), pri=" << p;
} break;
case BR_DEAD_BINDER:
case BR_CLEAR_DEATH_NOTIFICATION_DONE: {
const int32_t c = *cmd++;
- out << ": death cookie " << (void*)(long)c;
+ out << ": death cookie " << (void*)(uint64_t)c;
} break;
default:
@@ -232,7 +232,7 @@
case BC_FREE_BUFFER: {
const int32_t buf = *cmd++;
- out << ": buffer=" << (void*)(long)buf;
+ out << ": buffer=" << (void*)(uint64_t)buf;
} break;
case BC_INCREFS:
@@ -247,7 +247,7 @@
case BC_ACQUIRE_DONE: {
const int32_t b = *cmd++;
const int32_t c = *cmd++;
- out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c << ")";
+ out << ": target=" << (void*)(uint64_t)b << " (cookie " << (void*)(uint64_t)c << ")";
} break;
case BC_ATTEMPT_ACQUIRE: {
@@ -260,12 +260,12 @@
case BC_CLEAR_DEATH_NOTIFICATION: {
const int32_t h = *cmd++;
const int32_t c = *cmd++;
- out << ": handle=" << h << " (death cookie " << (void*)(long)c << ")";
+ out << ": handle=" << h << " (death cookie " << (void*)(uint64_t)c << ")";
} break;
case BC_DEAD_BINDER_DONE: {
const int32_t c = *cmd++;
- out << ": death cookie " << (void*)(long)c;
+ out << ": death cookie " << (void*)(uint64_t)c;
} break;
default:
@@ -895,21 +895,21 @@
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t),
- freeBuffer, this);
+ freeBuffer);
} else {
err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(nullptr,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(binder_size_t), this);
+ tr.offsets_size/sizeof(binder_size_t));
}
} else {
freeBuffer(nullptr,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(binder_size_t), this);
+ tr.offsets_size/sizeof(binder_size_t));
continue;
}
}
@@ -1077,7 +1077,7 @@
sp<BBinder> the_context_object;
-void IPCThreadState::setTheContextObject(sp<BBinder> obj)
+void IPCThreadState::setTheContextObject(const sp<BBinder>& obj)
{
the_context_object = obj;
}
@@ -1183,7 +1183,7 @@
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
+ tr.offsets_size/sizeof(binder_size_t), freeBuffer);
const void* origServingStackPointer = mServingStackPointer;
mServingStackPointer = &origServingStackPointer; // anything on the stack
@@ -1244,12 +1244,26 @@
if ((tr.flags & TF_ONE_WAY) == 0) {
LOG_ONEWAY("Sending reply to %d!", mCallingPid);
if (error < NO_ERROR) reply.setError(error);
- sendReply(reply, 0);
+
+ constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF;
+ sendReply(reply, (tr.flags & kForwardReplyFlags));
} else {
- if (error != OK || reply.dataSize() != 0) {
- alog << "oneway function results will be dropped but finished with status "
- << statusToString(error)
- << " and parcel size " << reply.dataSize() << endl;
+ if (error != OK) {
+ alog << "oneway function results for code " << tr.code
+ << " on binder at "
+ << reinterpret_cast<void*>(tr.target.ptr)
+ << " will be dropped but finished with status "
+ << statusToString(error);
+
+ // ideally we could log this even when error == OK, but it
+ // causes too much logspam because some manually-written
+ // interfaces have clients that call methods which always
+ // write results, sometimes as oneway methods.
+ if (reply.dataSize() != 0) {
+ alog << " and reply parcel size " << reply.dataSize();
+ }
+
+ alog << endl;
}
LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
}
@@ -1368,7 +1382,7 @@
void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data,
size_t /*dataSize*/,
const binder_size_t* /*objects*/,
- size_t /*objectsSize*/, void* /*cookie*/)
+ size_t /*objectsSize*/)
{
//ALOGI("Freeing parcel %p", &parcel);
IF_LOG_COMMANDS() {
diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp
index d9bf3cc..f94f413 100644
--- a/libs/binder/IPermissionController.cpp
+++ b/libs/binder/IPermissionController.cpp
@@ -103,7 +103,7 @@
}
};
-IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController");
+IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController")
// ----------------------------------------------------------------------
diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index a38a27a..570edb9 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -84,7 +84,7 @@
}
};
-IMPLEMENT_META_INTERFACE(ProcessInfoService, "android.os.IProcessInfoService");
+IMPLEMENT_META_INTERFACE(ProcessInfoService, "android.os.IProcessInfoService")
// ----------------------------------------------------------------------
diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp
index 556288c..cd92217 100644
--- a/libs/binder/IResultReceiver.cpp
+++ b/libs/binder/IResultReceiver.cpp
@@ -42,7 +42,7 @@
}
};
-IMPLEMENT_META_INTERFACE(ResultReceiver, "com.android.internal.os.IResultReceiver");
+IMPLEMENT_META_INTERFACE(ResultReceiver, "com.android.internal.os.IResultReceiver")
// ----------------------------------------------------------------------
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 6d728dc..ca067e2 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -221,7 +221,7 @@
const bool isVendorService =
strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
- const long timeout = 5000;
+ constexpr int64_t timeout = 5000;
int64_t startTime = uptimeMillis();
// Vendor code can't access system properties
if (!gSystemBootCompleted && !isVendorService) {
@@ -234,7 +234,7 @@
#endif
}
// retry interval in millisecond; note that vendor services stay at 100ms
- const long sleepTime = gSystemBootCompleted ? 1000 : 100;
+ const useconds_t sleepTime = gSystemBootCompleted ? 1000 : 100;
ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(),
ProcessState::self()->getDriverName().c_str());
@@ -310,7 +310,7 @@
// Simple RAII object to ensure a function call immediately before going out of scope
class Defer {
public:
- Defer(std::function<void()>&& f) : mF(std::move(f)) {}
+ explicit Defer(std::function<void()>&& f) : mF(std::move(f)) {}
~Defer() { mF(); }
private:
std::function<void()> mF;
diff --git a/libs/binder/IShellCallback.cpp b/libs/binder/IShellCallback.cpp
index a3e2b67..86dd5c4 100644
--- a/libs/binder/IShellCallback.cpp
+++ b/libs/binder/IShellCallback.cpp
@@ -52,7 +52,7 @@
}
};
-IMPLEMENT_META_INTERFACE(ShellCallback, "com.android.internal.os.IShellCallback");
+IMPLEMENT_META_INTERFACE(ShellCallback, "com.android.internal.os.IShellCallback")
// ----------------------------------------------------------------------
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index b21af96..a1b08db 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -71,10 +71,11 @@
// ----------------------------------------------------------------------
-IMPLEMENT_META_INTERFACE(UidObserver, "android.app.IUidObserver");
+IMPLEMENT_META_INTERFACE(UidObserver, "android.app.IUidObserver")
// ----------------------------------------------------------------------
+// NOLINTNEXTLINE(google-default-arguments)
status_t BnUidObserver::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index e4ea60f..e1cbc19 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -49,7 +49,7 @@
int fd = ashmem_create_region(name == nullptr ? "MemoryHeapBase" : name, size);
ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
if (fd >= 0) {
- if (mapfd(fd, size) == NO_ERROR) {
+ if (mapfd(fd, true, size) == NO_ERROR) {
if (flags & READ_ONLY) {
ashmem_set_prot_region(fd, PROT_READ);
}
@@ -70,7 +70,7 @@
if (fd >= 0) {
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
- if (mapfd(fd, size) == NO_ERROR) {
+ if (mapfd(fd, false, size) == NO_ERROR) {
mDevice = device;
}
}
@@ -82,7 +82,7 @@
{
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
- mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
+ mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), false, size, offset);
}
status_t MemoryHeapBase::init(int fd, void *base, size_t size, int flags, const char* device)
@@ -98,7 +98,7 @@
return NO_ERROR;
}
-status_t MemoryHeapBase::mapfd(int fd, size_t size, off_t offset)
+status_t MemoryHeapBase::mapfd(int fd, bool writeableByCaller, size_t size, off_t offset)
{
if (size == 0) {
// try to figure out the size automatically
@@ -116,8 +116,12 @@
}
if ((mFlags & DONT_MAP_LOCALLY) == 0) {
+ int prot = PROT_READ;
+ if (writeableByCaller || (mFlags & READ_ONLY) == 0) {
+ prot |= PROT_WRITE;
+ }
void* base = (uint8_t*)mmap(nullptr, size,
- PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
+ prot, MAP_SHARED, fd, offset);
if (base == MAP_FAILED) {
ALOGE("mmap(fd=%d, size=%zu) failed (%s)",
fd, size, strerror(errno));
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a9c19b3..8087443 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -49,6 +49,7 @@
#include <private/binder/binder_module.h>
#include "Static.h"
+#include "Utils.h"
#define LOG_REFS(...)
//#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -77,9 +78,8 @@
// many things compile this into prebuilts on the stack
static_assert(sizeof(Parcel) == 60 || sizeof(Parcel) == 120);
-static pthread_mutex_t gParcelGlobalAllocSizeLock = PTHREAD_MUTEX_INITIALIZER;
-static size_t gParcelGlobalAllocSize = 0;
-static size_t gParcelGlobalAllocCount = 0;
+static std::atomic<size_t> gParcelGlobalAllocCount;
+static std::atomic<size_t> gParcelGlobalAllocSize;
static size_t gMaxFds = 0;
@@ -164,14 +164,11 @@
ALOGE("Invalid object type 0x%08x", obj.hdr.type);
}
-status_t Parcel::finishFlattenBinder(
- const sp<IBinder>& binder, const flat_binder_object& flat)
+status_t Parcel::finishFlattenBinder(const sp<IBinder>& binder)
{
- status_t status = writeObject(flat, false);
- if (status != OK) return status;
-
internal::Stability::tryMarkCompilationUnit(binder.get());
- return writeInt32(internal::Stability::get(binder.get()));
+ auto category = internal::Stability::getCategory(binder.get());
+ return writeInt32(category.repr());
}
status_t Parcel::finishUnflattenBinder(
@@ -181,7 +178,7 @@
status_t status = readInt32(&stability);
if (status != OK) return status;
- status = internal::Stability::set(binder.get(), stability, true /*log*/);
+ status = internal::Stability::setRepr(binder.get(), stability, true /*log*/);
if (status != OK) return status;
*out = binder;
@@ -209,7 +206,7 @@
if (proxy == nullptr) {
ALOGE("null proxy");
}
- const int32_t handle = proxy ? proxy->handle() : 0;
+ const int32_t handle = proxy ? proxy->getPrivateAccessorForHandle().handle() : 0;
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
@@ -225,6 +222,9 @@
if (local->isRequestingSid()) {
obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
}
+ if (local->isInheritRt()) {
+ obj.flags |= FLAT_BINDER_FLAG_INHERIT_RT;
+ }
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
@@ -237,7 +237,10 @@
obj.flags |= schedBits;
- return finishFlattenBinder(binder, obj);
+ status_t status = writeObject(obj, false);
+ if (status != OK) return status;
+
+ return finishFlattenBinder(binder);
}
status_t Parcel::unflattenBinder(sp<IBinder>* out) const
@@ -275,17 +278,11 @@
}
size_t Parcel::getGlobalAllocSize() {
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- size_t size = gParcelGlobalAllocSize;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
- return size;
+ return gParcelGlobalAllocSize.load();
}
size_t Parcel::getGlobalAllocCount() {
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- size_t count = gParcelGlobalAllocCount;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
- return count;
+ return gParcelGlobalAllocCount.load();
}
const uint8_t* Parcel::data() const
@@ -509,6 +506,11 @@
return mHasFds;
}
+void Parcel::markSensitive() const
+{
+ mDeallocZero = true;
+}
+
void Parcel::updateWorkSourceRequestHeaderPosition() const {
// Only update the request headers once. We only want to point
// to the first headers read/written.
@@ -1075,6 +1077,7 @@
{
if (str == nullptr) return writeInt32(-1);
+ // NOTE: Keep this logic in sync with android_os_Parcel.cpp
status_t err = writeInt32(len);
if (err == NO_ERROR) {
uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char));
@@ -1115,6 +1118,7 @@
{
if (str == nullptr) return writeInt32(-1);
+ // NOTE: Keep this logic in sync with android_os_Parcel.cpp
status_t err = writeInt32(len);
if (err == NO_ERROR) {
len *= sizeof(char16_t);
@@ -1473,6 +1477,31 @@
goto data_sorted;
}
+status_t Parcel::readVectorSizeWithCoarseBoundCheck(int32_t *size) const {
+ int32_t requestedSize;
+ const status_t status = readInt32(&requestedSize);
+ if (status != NO_ERROR) return status;
+
+ // We permit negative sizes, which indicate presence of a nullable vector,
+ // i.e. a vector embedded in std::optional, std::unique_ptr, or std::shared_ptr.
+ if (requestedSize > 0) {
+ // Check if there are fewer bytes than vector elements.
+ // A lower bound is 1 byte per element, satisfied by some enum and int8_t and uint8_t.
+ const size_t availableBytes = dataAvail();
+ if (static_cast<size_t>(requestedSize) > availableBytes) {
+ // We have a size that is greater than the number of bytes available.
+ // On bounds failure we do not 'rewind' position by 4 bytes of the size already read.
+ ALOGW("%s: rejecting out of bounds vector size (requestedSize):%d "
+ "Parcel{dataAvail:%zu mDataSize:%zu mDataPos:%zu mDataCapacity:%zu}",
+ __func__, requestedSize, availableBytes, mDataSize, mDataPos, mDataCapacity);
+ return BAD_VALUE;
+ }
+ }
+
+ *size = requestedSize;
+ return NO_ERROR;
+}
+
status_t Parcel::read(void* outData, size_t len) const
{
if (len > INT32_MAX) {
@@ -1695,7 +1724,7 @@
status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const {
const int32_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -1717,7 +1746,7 @@
status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const {
const int32_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -1738,7 +1767,7 @@
status_t Parcel::readBoolVector(std::vector<bool>* val) const {
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
if (status != OK) {
return status;
@@ -1913,17 +1942,6 @@
#endif
-status_t Parcel::readIntPtr(intptr_t *pArg) const
-{
- return readAligned(pArg);
-}
-
-
-intptr_t Parcel::readIntPtr() const
-{
- return readAligned<intptr_t>();
-}
-
status_t Parcel::readBool(bool *pArg) const
{
int32_t tmp = 0;
@@ -2508,22 +2526,17 @@
}
void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)
+ const binder_size_t* objects, size_t objectsCount, release_func relFunc)
{
- binder_size_t minOffset = 0;
- freeDataNoInit();
- mError = NO_ERROR;
+ freeData();
+
mData = const_cast<uint8_t*>(data);
mDataSize = mDataCapacity = dataSize;
- //ALOGI("setDataReference Setting data size of %p to %lu (pid=%d)", this, mDataSize, getpid());
- mDataPos = 0;
- ALOGV("setDataReference Setting data pos of %p to %zu", this, mDataPos);
mObjects = const_cast<binder_size_t*>(objects);
mObjectsSize = mObjectsCapacity = objectsCount;
- mNextObjectHint = 0;
- mObjectsSorted = false;
mOwner = relFunc;
- mOwnerCookie = relCookie;
+
+ binder_size_t minOffset = 0;
for (size_t i = 0; i < mObjectsSize; i++) {
binder_size_t offset = mObjects[i];
if (offset < minOffset) {
@@ -2624,22 +2637,17 @@
if (mOwner) {
LOG_ALLOC("Parcel %p: freeing other owner data", this);
//ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
- mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
+ mOwner(this, mData, mDataSize, mObjects, mObjectsSize);
} else {
LOG_ALLOC("Parcel %p: freeing allocated data", this);
releaseObjects();
if (mData) {
LOG_ALLOC("Parcel %p: freeing with %zu capacity", this, mDataCapacity);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- if (mDataCapacity <= gParcelGlobalAllocSize) {
- gParcelGlobalAllocSize = gParcelGlobalAllocSize - mDataCapacity;
- } else {
- gParcelGlobalAllocSize = 0;
+ gParcelGlobalAllocSize -= mDataCapacity;
+ gParcelGlobalAllocCount--;
+ if (mDeallocZero) {
+ zeroMemory(mData, mDataSize);
}
- if (gParcelGlobalAllocCount > 0) {
- gParcelGlobalAllocCount--;
- }
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
free(mData);
}
if (mObjects) free(mObjects);
@@ -2662,6 +2670,21 @@
: continueWrite(std::max(newSize, (size_t) 128));
}
+static uint8_t* reallocZeroFree(uint8_t* data, size_t oldCapacity, size_t newCapacity, bool zero) {
+ if (!zero) {
+ return (uint8_t*)realloc(data, newCapacity);
+ }
+ uint8_t* newData = (uint8_t*)malloc(newCapacity);
+ if (!newData) {
+ return nullptr;
+ }
+
+ memcpy(newData, data, std::min(oldCapacity, newCapacity));
+ zeroMemory(data, oldCapacity);
+ free(data);
+ return newData;
+}
+
status_t Parcel::restartWrite(size_t desired)
{
if (desired > INT32_MAX) {
@@ -2675,7 +2698,7 @@
return continueWrite(desired);
}
- uint8_t* data = (uint8_t*)realloc(mData, desired);
+ uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (!data && desired > mDataCapacity) {
mError = NO_MEMORY;
return NO_MEMORY;
@@ -2683,15 +2706,17 @@
releaseObjects();
- if (data) {
+ if (data || desired == 0) {
LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- gParcelGlobalAllocSize += desired;
- gParcelGlobalAllocSize -= mDataCapacity;
+ if (mDataCapacity > desired) {
+ gParcelGlobalAllocSize -= (mDataCapacity - desired);
+ } else {
+ gParcelGlobalAllocSize += (desired - mDataCapacity);
+ }
+
if (!mData) {
gParcelGlobalAllocCount++;
}
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataCapacity = desired;
}
@@ -2775,14 +2800,12 @@
memcpy(objects, mObjects, objectsSize*sizeof(binder_size_t));
}
//ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
- mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
+ mOwner(this, mData, mDataSize, mObjects, mObjectsSize);
mOwner = nullptr;
LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mObjects = objects;
@@ -2826,14 +2849,12 @@
// We own the data, so we can just do a realloc().
if (desired > mDataCapacity) {
- uint8_t* data = (uint8_t*)realloc(mData, desired);
+ uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (data) {
LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocSize -= mDataCapacity;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataCapacity = desired;
} else {
@@ -2865,10 +2886,8 @@
}
LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataSize = mDataPos = 0;
@@ -2898,6 +2917,7 @@
mHasFds = false;
mFdsKnown = true;
mAllowFds = true;
+ mDeallocZero = false;
mOwner = nullptr;
mOpenAshmemSize = 0;
mWorkSourceRequestHeaderPosition = 0;
diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp
index e9df279..2e86b74 100644
--- a/libs/binder/ParcelableHolder.cpp
+++ b/libs/binder/ParcelableHolder.cpp
@@ -27,7 +27,6 @@
namespace android {
namespace os {
status_t ParcelableHolder::writeToParcel(Parcel* p) const {
- std::lock_guard<std::mutex> l(mMutex);
RETURN_ON_FAILURE(p->writeInt32(static_cast<int32_t>(this->getStability())));
if (this->mParcelPtr) {
RETURN_ON_FAILURE(p->writeInt32(this->mParcelPtr->dataSize()));
@@ -38,7 +37,7 @@
size_t sizePos = p->dataPosition();
RETURN_ON_FAILURE(p->writeInt32(0));
size_t dataStartPos = p->dataPosition();
- RETURN_ON_FAILURE(p->writeUtf8AsUtf16(this->mParcelableName));
+ RETURN_ON_FAILURE(p->writeString16(this->mParcelableName));
this->mParcelable->writeToParcel(p);
size_t dataSize = p->dataPosition() - dataStartPos;
@@ -53,7 +52,6 @@
}
status_t ParcelableHolder::readFromParcel(const Parcel* p) {
- std::lock_guard<std::mutex> l(mMutex);
this->mStability = static_cast<Stability>(p->readInt32());
this->mParcelable = nullptr;
this->mParcelableName = std::nullopt;
diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp
index 75a6d22..6eae5ef 100644
--- a/libs/binder/PermissionCache.cpp
+++ b/libs/binder/PermissionCache.cpp
@@ -27,7 +27,7 @@
// ----------------------------------------------------------------------------
-ANDROID_SINGLETON_STATIC_INSTANCE(PermissionCache) ;
+ANDROID_SINGLETON_STATIC_INSTANCE(PermissionCache)
// ----------------------------------------------------------------------------
diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp
index 97a6c94..c807cfe 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -34,10 +34,22 @@
using android::sp;
using android::status_t;
using android::UNEXPECTED_NULL;
+
+using android::binder::VAL_BOOLEAN;
+using android::binder::VAL_INTEGER;
+using android::binder::VAL_LONG;
+using android::binder::VAL_DOUBLE;
+using android::binder::VAL_STRING;
+using android::binder::VAL_BOOLEANARRAY;
+using android::binder::VAL_INTARRAY;
+using android::binder::VAL_LONGARRAY;
+using android::binder::VAL_DOUBLEARRAY;
+using android::binder::VAL_STRINGARRAY;
+using android::binder::VAL_PERSISTABLEBUNDLE;
+
using std::map;
using std::set;
using std::vector;
-using namespace ::android::binder;
enum {
// Keep them in sync with BUNDLE_MAGIC* in frameworks/base/core/java/android/os/BaseBundle.java.
diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp
index 00d6eef..f75141e 100644
--- a/libs/binder/ProcessInfoService.cpp
+++ b/libs/binder/ProcessInfoService.cpp
@@ -99,6 +99,6 @@
}
}
-ANDROID_SINGLETON_STATIC_INSTANCE(ProcessInfoService);
+ANDROID_SINGLETON_STATIC_INSTANCE(ProcessInfoService)
} // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 83ca687..b5e4dfe 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -124,14 +124,14 @@
{
sp<IBinder> context = getStrongProxyForHandle(0);
- if (context == nullptr) {
- ALOGW("Not able to get context object on %s.", mDriverName.c_str());
+ if (context) {
+ // The root object is special since we get it directly from the driver, it is never
+ // written by Parcell::writeStrongBinder.
+ internal::Stability::markCompilationUnit(context.get());
+ } else {
+ ALOGW("Not able to get context object on %s.", mDriverName.c_str());
}
- // The root object is special since we get it directly from the driver, it is never
- // written by Parcell::writeStrongBinder.
- internal::Stability::tryMarkCompilationUnit(context.get());
-
return context;
}
@@ -204,11 +204,11 @@
// that the handle points to. Can only be used by the servicemanager.
//
// Returns -1 in case of failure, otherwise the strong reference count.
-ssize_t ProcessState::getStrongRefCountForNodeByHandle(int32_t handle) {
+ssize_t ProcessState::getStrongRefCountForNode(const sp<BpBinder>& binder) {
binder_node_info_for_ref info;
memset(&info, 0, sizeof(binder_node_info_for_ref));
- info.handle = handle;
+ info.handle = binder->getPrivateAccessorForHandle().handle();
status_t result = ioctl(mDriverFD, BINDER_GET_NODE_INFO_FOR_REF, &info);
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index 6115aec..339c538 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "Stability"
+
#include <binder/Stability.h>
#include <binder/BpBinder.h>
@@ -21,34 +23,59 @@
namespace android {
namespace internal {
+// the libbinder parcel format is currently unstable
+
+// oldest version which is supported
+constexpr uint8_t kBinderWireFormatOldest = 1;
+// current version
+constexpr uint8_t kBinderWireFormatVersion = 1;
+
+Stability::Category Stability::Category::currentFromLevel(Level level) {
+ return {
+ .version = kBinderWireFormatVersion,
+ .reserved = {0},
+ .level = level,
+ };
+}
+
+std::string Stability::Category::debugString() {
+ return levelString(level) + " wire protocol version "
+ + std::to_string(version);
+}
+
void Stability::markCompilationUnit(IBinder* binder) {
- status_t result = set(binder, getLocalStability(), true /*log*/);
+ auto stability = Category::currentFromLevel(getLocalLevel());
+ status_t result = setRepr(binder, stability.repr(), true /*log*/);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
void Stability::markVintf(IBinder* binder) {
- status_t result = set(binder, Level::VINTF, true /*log*/);
+ auto stability = Category::currentFromLevel(Level::VINTF);
+ status_t result = setRepr(binder, stability.repr(), true /*log*/);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
void Stability::debugLogStability(const std::string& tag, const sp<IBinder>& binder) {
- ALOGE("%s: stability is %s", tag.c_str(), stabilityString(get(binder.get())).c_str());
+ auto stability = getCategory(binder.get());
+ ALOGE("%s: stability is %s", tag.c_str(), stability.debugString().c_str());
}
void Stability::markVndk(IBinder* binder) {
- status_t result = set(binder, Level::VENDOR, true /*log*/);
+ auto stability = Category::currentFromLevel(Level::VENDOR);
+ status_t result = setRepr(binder, stability.repr(), true /*log*/);
LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
}
bool Stability::requiresVintfDeclaration(const sp<IBinder>& binder) {
- return check(get(binder.get()), Level::VINTF);
+ return check(getCategory(binder.get()), Level::VINTF);
}
void Stability::tryMarkCompilationUnit(IBinder* binder) {
- (void) set(binder, getLocalStability(), false /*log*/);
+ auto stability = Category::currentFromLevel(getLocalLevel());
+ (void) setRepr(binder, stability.repr(), false /*log*/);
}
-Stability::Level Stability::getLocalStability() {
+Stability::Level Stability::getLocalLevel() {
#ifdef __ANDROID_VNDK__
#ifdef __ANDROID_APEX__
// TODO(b/142684679) avoid use_vendor on system APEXes
@@ -67,65 +94,81 @@
#endif
}
-status_t Stability::set(IBinder* binder, int32_t stability, bool log) {
- Level currentStability = get(binder);
+status_t Stability::setRepr(IBinder* binder, int32_t representation, bool log) {
+ auto current = getCategory(binder);
+ auto setting = Category::fromRepr(representation);
+
+ // If we have ahold of a binder with a newer declared version, then it
+ // should support older versions, and we will simply write our parcels with
+ // the current wire parcel format.
+ if (setting.version < kBinderWireFormatOldest) {
+ // always log, because this shouldn't happen
+ ALOGE("Cannot accept binder with older binder wire protocol version "
+ "%u. Versions less than %u are unsupported.", setting.version,
+ kBinderWireFormatOldest);
+ return BAD_TYPE;
+ }
// null binder is always written w/ 'UNDECLARED' stability
if (binder == nullptr) {
- if (stability == UNDECLARED) {
+ if (setting.level == UNDECLARED) {
return OK;
} else {
if (log) {
ALOGE("Null binder written with stability %s.",
- stabilityString(stability).c_str());
+ levelString(setting.level).c_str());
}
return BAD_TYPE;
}
}
- if (!isDeclaredStability(stability)) {
+ if (!isDeclaredLevel(setting.level)) {
if (log) {
- ALOGE("Can only set known stability, not %d.", stability);
+ ALOGE("Can only set known stability, not %u.", setting.level);
}
return BAD_TYPE;
}
- if (currentStability != Level::UNDECLARED && currentStability != stability) {
+ if (current.repr() != 0 && current != setting) {
if (log) {
- ALOGE("Interface being set with %s but it is already marked as %s.",
- stabilityString(stability).c_str(), stabilityString(currentStability).c_str());
+ ALOGE("Interface being set with %s but it is already marked as %s",
+ setting.debugString().c_str(),
+ current.debugString().c_str());
}
return BAD_TYPE;
}
- if (currentStability == stability) return OK;
+ if (current == setting) return OK;
BBinder* local = binder->localBinder();
if (local != nullptr) {
- local->mStability = static_cast<int32_t>(stability);
+ local->mStability = setting.repr();
} else {
- binder->remoteBinder()->mStability = static_cast<int32_t>(stability);
+ binder->remoteBinder()->mStability = setting.repr();
}
return OK;
}
-Stability::Level Stability::get(IBinder* binder) {
- if (binder == nullptr) return UNDECLARED;
+Stability::Category Stability::getCategory(IBinder* binder) {
+ if (binder == nullptr) {
+ return Category::currentFromLevel(Level::UNDECLARED);
+ }
BBinder* local = binder->localBinder();
if (local != nullptr) {
- return static_cast<Stability::Level>(local->mStability);
+ return Category::fromRepr(local->mStability);
}
- return static_cast<Stability::Level>(binder->remoteBinder()->mStability);
+ return Category::fromRepr(binder->remoteBinder()->mStability);
}
-bool Stability::check(int32_t provided, Level required) {
- bool stable = (provided & required) == required;
+bool Stability::check(Category provided, Level required) {
+ bool stable = (provided.level & required) == required;
- if (!isDeclaredStability(provided) && provided != UNDECLARED) {
- ALOGE("Unknown stability when checking interface stability %d.", provided);
+ if (provided.level != UNDECLARED && !isDeclaredLevel(provided.level)) {
+ ALOGE("Unknown stability when checking interface stability %d.",
+ provided.level);
stable = false;
}
@@ -133,18 +176,18 @@
return stable;
}
-bool Stability::isDeclaredStability(int32_t stability) {
+bool Stability::isDeclaredLevel(Level stability) {
return stability == VENDOR || stability == SYSTEM || stability == VINTF;
}
-std::string Stability::stabilityString(int32_t stability) {
- switch (stability) {
+std::string Stability::levelString(Level level) {
+ switch (level) {
case Level::UNDECLARED: return "undeclared stability";
case Level::VENDOR: return "vendor stability";
case Level::SYSTEM: return "system stability";
case Level::VINTF: return "vintf stability";
}
- return "unknown stability " + std::to_string(stability);
+ return "unknown stability " + std::to_string(level);
}
} // namespace internal
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index db0f1c7..565f2e2 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -33,7 +33,7 @@
{
public:
LogTextOutput() : BufferedTextOutput(MULTITHREADED) { }
- virtual ~LogTextOutput() { };
+ virtual ~LogTextOutput() { }
protected:
virtual status_t writeLines(const struct iovec& vec, size_t N)
@@ -49,7 +49,7 @@
{
public:
explicit FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { }
- virtual ~FdTextOutput() { };
+ virtual ~FdTextOutput() { }
protected:
virtual status_t writeLines(const struct iovec& vec, size_t N)
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index 64ab7a9..b5a078c 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -245,10 +245,5 @@
return ret;
}
-std::stringstream& operator<< (std::stringstream& stream, const Status& s) {
- stream << s.toString8().string();
- return stream;
-}
-
} // namespace binder
} // namespace android
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index c232283..00a14f4 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -10,12 +10,22 @@
"name": "binderAllocationLimits"
},
{
+ "name": "binderClearBufTest"
+ },
+ {
"name": "binderDriverInterfaceTest"
},
{
"name": "binderTextOutputTest"
},
{
+ "name": "binderParcelTest"
+ },
+ {
+ "name": "binderParcelTest",
+ "host": true
+ },
+ {
"name": "binderLibTest"
},
{
@@ -31,6 +41,9 @@
"name": "aidl_lazy_test"
},
{
+ "name": "aidl_integration_test"
+ },
+ {
"name": "libbinderthreadstateutils_test"
},
{
@@ -43,6 +56,15 @@
"exclude-filter": "android.os.cts.StrictModeTest#testNonSdkApiUsage"
}
]
+ },
+ {
+ "name": "libbinder_rs-internal_test"
+ },
+ {
+ "name": "rustBinderTest"
+ },
+ {
+ "name": "binderRustNdkInteropTest"
}
]
}
diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp
new file mode 100644
index 0000000..90a4502
--- /dev/null
+++ b/libs/binder/Utils.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Utils.h"
+
+#include <string.h>
+
+namespace android {
+
+void zeroMemory(uint8_t* data, size_t size) {
+ memset(data, 0, size);
+}
+
+} // namespace android
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
new file mode 100644
index 0000000..f94b158
--- /dev/null
+++ b/libs/binder/Utils.h
@@ -0,0 +1,25 @@
+/*
+ * 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 <cstdint>
+#include <stddef.h>
+
+namespace android {
+
+// avoid optimizations
+void zeroMemory(uint8_t* data, size_t size);
+
+} // namespace android
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..b90dc86 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_ACTIVITY_MANAGER_H
-#define ANDROID_ACTIVITY_MANAGER_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -94,5 +93,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_ACTIVITY_MANAGER_H
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 6d04f13..35c697e 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_APP_OPS_MANAGER_H
-#define ANDROID_APP_OPS_MANAGER_H
+#pragma once
#include <binder/IAppOpsService.h>
@@ -133,7 +132,11 @@
OP_DEPRECATED_1 = 96,
OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97,
OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98,
- _NUM_OP = 99
+ OP_NO_ISOLATED_STORAGE = 99,
+ OP_PHONE_CALL_MICROPHONE = 100,
+ OP_PHONE_CALL_CAMERA = 101,
+ OP_RECORD_AUDIO_HOTWORD = 102,
+ _NUM_OP = 103
};
AppOpsManager();
@@ -175,5 +178,3 @@
} // namespace android
// ---------------------------------------------------------------------------
-
-#endif // ANDROID_APP_OPS_MANAGER_H
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index f3fea16..7079544 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BINDER_H
-#define ANDROID_BINDER_H
+#pragma once
#include <atomic>
#include <stdint.h>
@@ -88,6 +87,11 @@
int getMinSchedulerPolicy();
int getMinSchedulerPriority();
+ // Whether realtime scheduling policies are inherited.
+ bool isInheritRt();
+ // This must be called before the object is sent to another process. Not thread safe.
+ void setInheritRt(bool inheritRt);
+
pid_t getDebugPid();
protected:
@@ -142,5 +146,3 @@
} // namespace android
// ---------------------------------------------------------------------------
-
-#endif // ANDROID_BINDER_H
diff --git a/libs/binder/include/binder/BinderService.h b/libs/binder/include/binder/BinderService.h
index c17ae6f..5776f3c 100644
--- a/libs/binder/include/binder/BinderService.h
+++ b/libs/binder/include/binder/BinderService.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BINDER_SERVICE_H
-#define ANDROID_BINDER_SERVICE_H
+#pragma once
#include <stdint.h>
@@ -64,4 +63,3 @@
} // namespace android
// ---------------------------------------------------------------------------
-#endif // ANDROID_BINDER_SERVICE_H
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 378a911..22300ac 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BPBINDER_H
-#define ANDROID_BPBINDER_H
+#pragma once
#include <binder/IBinder.h>
#include <utils/KeyedVector.h>
@@ -29,7 +28,8 @@
namespace internal {
class Stability;
-};
+}
+class ProcessState;
using binder_proxy_limit_callback = void(*)(int);
@@ -38,8 +38,6 @@
public:
static BpBinder* create(int32_t handle);
- int32_t handle() const;
-
virtual const String16& getInterfaceDescriptor() const;
virtual bool isBinderAlive() const;
virtual status_t pingBinder();
@@ -110,19 +108,34 @@
KeyedVector<const void*, entry_t> mObjects;
};
-protected:
+ class PrivateAccessorForHandle {
+ private:
+ friend BpBinder;
+ friend ::android::Parcel;
+ friend ::android::ProcessState;
+ explicit PrivateAccessorForHandle(const BpBinder* binder) : mBinder(binder) {}
+ int32_t handle() const { return mBinder->handle(); }
+ const BpBinder* mBinder;
+ };
+ const PrivateAccessorForHandle getPrivateAccessorForHandle() const {
+ return PrivateAccessorForHandle(this);
+ }
+
+private:
+ friend PrivateAccessorForHandle;
+
+ int32_t handle() const;
BpBinder(int32_t handle,int32_t trackedUid);
virtual ~BpBinder();
virtual void onFirstRef();
virtual void onLastStrongRef(const void* id);
virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
-private:
- const int32_t mHandle;
-
friend ::android::internal::Stability;
int32_t mStability;
+ const int32_t mHandle;
+
struct Obituary {
wp<DeathRecipient> recipient;
void* cookie;
@@ -153,5 +166,3 @@
} // namespace android
// ---------------------------------------------------------------------------
-
-#endif // ANDROID_BPBINDER_H
diff --git a/libs/binder/include/binder/Debug.h b/libs/binder/include/binder/Debug.h
index 324e5c1..ac71e00 100644
--- a/libs/binder/include/binder/Debug.h
+++ b/libs/binder/include/binder/Debug.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BINDER_DEBUG_H
-#define ANDROID_BINDER_DEBUG_H
+#pragma once
#include <stdint.h>
#include <sys/cdefs.h>
@@ -45,5 +44,3 @@
// ---------------------------------------------------------------------------
} // namespace android
-
-#endif // ANDROID_BINDER_DEBUG_H
diff --git a/libs/binder/include/binder/Enums.h b/libs/binder/include/binder/Enums.h
index aec6f70..c6803bd 100644
--- a/libs/binder/include/binder/Enums.h
+++ b/libs/binder/include/binder/Enums.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#pragma once
#include <iterator>
@@ -38,4 +39,4 @@
constexpr auto end() const { return std::end(internal::enum_values<EnumType>); }
};
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..fde56a0 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_IACTIVITY_MANAGER_H
-#define ANDROID_IACTIVITY_MANAGER_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -56,5 +55,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IACTIVITY_MANAGER_H
diff --git a/libs/binder/include/binder/IAppOpsCallback.h b/libs/binder/include/binder/IAppOpsCallback.h
index 7664260..eb76f57 100644
--- a/libs/binder/include/binder/IAppOpsCallback.h
+++ b/libs/binder/include/binder/IAppOpsCallback.h
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-//
-#ifndef ANDROID_IAPP_OPS_CALLBACK_H
-#define ANDROID_IAPP_OPS_CALLBACK_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -57,6 +55,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IAPP_OPS_CALLBACK_H
-
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index a4a20c8..b0719d4 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-//
-#ifndef ANDROID_IAPP_OPS_SERVICE_H
-#define ANDROID_IAPP_OPS_SERVICE_H
+#pragma once
#include <binder/IAppOpsCallback.h>
#include <binder/IInterface.h>
@@ -89,5 +87,3 @@
// ----------------------------------------------------------------------
} // namespace android
-
-#endif // ANDROID_IAPP_OPS_SERVICE_H
diff --git a/libs/binder/include/binder/IBatteryStats.h b/libs/binder/include/binder/IBatteryStats.h
index b786f89..6defc7f 100644
--- a/libs/binder/include/binder/IBatteryStats.h
+++ b/libs/binder/include/binder/IBatteryStats.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_IBATTERYSTATS_H
-#define ANDROID_IBATTERYSTATS_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -82,5 +81,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IBATTERYSTATS_H
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index eea0e89..c8fb448 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_IBINDER_H
-#define ANDROID_IBINDER_H
+#pragma once
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -64,6 +63,10 @@
// Corresponds to TF_ONE_WAY -- an asynchronous call.
FLAG_ONEWAY = 0x00000001,
+ // Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call
+ // is made
+ FLAG_CLEAR_BUF = 0x00000020,
+
// Private userspace flag for transaction which is being requested from
// a vendor context.
FLAG_PRIVATE_VENDOR = 0x10000000,
@@ -253,5 +256,3 @@
} // namespace android
// ---------------------------------------------------------------------------
-
-#endif // ANDROID_IBINDER_H
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 7116154..f4a21dd 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-//
-#ifndef ANDROID_IINTERFACE_H
-#define ANDROID_IINTERFACE_H
+#pragma once
#include <binder/Binder.h>
@@ -335,5 +333,3 @@
} // namespace internal
} // namespace android
-
-#endif // ANDROID_IINTERFACE_H
diff --git a/libs/binder/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h
index da2b7cf..f92d557 100644
--- a/libs/binder/include/binder/IMediaResourceMonitor.h
+++ b/libs/binder/include/binder/IMediaResourceMonitor.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_I_MEDIA_RESOURCE_MONITOR_H
-#define ANDROID_I_MEDIA_RESOURCE_MONITOR_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -46,6 +45,7 @@
class BnMediaResourceMonitor : public BnInterface<IMediaResourceMonitor> {
public:
+ // NOLINTNEXTLINE(google-default-arguments)
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags = 0);
};
@@ -57,5 +57,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_I_MEDIA_RESOURCE_MONITOR_H
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
index 1a36eb0..d8b7ec1 100644
--- a/libs/binder/include/binder/IMemory.h
+++ b/libs/binder/include/binder/IMemory.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_IMEMORY_H
-#define ANDROID_IMEMORY_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -124,5 +123,3 @@
// ----------------------------------------------------------------------------
} // namespace android
-
-#endif // ANDROID_IMEMORY_H
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 49ef253..4da8aa1 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_IPC_THREAD_STATE_H
-#define ANDROID_IPC_THREAD_STATE_H
+#pragma once
#include <utils/Errors.h>
#include <binder/Parcel.h>
@@ -147,7 +146,7 @@
void blockUntilThreadAvailable();
// Service manager registration
- void setTheContextObject(sp<BBinder> obj);
+ void setTheContextObject(const sp<BBinder>& obj);
// WARNING: DO NOT USE THIS API
//
@@ -186,9 +185,8 @@
static void threadDestructor(void *st);
static void freeBuffer(Parcel* parcel,
const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsSize,
- void* cookie);
-
+ const binder_size_t* objects, size_t objectsSize);
+
const sp<ProcessState> mProcess;
Vector<BBinder*> mPendingStrongDerefs;
Vector<RefBase::weakref_type*> mPendingWeakDerefs;
@@ -214,5 +212,3 @@
} // namespace android
// ---------------------------------------------------------------------------
-
-#endif // ANDROID_IPC_THREAD_STATE_H
diff --git a/libs/binder/include/binder/IPermissionController.h b/libs/binder/include/binder/IPermissionController.h
index 4b66df8..a4f93d9 100644
--- a/libs/binder/include/binder/IPermissionController.h
+++ b/libs/binder/include/binder/IPermissionController.h
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-//
-#ifndef ANDROID_IPERMISSION_CONTROLLER_H
-#define ANDROID_IPERMISSION_CONTROLLER_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -70,6 +68,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IPERMISSION_CONTROLLER_H
-
diff --git a/libs/binder/include/binder/IProcessInfoService.h b/libs/binder/include/binder/IProcessInfoService.h
index ca30ad3..622f231 100644
--- a/libs/binder/include/binder/IProcessInfoService.h
+++ b/libs/binder/include/binder/IProcessInfoService.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_I_PROCESS_INFO_SERVICE_H
-#define ANDROID_I_PROCESS_INFO_SERVICE_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -51,5 +50,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_I_PROCESS_INFO_SERVICE_H
diff --git a/libs/binder/include/binder/IResultReceiver.h b/libs/binder/include/binder/IResultReceiver.h
index 70e99e7..5434445 100644
--- a/libs/binder/include/binder/IResultReceiver.h
+++ b/libs/binder/include/binder/IResultReceiver.h
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-//
-#ifndef ANDROID_IRESULT_RECEIVER_H
-#define ANDROID_IRESULT_RECEIVER_H
+#pragma once
#include <binder/IInterface.h>
@@ -51,6 +49,3 @@
// ----------------------------------------------------------------------
} // namespace android
-
-#endif // ANDROID_IRESULT_RECEIVER_H
-
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 3c5ccc1..5f0d056 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-//
-#ifndef ANDROID_ISERVICE_MANAGER_H
-#define ANDROID_ISERVICE_MANAGER_H
+#pragma once
#include <binder/IInterface.h>
#include <utils/Vector.h>
@@ -164,6 +162,3 @@
bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
} // namespace android
-
-#endif // ANDROID_ISERVICE_MANAGER_H
-
diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h
index b7ab6ea..6d3fe4a 100644
--- a/libs/binder/include/binder/IShellCallback.h
+++ b/libs/binder/include/binder/IShellCallback.h
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-//
-#ifndef ANDROID_ISHELL_CALLBACK_H
-#define ANDROID_ISHELL_CALLBACK_H
+#pragma once
#include <binder/IInterface.h>
@@ -27,7 +25,7 @@
class IShellCallback : public IInterface
{
public:
- DECLARE_META_INTERFACE(ShellCallback);
+ DECLARE_META_INTERFACE(ShellCallback)
virtual int openFile(const String16& path, const String16& seLinuxContext,
const String16& mode) = 0;
@@ -52,6 +50,3 @@
// ----------------------------------------------------------------------
} // namespace android
-
-#endif // ANDROID_ISHELL_CALLBACK_H
-
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h
index d070390..9291c0b 100644
--- a/libs/binder/include/binder/IUidObserver.h
+++ b/libs/binder/include/binder/IUidObserver.h
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-//
-#ifndef ANDROID_IUID_OBSERVER_H
-#define ANDROID_IUID_OBSERVER_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -64,5 +62,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IUID_OBSERVER_H
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h
index c7e7a50..a8faa3f 100644
--- a/libs/binder/include/binder/IpPrefix.h
+++ b/libs/binder/include/binder/IpPrefix.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_IP_PREFIX_H
-#define ANDROID_IP_PREFIX_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -74,8 +73,8 @@
private:
union InternalUnion {
InternalUnion() = default;
- explicit InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { };
- explicit InternalUnion(const struct in_addr &addr):mInAddr(addr) { };
+ explicit InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }
+ explicit InternalUnion(const struct in_addr &addr):mInAddr(addr) { }
struct in6_addr mIn6Addr;
struct in_addr mInAddr;
} mUnion;
@@ -90,5 +89,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_IP_PREFIX_H
diff --git a/libs/binder/include/binder/MemoryBase.h b/libs/binder/include/binder/MemoryBase.h
index 4dd3638..61a029c 100644
--- a/libs/binder/include/binder/MemoryBase.h
+++ b/libs/binder/include/binder/MemoryBase.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_MEMORY_BASE_H
-#define ANDROID_MEMORY_BASE_H
+#pragma once
#include <stdlib.h>
#include <stdint.h>
@@ -47,5 +46,3 @@
// ---------------------------------------------------------------------------
} // namespace android
-
-#endif // ANDROID_MEMORY_BASE_H
diff --git a/libs/binder/include/binder/MemoryDealer.h b/libs/binder/include/binder/MemoryDealer.h
index 6c1c412..e727772 100644
--- a/libs/binder/include/binder/MemoryDealer.h
+++ b/libs/binder/include/binder/MemoryDealer.h
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_MEMORY_DEALER_H
-#define ANDROID_MEMORY_DEALER_H
-
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -60,5 +58,3 @@
// ----------------------------------------------------------------------------
} // namespace android
-
-#endif // ANDROID_MEMORY_DEALER_H
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 52bd5de..dd76943 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_MEMORY_HEAP_BASE_H
-#define ANDROID_MEMORY_HEAP_BASE_H
+#pragma once
#include <stdlib.h>
#include <stdint.h>
@@ -51,6 +50,8 @@
/*
* maps memory from ashmem, with the given name for debugging
+ * if the READ_ONLY flag is set, the memory will be writeable by the calling process,
+ * but not by others. this is NOT the case with the other ctors.
*/
explicit MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = nullptr);
@@ -78,7 +79,7 @@
int flags = 0, const char* device = nullptr);
private:
- status_t mapfd(int fd, size_t size, off_t offset = 0);
+ status_t mapfd(int fd, bool writeableByCaller, size_t size, off_t offset = 0);
int mFD;
size_t mSize;
@@ -91,5 +92,3 @@
// ---------------------------------------------------------------------------
} // namespace android
-
-#endif // ANDROID_MEMORY_HEAP_BASE_H
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index fbfd6c5..54c49e4 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_PARCEL_H
-#define ANDROID_PARCEL_H
+#pragma once
#include <map> // for legacy reasons
#include <string>
@@ -34,18 +33,20 @@
#include <binder/Parcelable.h>
#ifdef BINDER_IPC_32BIT
+//NOLINTNEXTLINE(google-runtime-int) b/173188702
typedef unsigned int binder_size_t;
#else
+//NOLINTNEXTLINE(google-runtime-int) b/173188702
typedef unsigned long long binder_size_t;
#endif
+struct flat_binder_object;
// ---------------------------------------------------------------------------
namespace android {
template <typename T> class Flattenable;
template <typename T> class LightFlattenable;
-struct flat_binder_object;
class IBinder;
class IPCThreadState;
class ProcessState;
@@ -84,6 +85,13 @@
bool hasFileDescriptors() const;
+ // Zeros data when reallocating. Other mitigations may be added
+ // in the future.
+ //
+ // WARNING: some read methods may make additional copies of data.
+ // In order to verify this, heap dumps should be used.
+ void markSensitive() const;
+
// Writes the RPC header.
status_t writeInterfaceToken(const String16& interface);
status_t writeInterfaceToken(const char16_t* str, size_t len);
@@ -123,7 +131,7 @@
status_t writeString8(const char* str, size_t len);
status_t writeString16(const String16& str);
status_t writeString16(const std::optional<String16>& str);
- status_t writeString16(const std::unique_ptr<String16>& str);
+ status_t writeString16(const std::unique_ptr<String16>& str) __attribute__((deprecated("use std::optional version instead")));
status_t writeString16(const char16_t* str, size_t len);
status_t writeStrongBinder(const sp<IBinder>& val);
status_t writeInt32Array(size_t len, const int32_t *val);
@@ -135,48 +143,48 @@
// Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
status_t writeUtf8AsUtf16(const std::string& str);
status_t writeUtf8AsUtf16(const std::optional<std::string>& str);
- status_t writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
+ status_t writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) __attribute__((deprecated("use std::optional version instead")));
status_t writeByteVector(const std::optional<std::vector<int8_t>>& val);
- status_t writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
+ status_t writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeByteVector(const std::vector<int8_t>& val);
status_t writeByteVector(const std::optional<std::vector<uint8_t>>& val);
- status_t writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val);
+ status_t writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeByteVector(const std::vector<uint8_t>& val);
status_t writeInt32Vector(const std::optional<std::vector<int32_t>>& val);
- status_t writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
+ status_t writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeInt32Vector(const std::vector<int32_t>& val);
status_t writeInt64Vector(const std::optional<std::vector<int64_t>>& val);
- status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
+ status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeInt64Vector(const std::vector<int64_t>& val);
status_t writeUint64Vector(const std::optional<std::vector<uint64_t>>& val);
- status_t writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val);
+ status_t writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeUint64Vector(const std::vector<uint64_t>& val);
status_t writeFloatVector(const std::optional<std::vector<float>>& val);
- status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val);
+ status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeFloatVector(const std::vector<float>& val);
status_t writeDoubleVector(const std::optional<std::vector<double>>& val);
- status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val);
+ status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeDoubleVector(const std::vector<double>& val);
status_t writeBoolVector(const std::optional<std::vector<bool>>& val);
- status_t writeBoolVector(const std::unique_ptr<std::vector<bool>>& val);
+ status_t writeBoolVector(const std::unique_ptr<std::vector<bool>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeBoolVector(const std::vector<bool>& val);
status_t writeCharVector(const std::optional<std::vector<char16_t>>& val);
- status_t writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val);
+ status_t writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeCharVector(const std::vector<char16_t>& val);
status_t writeString16Vector(
const std::optional<std::vector<std::optional<String16>>>& val);
status_t writeString16Vector(
- const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
+ const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeString16Vector(const std::vector<String16>& val);
status_t writeUtf8VectorAsUtf16Vector(
const std::optional<std::vector<std::optional<std::string>>>& val);
status_t writeUtf8VectorAsUtf16Vector(
- const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
+ const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
status_t writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val);
- status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
+ status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
// Write an Enum vector with underlying type int8_t.
@@ -186,28 +194,30 @@
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::optional<std::vector<T>>& val);
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
- status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val);
+ status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
// Write an Enum vector with underlying type != int8_t.
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::vector<T>& val);
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::optional<std::vector<T>>& val);
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
- status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val);
+ status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
template<typename T>
status_t writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val);
template<typename T>
- status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val);
+ status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")));
template<typename T>
- status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val);
+ status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")));
+ template<typename T>
+ status_t writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val);
template<typename T>
status_t writeParcelableVector(const std::vector<T>& val);
template<typename T>
status_t writeNullableParcelable(const std::optional<T>& parcelable);
template<typename T>
- status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable);
+ status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable) __attribute__((deprecated("use std::optional version instead")));
status_t writeParcelable(const Parcelable& parcelable);
@@ -222,7 +232,7 @@
template<typename T>
status_t writeVectorSize(const std::optional<std::vector<T>>& val);
template<typename T>
- status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val);
+ status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
// Place a native_handle into the parcel (the native_handle's file-
// descriptors are dup'ed, so it is safe to delete the native_handle
@@ -259,7 +269,7 @@
status_t writeUniqueFileDescriptorVector(
const std::optional<std::vector<base::unique_fd>>& val);
status_t writeUniqueFileDescriptorVector(
- const std::unique_ptr<std::vector<base::unique_fd>>& val);
+ const std::unique_ptr<std::vector<base::unique_fd>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeUniqueFileDescriptorVector(
const std::vector<base::unique_fd>& val);
@@ -297,8 +307,6 @@
status_t readFloat(float *pArg) const;
double readDouble() const;
status_t readDouble(double *pArg) const;
- intptr_t readIntPtr() const;
- status_t readIntPtr(intptr_t *pArg) const;
bool readBool() const;
status_t readBool(bool *pArg) const;
char16_t readChar() const;
@@ -309,7 +317,7 @@
// Read a UTF16 encoded string, convert to UTF8
status_t readUtf8FromUtf16(std::string* str) const;
status_t readUtf8FromUtf16(std::optional<std::string>* str) const;
- status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
+ status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const __attribute__((deprecated("use std::optional version instead")));
const char* readCString() const;
String8 readString8() const;
@@ -318,7 +326,7 @@
String16 readString16() const;
status_t readString16(String16* pArg) const;
status_t readString16(std::optional<String16>* pArg) const;
- status_t readString16(std::unique_ptr<String16>* pArg) const;
+ status_t readString16(std::unique_ptr<String16>* pArg) const __attribute__((deprecated("use std::optional version instead")));
const char16_t* readString16Inplace(size_t* outLen) const;
sp<IBinder> readStrongBinder() const;
status_t readStrongBinder(sp<IBinder>* val) const;
@@ -329,14 +337,14 @@
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::vector<T>* val) const;
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
- status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const;
+ status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")));
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::optional<std::vector<T>>* val) const;
// Read an Enum vector with underlying type != int8_t.
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::vector<T>* val) const;
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
- status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const;
+ status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")));
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::optional<std::vector<T>>* val) const;
@@ -345,7 +353,7 @@
std::optional<std::vector<std::optional<T>>>* val) const;
template<typename T>
status_t readParcelableVector(
- std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const;
+ std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const __attribute__((deprecated("use std::optional version instead")));
template<typename T>
status_t readParcelableVector(std::vector<T>* val) const;
@@ -354,7 +362,7 @@
template<typename T>
status_t readParcelable(std::optional<T>* parcelable) const;
template<typename T>
- status_t readParcelable(std::unique_ptr<T>* parcelable) const;
+ status_t readParcelable(std::unique_ptr<T>* parcelable) const __attribute__((deprecated("use std::optional version instead")));
template<typename T>
status_t readStrongBinder(sp<T>* val) const;
@@ -363,45 +371,45 @@
status_t readNullableStrongBinder(sp<T>* val) const;
status_t readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const;
- status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const;
+ status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
status_t readByteVector(std::optional<std::vector<int8_t>>* val) const;
- status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const;
+ status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readByteVector(std::vector<int8_t>* val) const;
status_t readByteVector(std::optional<std::vector<uint8_t>>* val) const;
- status_t readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const;
+ status_t readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readByteVector(std::vector<uint8_t>* val) const;
status_t readInt32Vector(std::optional<std::vector<int32_t>>* val) const;
- status_t readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const;
+ status_t readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readInt32Vector(std::vector<int32_t>* val) const;
status_t readInt64Vector(std::optional<std::vector<int64_t>>* val) const;
- status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
+ status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readInt64Vector(std::vector<int64_t>* val) const;
status_t readUint64Vector(std::optional<std::vector<uint64_t>>* val) const;
- status_t readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const;
+ status_t readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readUint64Vector(std::vector<uint64_t>* val) const;
status_t readFloatVector(std::optional<std::vector<float>>* val) const;
- status_t readFloatVector(std::unique_ptr<std::vector<float>>* val) const;
+ status_t readFloatVector(std::unique_ptr<std::vector<float>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readFloatVector(std::vector<float>* val) const;
status_t readDoubleVector(std::optional<std::vector<double>>* val) const;
- status_t readDoubleVector(std::unique_ptr<std::vector<double>>* val) const;
+ status_t readDoubleVector(std::unique_ptr<std::vector<double>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readDoubleVector(std::vector<double>* val) const;
status_t readBoolVector(std::optional<std::vector<bool>>* val) const;
- status_t readBoolVector(std::unique_ptr<std::vector<bool>>* val) const;
+ status_t readBoolVector(std::unique_ptr<std::vector<bool>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readBoolVector(std::vector<bool>* val) const;
status_t readCharVector(std::optional<std::vector<char16_t>>* val) const;
- status_t readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const;
+ status_t readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readCharVector(std::vector<char16_t>* val) const;
status_t readString16Vector(
std::optional<std::vector<std::optional<String16>>>* val) const;
status_t readString16Vector(
- std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
+ std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readString16Vector(std::vector<String16>* val) const;
status_t readUtf8VectorFromUtf16Vector(
std::optional<std::vector<std::optional<std::string>>>* val) const;
status_t readUtf8VectorFromUtf16Vector(
- std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
+ std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
template<typename T>
@@ -415,7 +423,7 @@
template<typename T>
status_t resizeOutVector(std::optional<std::vector<T>>* val) const;
template<typename T>
- status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const;
+ status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")));
template<typename T>
status_t reserveOutVector(std::vector<T>* val, size_t* size) const;
template<typename T>
@@ -423,7 +431,7 @@
size_t* size) const;
template<typename T>
status_t reserveOutVector(std::unique_ptr<std::vector<T>>* val,
- size_t* size) const;
+ size_t* size) const __attribute__((deprecated("use std::optional version instead")));
// Like Parcel.java's readExceptionCode(). Reads the first int32
// off of a Parcel's header, returning 0 or the negative error
@@ -459,7 +467,7 @@
status_t readUniqueFileDescriptorVector(
std::optional<std::vector<base::unique_fd>>* val) const;
status_t readUniqueFileDescriptorVector(
- std::unique_ptr<std::vector<base::unique_fd>>* val) const;
+ std::unique_ptr<std::vector<base::unique_fd>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readUniqueFileDescriptorVector(
std::vector<base::unique_fd>* val) const;
@@ -481,27 +489,21 @@
// uid.
uid_t readCallingWorkSourceUid() const;
+ void print(TextOutput& to, uint32_t flags = 0) const;
+
private:
typedef void (*release_func)(Parcel* parcel,
const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsSize,
- void* cookie);
-
+ const binder_size_t* objects, size_t objectsSize);
+
uintptr_t ipcData() const;
size_t ipcDataSize() const;
uintptr_t ipcObjects() const;
size_t ipcObjectsCount() const;
void ipcSetDataReference(const uint8_t* data, size_t dataSize,
const binder_size_t* objects, size_t objectsCount,
- release_func relFunc, void* relCookie);
-
-public:
- void print(TextOutput& to, uint32_t flags = 0) const;
+ release_func relFunc);
-private:
- Parcel(const Parcel& o);
- Parcel& operator=(const Parcel& o);
-
status_t finishWrite(size_t len);
void releaseObjects();
void acquireObjects();
@@ -515,10 +517,14 @@
void initState();
void scanForFds() const;
status_t validateReadData(size_t len) const;
+
+ // Reads an int32 size and does a coarse bounds check against the number
+ // of available bytes in the Parcel.
+ status_t readVectorSizeWithCoarseBoundCheck(int32_t *size) const;
+
void updateWorkSourceRequestHeaderPosition() const;
- status_t finishFlattenBinder(const sp<IBinder>& binder,
- const flat_binder_object& flat);
+ status_t finishFlattenBinder(const sp<IBinder>& binder);
status_t finishUnflattenBinder(const sp<IBinder>& binder, sp<IBinder>* out) const;
status_t flattenBinder(const sp<IBinder>& binder);
status_t unflattenBinder(sp<IBinder>* out) const;
@@ -556,7 +562,7 @@
status_t(Parcel::*read_func)(T*) const) const;
template<typename T>
status_t readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
- status_t(Parcel::*read_func)(T*) const) const;
+ status_t(Parcel::*read_func)(T*) const) const __attribute__((deprecated("use std::optional version instead")));
template<typename T>
status_t readTypedVector(std::vector<T>* val,
status_t(Parcel::*read_func)(T*) const) const;
@@ -568,13 +574,13 @@
status_t(Parcel::*write_func)(const T&));
template<typename T>
status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
- status_t(Parcel::*write_func)(const T&));
+ status_t(Parcel::*write_func)(const T&)) __attribute__((deprecated("use std::optional version instead")));
template<typename T>
status_t writeNullableTypedVector(const std::optional<std::vector<T>>& val,
status_t(Parcel::*write_func)(T));
template<typename T>
status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
- status_t(Parcel::*write_func)(T));
+ status_t(Parcel::*write_func)(T)) __attribute__((deprecated("use std::optional version instead")));
template<typename T>
status_t writeTypedVector(const std::vector<T>& val,
status_t(Parcel::*write_func)(const T&));
@@ -600,8 +606,14 @@
mutable bool mHasFds;
bool mAllowFds;
+ // if this parcelable is involved in a secure transaction, force the
+ // data to be overridden with zero when deallocated
+ mutable bool mDeallocZero;
+
release_func mOwner;
- void* mOwnerCookie;
+
+ // TODO(167966510): reserved for binder/version/stability
+ void* mReserved = reinterpret_cast<void*>(0xAAAAAAAA);
class Blob {
public:
@@ -780,6 +792,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::vector<T>* val) const {
int32_t size;
+ // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
status_t err = readInt32(&size);
if (err != NO_ERROR) {
return err;
@@ -795,6 +808,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const {
int32_t size;
+ // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
status_t err = readInt32(&size);
if (err != NO_ERROR) {
return err;
@@ -811,6 +825,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
int32_t size;
+ // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
status_t err = readInt32(&size);
if (err != NO_ERROR) {
return err;
@@ -827,7 +842,7 @@
template<typename T>
status_t Parcel::reserveOutVector(std::vector<T>* val, size_t* size) const {
int32_t read_size;
- status_t err = readInt32(&read_size);
+ status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
if (err != NO_ERROR) {
return err;
}
@@ -843,7 +858,7 @@
template<typename T>
status_t Parcel::reserveOutVector(std::optional<std::vector<T>>* val, size_t* size) const {
int32_t read_size;
- status_t err = readInt32(&read_size);
+ status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
if (err != NO_ERROR) {
return err;
}
@@ -863,7 +878,7 @@
status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val,
size_t* size) const {
int32_t read_size;
- status_t err = readInt32(&read_size);
+ status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
if (err != NO_ERROR) {
return err;
}
@@ -916,7 +931,7 @@
std::vector<T>* val,
status_t(Parcel::*read_func)(U*) const) const {
int32_t size;
- status_t status = this->readInt32(&size);
+ status_t status = this->readVectorSizeWithCoarseBoundCheck(&size);
if (status != OK) {
return status;
@@ -958,7 +973,7 @@
status_t(Parcel::*read_func)(T*) const) const {
const size_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -982,7 +997,7 @@
status_t(Parcel::*read_func)(T*) const) const {
const size_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -1086,7 +1101,7 @@
status_t Parcel::readParcelableVector(std::optional<std::vector<std::optional<T>>>* val) const {
const size_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -1110,7 +1125,7 @@
status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
const size_t start = dataPosition();
int32_t size;
- status_t status = readInt32(&size);
+ status_t status = readVectorSizeWithCoarseBoundCheck(&size);
val->reset();
if (status != OK || size < 0) {
@@ -1220,6 +1235,16 @@
return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
}
+template<typename T>
+status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val) {
+ if (val.get() == nullptr) {
+ return this->writeInt32(-1);
+ }
+
+ using NullableT = std::optional<T>;
+ return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
+}
+
template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>>
status_t Parcel::writeEnum(const T& val) {
return writeInt32(static_cast<int32_t>(val));
@@ -1328,5 +1353,3 @@
} // namespace android
// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PARCEL_H
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 71e1d3c..9896fd7 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_PARCEL_FILE_DESCRIPTOR_H_
-#define ANDROID_PARCEL_FILE_DESCRIPTOR_H_
+#pragma once
#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
@@ -44,22 +43,22 @@
android::status_t readFromParcel(const android::Parcel* parcel) override;
inline bool operator!=(const ParcelFileDescriptor& rhs) const {
- return mFd != rhs.mFd;
+ return mFd.get() != rhs.mFd.get();
}
inline bool operator<(const ParcelFileDescriptor& rhs) const {
- return mFd < rhs.mFd;
+ return mFd.get() < rhs.mFd.get();
}
inline bool operator<=(const ParcelFileDescriptor& rhs) const {
- return mFd <= rhs.mFd;
+ return mFd.get() <= rhs.mFd.get();
}
inline bool operator==(const ParcelFileDescriptor& rhs) const {
- return mFd == rhs.mFd;
+ return mFd.get() == rhs.mFd.get();
}
inline bool operator>(const ParcelFileDescriptor& rhs) const {
- return mFd > rhs.mFd;
+ return mFd.get() > rhs.mFd.get();
}
inline bool operator>=(const ParcelFileDescriptor& rhs) const {
- return mFd >= rhs.mFd;
+ return mFd.get() >= rhs.mFd.get();
}
private:
android::base::unique_fd mFd;
@@ -67,5 +66,3 @@
} // namespace os
} // namespace android
-
-#endif // ANDROID_OS_PARCEL_FILE_DESCRIPTOR_H_
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
index a6e610c..2c652be 100644
--- a/libs/binder/include/binder/Parcelable.h
+++ b/libs/binder/include/binder/Parcelable.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_PARCELABLE_H
-#define ANDROID_PARCELABLE_H
+#pragma once
#include <vector>
@@ -74,5 +73,3 @@
#endif
} // namespace android
-
-#endif // ANDROID_PARCELABLE_H
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index 5da2515..9e4475c 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -18,6 +18,7 @@
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
+#include <utils/String16.h>
#include <mutex>
#include <optional>
#include <tuple>
@@ -30,7 +31,7 @@
class ParcelableHolder : public android::Parcelable {
public:
ParcelableHolder() = delete;
- explicit ParcelableHolder(Stability stability) : mStability(stability){};
+ explicit ParcelableHolder(Stability stability) : mStability(stability){}
virtual ~ParcelableHolder() = default;
ParcelableHolder(const ParcelableHolder& other) {
mParcelable = other.mParcelable;
@@ -40,7 +41,7 @@
mParcelPtr->appendFrom(other.mParcelPtr.get(), 0, other.mParcelPtr->dataSize());
}
mStability = other.mStability;
- };
+ }
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
@@ -52,90 +53,87 @@
}
template <typename T>
- bool setParcelable(T&& p) {
+ status_t setParcelable(T&& p) {
using Tt = typename std::decay<T>::type;
return setParcelable<Tt>(std::make_shared<Tt>(std::forward<T>(p)));
}
template <typename T>
- bool setParcelable(std::shared_ptr<T> p) {
- std::lock_guard<std::mutex> l(mMutex);
+ status_t setParcelable(std::shared_ptr<T> p) {
static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
if (p && this->getStability() > p->getStability()) {
- return false;
+ return android::BAD_VALUE;
}
this->mParcelable = p;
this->mParcelableName = T::getParcelableDescriptor();
this->mParcelPtr = nullptr;
- return true;
+ return android::OK;
}
template <typename T>
- std::shared_ptr<T> getParcelable() const {
+ status_t getParcelable(std::shared_ptr<T>* ret) const {
static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
- std::lock_guard<std::mutex> l(mMutex);
- const std::string& parcelableDesc = T::getParcelableDescriptor();
+ const String16& parcelableDesc = T::getParcelableDescriptor();
if (!this->mParcelPtr) {
if (!this->mParcelable || !this->mParcelableName) {
ALOGD("empty ParcelableHolder");
- return nullptr;
+ *ret = nullptr;
+ return android::OK;
} else if (parcelableDesc != *mParcelableName) {
ALOGD("extension class name mismatch expected:%s actual:%s",
- mParcelableName->c_str(), parcelableDesc.c_str());
- return nullptr;
+ String8(*mParcelableName).c_str(), String8(parcelableDesc).c_str());
+ *ret = nullptr;
+ return android::BAD_VALUE;
}
- return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+ *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+ return android::OK;
}
this->mParcelPtr->setDataPosition(0);
- status_t status = this->mParcelPtr->readUtf8FromUtf16(&this->mParcelableName);
+ status_t status = this->mParcelPtr->readString16(&this->mParcelableName);
if (status != android::OK || parcelableDesc != this->mParcelableName) {
this->mParcelableName = std::nullopt;
- return nullptr;
+ *ret = nullptr;
+ return status;
}
this->mParcelable = std::make_shared<T>();
status = mParcelable.get()->readFromParcel(this->mParcelPtr.get());
if (status != android::OK) {
this->mParcelableName = std::nullopt;
this->mParcelable = nullptr;
- return nullptr;
+ *ret = nullptr;
+ return status;
}
this->mParcelPtr = nullptr;
- return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+ *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+ return android::OK;
}
Stability getStability() const override { return mStability; }
inline bool operator!=(const ParcelableHolder& rhs) const {
- return std::tie(mParcelable, mParcelPtr, mStability) !=
- std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ return this != &rhs;
}
inline bool operator<(const ParcelableHolder& rhs) const {
- return std::tie(mParcelable, mParcelPtr, mStability) <
- std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ return this < &rhs;
}
inline bool operator<=(const ParcelableHolder& rhs) const {
- return std::tie(mParcelable, mParcelPtr, mStability) <=
- std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ return this <= &rhs;
}
inline bool operator==(const ParcelableHolder& rhs) const {
- return std::tie(mParcelable, mParcelPtr, mStability) ==
- std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ return this == &rhs;
}
inline bool operator>(const ParcelableHolder& rhs) const {
- return std::tie(mParcelable, mParcelPtr, mStability) >
- std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ return this > &rhs;
}
inline bool operator>=(const ParcelableHolder& rhs) const {
- return std::tie(mParcelable, mParcelPtr, mStability) >=
- std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ return this >= &rhs;
}
private:
mutable std::shared_ptr<Parcelable> mParcelable;
- mutable std::optional<std::string> mParcelableName;
+ mutable std::optional<String16> mParcelableName;
mutable std::unique_ptr<Parcel> mParcelPtr;
Stability mStability;
- mutable std::mutex mMutex;
};
} // namespace os
} // namespace android
diff --git a/libs/binder/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h
index c258215..835a3a8 100644
--- a/libs/binder/include/binder/PermissionCache.h
+++ b/libs/binder/include/binder/PermissionCache.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef BINDER_PERMISSION_H
-#define BINDER_PERMISSION_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -82,5 +81,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif /* BINDER_PERMISSION_H */
diff --git a/libs/binder/include/binder/PermissionController.h b/libs/binder/include/binder/PermissionController.h
index 4db522a..e658574 100644
--- a/libs/binder/include/binder/PermissionController.h
+++ b/libs/binder/include/binder/PermissionController.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_PERMISSION_CONTROLLER_H
-#define ANDROID_PERMISSION_CONTROLLER_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -65,5 +64,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_PERMISSION_CONTROLLER_H
diff --git a/libs/binder/include/binder/PersistableBundle.h b/libs/binder/include/binder/PersistableBundle.h
index 322fef9..4517cf2 100644
--- a/libs/binder/include/binder/PersistableBundle.h
+++ b/libs/binder/include/binder/PersistableBundle.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_PERSISTABLE_BUNDLE_H
-#define ANDROID_PERSISTABLE_BUNDLE_H
+#pragma once
#include <map>
#include <set>
@@ -128,5 +127,3 @@
} // namespace os
} // namespace android
-
-#endif // ANDROID_PERSISTABLE_BUNDLE_H
diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include/binder/ProcessInfoService.h
index 6bfd1bc..6b3b5ce 100644
--- a/libs/binder/include/binder/ProcessInfoService.h
+++ b/libs/binder/include/binder/ProcessInfoService.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_PROCESS_INFO_SERVICE_H
-#define ANDROID_PROCESS_INFO_SERVICE_H
+#pragma once
#ifndef __ANDROID_VNDK__
@@ -83,6 +82,3 @@
#else // __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif // __ANDROID_VNDK__
-
-#endif // ANDROID_PROCESS_INFO_SERVICE_H
-
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index efb95f4..bab6469 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_PROCESS_STATE_H
-#define ANDROID_PROCESS_STATE_H
+#pragma once
#include <binder/IBinder.h>
#include <utils/KeyedVector.h>
@@ -71,7 +70,7 @@
// 2. Temporary strong references held by the kernel during a
// transaction on the node.
// It does NOT include local strong references to the node
- ssize_t getStrongRefCountForNodeByHandle(int32_t handle);
+ ssize_t getStrongRefCountForNode(const sp<BpBinder>& binder);
enum class CallRestriction {
// all calls okay
@@ -132,5 +131,3 @@
} // namespace android
// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PROCESS_STATE_H
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index 6566285..12272ba 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -26,6 +26,29 @@
namespace internal {
+// Stability encodes how a binder changes over time. There are two levels of
+// stability:
+// 1). the interface stability - this is how a particular set of API calls (a
+// particular ordering of things like writeInt32/readInt32) are changed over
+// time. If one release, we have 'writeInt32' and the next release, we have
+// 'writeInt64', then this interface doesn't have a very stable
+// Stability::Level. Usually this ordering is controlled by a .aidl file.
+// 2). the wire format stability - this is how these API calls map to actual
+// bytes that are written to the wire (literally, this is how they are written
+// to the kernel inside of IBinder::transact, but it may be expanded to other
+// wires in the future). For instance, writeInt32 in binder translates to
+// writing a 4-byte little-endian integer in two's complement. You can imagine
+// in the future, we change writeInt32/readInt32 to instead write 8-bytes with
+// that integer and some check bits. In this case, the wire format changes,
+// but as long as a client libbinder knows to keep on writing a 4-byte value
+// to old servers, and new servers know how to interpret the 8-byte result,
+// they can still communicate.
+//
+// Every binder object has a stability level associated with it, and when
+// communicating with a binder, we make sure that the command we sent is one
+// that it knows how to process. The summary of stability of a binder is
+// represented by a Stability::Category object.
+
// WARNING: These APIs are only ever expected to be called by auto-generated code.
// Instead of calling them, you should set the stability of a .aidl interface
class Stability final {
@@ -73,7 +96,7 @@
static void tryMarkCompilationUnit(IBinder* binder);
- enum Level : int32_t {
+ enum Level : uint8_t {
UNDECLARED = 0,
VENDOR = 0b000011,
@@ -81,19 +104,54 @@
VINTF = 0b111111,
};
+ // This is the format of stability passed on the wire.
+ struct Category {
+ static inline Category fromRepr(int32_t representation) {
+ return *reinterpret_cast<Category*>(&representation);
+ }
+ int32_t repr() const {
+ return *reinterpret_cast<const int32_t*>(this);
+ }
+ static inline Category currentFromLevel(Level level);
+
+ bool operator== (const Category& o) const {
+ return repr() == o.repr();
+ }
+ bool operator!= (const Category& o) const {
+ return !(*this == o);
+ }
+
+ std::string debugString();
+
+ // This is the version of the wire protocol associated with the host
+ // process of a particular binder. As the wire protocol changes, if
+ // sending a transaction to a binder with an old version, the Parcel
+ // class must write parcels according to the version documented here.
+ uint8_t version;
+
+ uint8_t reserved[2];
+
+ // bitmask of Stability::Level
+ Level level;
+ };
+ static_assert(sizeof(Category) == sizeof(int32_t));
+
// returns the stability according to how this was built
- static Level getLocalStability();
+ static Level getLocalLevel();
// applies stability to binder if stability level is known
__attribute__((warn_unused_result))
- static status_t set(IBinder* binder, int32_t stability, bool log);
+ static status_t setRepr(IBinder* binder, int32_t representation, bool log);
- static Level get(IBinder* binder);
+ // get stability information as encoded on the wire
+ static Category getCategory(IBinder* binder);
- static bool check(int32_t provided, Level required);
+ // whether a transaction on binder is allowed, if the transaction
+ // is done from a context with a specific stability level
+ static bool check(Category provided, Level required);
- static bool isDeclaredStability(int32_t stability);
- static std::string stabilityString(int32_t stability);
+ static bool isDeclaredLevel(Level level);
+ static std::string levelString(Level level);
Stability();
};
diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h
index 7d889b6..c30ae01 100644
--- a/libs/binder/include/binder/Status.h
+++ b/libs/binder/include/binder/Status.h
@@ -18,7 +18,8 @@
#define ANDROID_BINDER_STATUS_H
#include <cstdint>
-#include <sstream>
+#include <sstream> // historical
+#include <ostream>
#include <binder/Parcel.h>
#include <utils/String8.h>
@@ -153,8 +154,9 @@
String8 mMessage;
}; // class Status
-// For gtest output logging
-std::stringstream& operator<< (std::stringstream& stream, const Status& s);
+static inline std::ostream& operator<< (std::ostream& o, const Status& s) {
+ return o << s.toString8();
+}
} // namespace binder
} // namespace android
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
index c7e1e14..bf9c92b 100644
--- a/libs/binder/include/binder/TextOutput.h
+++ b/libs/binder/include/binder/TextOutput.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_TEXTOUTPUT_H
-#define ANDROID_TEXTOUTPUT_H
+#pragma once
#include <utils/Errors.h>
#include <utils/String8.h>
@@ -206,5 +205,3 @@
// ---------------------------------------------------------------------------
} // namespace android
-
-#endif // ANDROID_TEXTOUTPUT_H
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index 7be8f7b..5a719b8 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -17,10 +17,6 @@
#ifndef _BINDER_MODULE_H_
#define _BINDER_MODULE_H_
-#ifdef __cplusplus
-namespace android {
-#endif
-
/* obtain structures and constants from the kernel header */
// TODO(b/31559095): bionic on host
@@ -36,6 +32,10 @@
#include <sys/ioctl.h>
#include <linux/android/binder.h>
+#ifdef __cplusplus
+namespace android {
+#endif
+
#ifndef BR_FROZEN_REPLY
// Temporary definition of BR_FROZEN_REPLY. For production
// this will come from UAPI binder.h
@@ -88,7 +88,9 @@
};
#endif //BINDER_GET_FROZEN_INFO
-
+enum transaction_flags_ext {
+ TF_CLEAR_BUF = 0x20, /* clear buffer on txn complete */
+};
#ifdef __cplusplus
} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 4fd0657..82f3882 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -26,6 +26,9 @@
"-D__ANDROID_API__=10000",
],
},
+ darwin: {
+ enabled: false,
+ },
},
}
@@ -35,6 +38,8 @@
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
+ llndk_stubs: "libbinder_ndk.llndk",
+
export_include_dirs: [
"include_cpp",
"include_ndk",
@@ -82,6 +87,9 @@
linux: {
version_script: "libbinder_ndk.map.txt",
},
+ darwin: {
+ enabled: false,
+ },
},
stubs: {
symbol_file: "libbinder_ndk.map.txt",
@@ -90,6 +98,35 @@
"30",
],
},
+ tidy: true,
+ tidy_flags: [
+ // Only check our headers
+ "--header-filter=^.*frameworks/native/libs/binder/.*.h$",
+ ],
+ tidy_checks_as_errors: [
+ // Explicitly list the checks that should not occur in this module.
+ "abseil-*",
+ "android-*",
+ "bugprone-*",
+ "cert-*",
+ "clang-analyzer-*",
+ "-clang-analyzer-core.CallAndMessage",
+ "-clang-analyzer-core.uninitialized.Assign",
+ "-clang-analyzer-unix.Malloc",
+ "-clang-analyzer-deadcode.DeadStores",
+ "-clang-analyzer-optin.cplusplus.UninitializedObject",
+ "google-*",
+ "-google-readability-*",
+ "-google-runtime-references",
+ "misc-*",
+ "-misc-no-recursion",
+ "-misc-non-private-member-variables-in-classes",
+ "-misc-redundant-expression",
+ "-misc-unused-parameters",
+ "-misc-unused-using-decls",
+ "performance*",
+ "portability*",
+ ],
}
ndk_headers {
@@ -120,7 +157,7 @@
}
llndk_library {
- name: "libbinder_ndk",
+ name: "libbinder_ndk.llndk",
symbol_file: "libbinder_ndk.map.txt",
export_include_dirs: [
"include_cpp",
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 2a2eed7..454fbd0 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include <android/binder_context.h>
#include <android/binder_ibinder.h>
#include <android/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
#include "ibinder_internal.h"
#include <android/binder_stability.h>
@@ -181,7 +181,7 @@
binder_status_t status = getClass()->onTransact(this, code, &in, &out);
return PruneStatusT(status);
- } else if (code == SHELL_COMMAND_TRANSACTION) {
+ } else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) {
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
int err = data.readFileDescriptor();
@@ -301,13 +301,34 @@
return binder.get();
}
+AIBinder_Weak* AIBinder_Weak_clone(const AIBinder_Weak* weak) {
+ if (weak == nullptr) {
+ return nullptr;
+ }
+
+ return new AIBinder_Weak{weak->binder};
+}
+
+bool AIBinder_lt(const AIBinder* lhs, const AIBinder* rhs) {
+ if (lhs == nullptr || rhs == nullptr) return lhs < rhs;
+
+ return const_cast<AIBinder*>(lhs)->getBinder() < const_cast<AIBinder*>(rhs)->getBinder();
+}
+
+bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs) {
+ if (lhs == nullptr || rhs == nullptr) return lhs < rhs;
+
+ return lhs->binder < rhs->binder;
+}
+
AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate,
AIBinder_Class_onDestroy onDestroy,
AIBinder_Class_onTransact onTransact)
: onCreate(onCreate),
onDestroy(onDestroy),
onTransact(onTransact),
- mInterfaceDescriptor(interfaceDescriptor) {}
+ mInterfaceDescriptor(interfaceDescriptor),
+ mWideInterfaceDescriptor(interfaceDescriptor) {}
AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor,
AIBinder_Class_onCreate onCreate,
@@ -335,6 +356,12 @@
clazz->handleShellCommand = handleShellCommand;
}
+const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) {
+ CHECK(clazz != nullptr) << "getDescriptor requires non-null clazz";
+
+ return clazz->getInterfaceDescriptorUtf8();
+}
+
void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
CHECK(who == mWho);
@@ -367,7 +394,7 @@
mDeathRecipients.end());
}
-binder_status_t AIBinder_DeathRecipient::linkToDeath(sp<IBinder> binder, void* cookie) {
+binder_status_t AIBinder_DeathRecipient::linkToDeath(const sp<IBinder>& binder, void* cookie) {
CHECK(binder != nullptr);
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
@@ -386,7 +413,7 @@
return STATUS_OK;
}
-binder_status_t AIBinder_DeathRecipient::unlinkToDeath(sp<IBinder> binder, void* cookie) {
+binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& binder, void* cookie) {
CHECK(binder != nullptr);
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
@@ -611,7 +638,7 @@
return STATUS_UNKNOWN_TRANSACTION;
}
- constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY;
+ constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY | FLAG_CLEAR_BUF;
if ((flags & ~kAllFlags) != 0) {
LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags;
return STATUS_BAD_VALUE;
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index f601127..6824306 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -112,21 +112,24 @@
AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate,
AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact);
- const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; }
+ const ::android::String16& getInterfaceDescriptor() const { return mWideInterfaceDescriptor; }
+ const char* getInterfaceDescriptorUtf8() const { return mInterfaceDescriptor.c_str(); }
// 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:
+ // Copy of the raw char string for when we don't have to return UTF-16
+ const std::string mInterfaceDescriptor;
// This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
// one.
- const ::android::String16 mInterfaceDescriptor;
+ const ::android::String16 mWideInterfaceDescriptor;
};
// Ownership is like this (when linked to death):
@@ -161,8 +164,8 @@
};
explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied);
- binder_status_t linkToDeath(::android::sp<::android::IBinder>, void* cookie);
- binder_status_t unlinkToDeath(::android::sp<::android::IBinder> binder, void* cookie);
+ binder_status_t linkToDeath(const ::android::sp<::android::IBinder>&, void* cookie);
+ binder_status_t unlinkToDeath(const ::android::sp<::android::IBinder>& binder, void* cookie);
private:
// When the user of this API deletes a Bp object but not the death recipient, the
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 18877af..53871f2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -27,6 +27,7 @@
#pragma once
#include <android/binder_ibinder.h>
+#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>
#include <android/binder_status.h>
@@ -74,6 +75,9 @@
* ownership of that other object.
*/
SpAIBinder& operator=(const SpAIBinder& other) {
+ if (this == &other) {
+ return *this;
+ }
AIBinder_incStrong(other.mBinder);
set(other.mBinder);
return *this;
@@ -111,6 +115,13 @@
*/
AIBinder** getR() { return &mBinder; }
+ bool operator!=(const SpAIBinder& rhs) const { return get() != rhs.get(); }
+ bool operator<(const SpAIBinder& rhs) const { return get() < rhs.get(); }
+ bool operator<=(const SpAIBinder& rhs) const { return get() <= rhs.get(); }
+ bool operator==(const SpAIBinder& rhs) const { return get() == rhs.get(); }
+ bool operator>(const SpAIBinder& rhs) const { return get() > rhs.get(); }
+ bool operator>=(const SpAIBinder& rhs) const { return get() >= rhs.get(); }
+
private:
AIBinder* mBinder = nullptr;
};
@@ -120,7 +131,7 @@
/**
* This baseclass owns a single object, used to make various classes RAII.
*/
-template <typename T, typename R, R (*Destroy)(T), T DEFAULT>
+template <typename T, void (*Destroy)(T), T DEFAULT>
class ScopedAResource {
public:
/**
@@ -170,8 +181,10 @@
ScopedAResource& operator=(const ScopedAResource&) = delete;
// move-constructing/assignment is okay
- ScopedAResource(ScopedAResource&& other) : mT(std::move(other.mT)) { other.mT = DEFAULT; }
- ScopedAResource& operator=(ScopedAResource&& other) {
+ ScopedAResource(ScopedAResource&& other) noexcept : mT(std::move(other.mT)) {
+ other.mT = DEFAULT;
+ }
+ ScopedAResource& operator=(ScopedAResource&& other) noexcept {
set(other.mT);
other.mT = DEFAULT;
return *this;
@@ -186,7 +199,7 @@
/**
* Convenience wrapper. See AParcel.
*/
-class ScopedAParcel : public impl::ScopedAResource<AParcel*, void, AParcel_delete, nullptr> {
+class ScopedAParcel : public impl::ScopedAResource<AParcel*, AParcel_delete, nullptr> {
public:
/**
* Takes ownership of a.
@@ -195,12 +208,19 @@
~ScopedAParcel() {}
ScopedAParcel(ScopedAParcel&&) = default;
ScopedAParcel& operator=(ScopedAParcel&&) = default;
+
+ bool operator!=(const ScopedAParcel& rhs) const { return get() != rhs.get(); }
+ bool operator<(const ScopedAParcel& rhs) const { return get() < rhs.get(); }
+ bool operator<=(const ScopedAParcel& rhs) const { return get() <= rhs.get(); }
+ bool operator==(const ScopedAParcel& rhs) const { return get() == rhs.get(); }
+ bool operator>(const ScopedAParcel& rhs) const { return get() > rhs.get(); }
+ bool operator>=(const ScopedAParcel& rhs) const { return get() >= rhs.get(); }
};
/**
* Convenience wrapper. See AStatus.
*/
-class ScopedAStatus : public impl::ScopedAResource<AStatus*, void, AStatus_delete, nullptr> {
+class ScopedAStatus : public impl::ScopedAResource<AStatus*, AStatus_delete, nullptr> {
public:
/**
* Takes ownership of a.
@@ -272,7 +292,7 @@
* Convenience wrapper. See AIBinder_DeathRecipient.
*/
class ScopedAIBinder_DeathRecipient
- : public impl::ScopedAResource<AIBinder_DeathRecipient*, void, AIBinder_DeathRecipient_delete,
+ : public impl::ScopedAResource<AIBinder_DeathRecipient*, AIBinder_DeathRecipient_delete,
nullptr> {
public:
/**
@@ -289,7 +309,7 @@
* Convenience wrapper. See AIBinder_Weak.
*/
class ScopedAIBinder_Weak
- : public impl::ScopedAResource<AIBinder_Weak*, void, AIBinder_Weak_delete, nullptr> {
+ : public impl::ScopedAResource<AIBinder_Weak*, AIBinder_Weak_delete, nullptr> {
public:
/**
* Takes ownership of a.
@@ -305,18 +325,38 @@
SpAIBinder promote() { return SpAIBinder(AIBinder_Weak_promote(get())); }
};
+namespace internal {
+
+static void closeWithError(int fd) {
+ if (fd == -1) return;
+ int ret = close(fd);
+ if (ret != 0) {
+ syslog(LOG_ERR, "Could not close FD %d: %s", fd, strerror(errno));
+ }
+}
+
+} // namespace internal
+
/**
* Convenience wrapper for a file descriptor.
*/
-class ScopedFileDescriptor : public impl::ScopedAResource<int, int, close, -1> {
+class ScopedFileDescriptor : public impl::ScopedAResource<int, internal::closeWithError, -1> {
public:
/**
* Takes ownership of a.
*/
- explicit ScopedFileDescriptor(int a = -1) : ScopedAResource(a) {}
+ ScopedFileDescriptor() : ScopedFileDescriptor(-1) {}
+ explicit ScopedFileDescriptor(int a) : ScopedAResource(a) {}
~ScopedFileDescriptor() {}
ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
+
+ bool operator!=(const ScopedFileDescriptor& rhs) const { return get() != rhs.get(); }
+ bool operator<(const ScopedFileDescriptor& rhs) const { return get() < rhs.get(); }
+ bool operator<=(const ScopedFileDescriptor& rhs) const { return get() <= rhs.get(); }
+ bool operator==(const ScopedFileDescriptor& rhs) const { return get() == rhs.get(); }
+ bool operator>(const ScopedFileDescriptor& rhs) const { return get() > rhs.get(); }
+ bool operator>=(const ScopedFileDescriptor& rhs) const { return get() >= rhs.get(); }
};
} // namespace ndk
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 a4f4441..a1102e2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -83,7 +83,8 @@
template <class T, class... Args>
static std::shared_ptr<T> make(Args&&... args) {
T* t = new T(std::forward<Args>(args)...);
- return t->template ref<T>();
+ // warning: Potential leak of memory pointed to by 't' [clang-analyzer-unix.Malloc]
+ return t->template ref<T>(); // NOLINT(clang-analyzer-unix.Malloc)
}
static void operator delete(void* p) { std::free(p); }
diff --git a/libs/binder/ndk/include_cpp/android/binder_internal_logging.h b/libs/binder/ndk/include_cpp/android/binder_internal_logging.h
new file mode 100644
index 0000000..88c6443
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/binder_internal_logging.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_internal_logging.h
+ * @brief This provides the ability to use syslog from binder headers, since
+ * other logging functionality might be inaccessable.
+ */
+
+#pragma once
+
+// defined differently by liblog
+#pragma push_macro("LOG_PRI")
+#ifdef LOG_PRI
+#undef LOG_PRI
+#endif
+#include <syslog.h>
+#pragma pop_macro("LOG_PRI")
+
+/** @} */
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 054aebe..83190aa 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -27,6 +27,7 @@
#pragma once
#include <android/binder_auto_utils.h>
+#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>
#include <optional>
@@ -179,6 +180,7 @@
static inline binder_status_t AParcel_writeRequiredStrongBinder(AParcel* parcel,
const SpAIBinder& binder) {
if (binder.get() == nullptr) {
+ syslog(LOG_ERR, "Passing null binder object as non-@nullable AIDL IBinder");
return STATUS_UNEXPECTED_NULL;
}
return AParcel_writeStrongBinder(parcel, binder.get());
@@ -228,6 +230,7 @@
static inline binder_status_t AParcel_writeRequiredParcelFileDescriptor(
AParcel* parcel, const ScopedFileDescriptor& fd) {
if (fd.get() < 0) {
+ syslog(LOG_ERR, "Passing -1 file descriptor as non-@nullable AIDL ParcelFileDescriptor");
return STATUS_UNEXPECTED_NULL;
}
return AParcel_writeParcelFileDescriptor(parcel, fd.get());
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index 6701518..6636a41 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -25,6 +25,8 @@
*/
#pragma once
+#include <android/binder_parcel_utils.h>
+#include <optional>
namespace ndk {
// Also see Parcelable.h in libbinder.
@@ -33,6 +35,101 @@
STABILITY_LOCAL,
STABILITY_VINTF, // corresponds to @VintfStability
};
+#define RETURN_ON_FAILURE(expr) \
+ do { \
+ binder_status_t _status = (expr); \
+ if (_status != STATUS_OK) return _status; \
+ } while (false)
+
+class AParcelableHolder {
+ public:
+ AParcelableHolder() = delete;
+ explicit AParcelableHolder(parcelable_stability_t stability)
+ : mParcel(AParcel_create()), mStability(stability) {}
+
+ virtual ~AParcelableHolder() = default;
+
+ binder_status_t writeToParcel(AParcel* parcel) const {
+ RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
+ RETURN_ON_FAILURE(AParcel_writeInt32(parcel, AParcel_getDataSize(this->mParcel.get())));
+ RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0,
+ AParcel_getDataSize(this->mParcel.get())));
+ return STATUS_OK;
+ }
+
+ binder_status_t readFromParcel(const AParcel* parcel) {
+ AParcel_reset(mParcel.get());
+
+ RETURN_ON_FAILURE(AParcel_readInt32(parcel, &this->mStability));
+ int32_t dataSize;
+ binder_status_t status = AParcel_readInt32(parcel, &dataSize);
+
+ if (status != STATUS_OK || dataSize < 0) {
+ return status != STATUS_OK ? status : STATUS_BAD_VALUE;
+ }
+
+ int32_t dataStartPos = AParcel_getDataPosition(parcel);
+
+ if (dataStartPos > INT32_MAX - dataSize) {
+ return STATUS_BAD_VALUE;
+ }
+
+ status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return AParcel_setDataPosition(parcel, dataStartPos + dataSize);
+ }
+
+ template <typename T>
+ binder_status_t setParcelable(const T& p) {
+ if (this->mStability > T::_aidl_stability) {
+ return STATUS_BAD_VALUE;
+ }
+ AParcel_reset(mParcel.get());
+ AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
+ p.writeToParcel(mParcel.get());
+ return STATUS_OK;
+ }
+
+ template <typename T>
+ binder_status_t getParcelable(std::optional<T>* ret) const {
+ const std::string parcelableDesc(T::descriptor);
+ AParcel_setDataPosition(mParcel.get(), 0);
+ if (AParcel_getDataSize(mParcel.get()) == 0) {
+ *ret = std::nullopt;
+ return STATUS_OK;
+ }
+ std::string parcelableDescInParcel;
+ binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
+ if (status != STATUS_OK || parcelableDesc != parcelableDescInParcel) {
+ *ret = std::nullopt;
+ return status;
+ }
+ *ret = std::make_optional<T>();
+ status = (*ret)->readFromParcel(this->mParcel.get());
+ if (status != STATUS_OK) {
+ *ret = std::nullopt;
+ return status;
+ }
+ return STATUS_OK;
+ }
+
+ void reset() { AParcel_reset(mParcel.get()); }
+
+ inline bool operator!=(const AParcelableHolder& rhs) const { return this != &rhs; }
+ inline bool operator<(const AParcelableHolder& rhs) const { return this < &rhs; }
+ inline bool operator<=(const AParcelableHolder& rhs) const { return this <= &rhs; }
+ inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; }
+ inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; }
+ inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; }
+
+ private:
+ mutable ndk::ScopedAParcel mParcel;
+ parcelable_stability_t mStability;
+};
+
+#undef RETURN_ON_FAILURE
} // namespace ndk
/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 33763d5..0ca3a07 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -43,7 +43,6 @@
#if __ANDROID_API__ >= 29
-// Also see TF_* in kernel's binder.h
typedef uint32_t binder_flags_t;
enum {
/**
@@ -643,6 +642,85 @@
#endif //__ANDROID_API__ >= 30
+#if __ANDROID_API__ >= 31
+
+/**
+ * Retrieve the class descriptor for the class.
+ *
+ * Available since API level 31.
+ *
+ * \param clazz the class to fetch the descriptor from
+ *
+ * \return the class descriptor string. This pointer will never be null; a
+ * descriptor is required to define a class. The pointer is owned by the class
+ * and will remain valid as long as the class does.
+ */
+const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) __INTRODUCED_IN(31);
+
+/**
+ * Whether AIBinder is less than another.
+ *
+ * This provides a per-process-unique total ordering of binders determined by
+ * an underlying allocation address where a null AIBinder* is considered to be
+ * ordered before all other binders.
+ *
+ * AIBinder* pointers themselves actually also create a per-process-unique total
+ * ordering. However, this ordering is inconsistent with AIBinder_Weak_lt for
+ * remote binders.
+ *
+ * Available since API level 31.
+ *
+ * \param lhs comparison object
+ * \param rhs comparison object
+ *
+ * \return whether "lhs < rhs" is true
+ */
+bool AIBinder_lt(const AIBinder* lhs, const AIBinder* rhs);
+
+/**
+ * Clone an AIBinder_Weak. Useful because even if a weak binder promotes to a
+ * null value, after further binder transactions, it may no longer promote to a
+ * null value.
+ *
+ * Available since API level 31.
+ *
+ * \param weak Object to clone
+ *
+ * \return clone of the input parameter. This must be deleted with
+ * AIBinder_Weak_delete. Null if weak input parameter is also null.
+ */
+AIBinder_Weak* AIBinder_Weak_clone(const AIBinder_Weak* weak);
+
+/**
+ * Whether AIBinder_Weak is less than another.
+ *
+ * This provides a per-process-unique total ordering of binders which is exactly
+ * the same as AIBinder_lt. Similarly, a null AIBinder_Weak* is considered to be
+ * ordered before all other weak references.
+ *
+ * If you have many AIBinder_Weak* objects which are all references to distinct
+ * binder objects which happen to have the same underlying address (as ordered
+ * by AIBinder_lt), these AIBinder_Weak* objects will retain the same order with
+ * respect to all other AIBinder_Weak* pointers with different underlying
+ * addresses and are also guaranteed to have a per-process-unique ordering. That
+ * is, even though multiple AIBinder* instances may happen to be allocated at
+ * the same underlying address, this function will still correctly distinguish
+ * that these are weak pointers to different binder objects.
+ *
+ * Unlike AIBinder*, the AIBinder_Weak* addresses themselves have nothing to do
+ * with the underlying binder.
+ *
+ * Available since API level 31.
+ *
+ * \param lhs comparison object
+ * \param rhs comparison object
+ *
+ * \return whether "lhs < rhs" is true
+ */
+bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs);
+
+#endif //__ANDROID_API__ >= 31
+
__END_DECLS
/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index a031e29..93c3f32 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -1120,6 +1120,53 @@
// @END-PRIMITIVE-READ-WRITE
#endif //__ANDROID_API__ >= 29
+#if __ANDROID_API__ >= 31
+/**
+ * Reset the parcel to the initial status.
+ *
+ * Available since API level 31.
+ *
+ * \param parcel The parcel of which to be reset.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AParcel_reset(AParcel* parcel) __INTRODUCED_IN(31);
+
+/**
+ * Gets the size of the parcel.
+ *
+ * Available since API level 31.
+ *
+ * \param parcel The parcel of which to get the size.
+ *
+ * \return The size of the parcel.
+ */
+int32_t AParcel_getDataSize(const AParcel* parcel) __INTRODUCED_IN(31);
+
+/**
+ * Copy the data of a parcel to other parcel.
+ *
+ * Available since API level 31.
+ *
+ * \param from The source
+ * \param to The detination
+ * \param start The position where the copied data starts.
+ * \param size The amount of data which will be copied.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AParcel_appendFrom(const AParcel* from, AParcel* to, int32_t start, int32_t size)
+ __INTRODUCED_IN(31);
+
+/**
+ * Creates a parcel.
+ *
+ * Available since API level 31.
+ *
+ * \return A parcel which is not related to any IBinder objects.
+ */
+AParcel* AParcel_create() __INTRODUCED_IN(31);
+#endif //__ANDROID_API__ >= 31
__END_DECLS
/** @} */
diff --git a/libs/binder/ndk/include_platform/android/binder_context.h b/libs/binder/ndk/include_platform/android/binder_context.h
deleted file mode 100644
index a99d555..0000000
--- a/libs/binder/ndk/include_platform/android/binder_context.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <android/binder_ibinder.h>
-
-__BEGIN_DECLS
-
-/**
- * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
- * must be called on a local binder server before it is sent out to any othe
- * process. If this is a remote binder, it will abort. If the kernel doesn't
- * support this feature, you'll always get null from AIBinder_getCallingSid.
- *
- * \param binder local server binder to request security contexts on
- */
-__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
- __INTRODUCED_IN(31);
-
-/**
- * Returns the selinux context of the callee.
- *
- * In order for this to work, the following conditions must be met:
- * - The kernel must be new enough to support this feature.
- * - The server must have called AIBinder_setRequestingSid.
- * - The callee must be a remote process.
- *
- * \return security context or null if unavailable. The lifetime of this context
- * is the lifetime of the transaction.
- */
-__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
-
-__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index 2af65cf..e315c79 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -16,39 +16,43 @@
#pragma once
-// binder_context.h used to be part of this header and is included for backwards
-// compatibility.
-#include <android/binder_context.h>
-
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
-
#include <android/binder_ibinder.h>
-#include <binder/IBinder.h>
+
+__BEGIN_DECLS
+
+// platform values for binder_flags_t
+enum {
+ /**
+ * The transaction and reply will be cleared by the kernel in read-only
+ * binder buffers storing transactions.
+ *
+ * Introduced in API level 31.
+ */
+ FLAG_CLEAR_BUF = 0x20,
+};
/**
- * Get libbinder version of binder from AIBinder.
+ * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
+ * must be called on a local binder server before it is sent out to any othe
+ * process. If this is a remote binder, it will abort. If the kernel doesn't
+ * support this feature, you'll always get null from AIBinder_getCallingSid.
*
- * WARNING: function calls to a local object on the other side of this function
- * will parcel. When converting between binders, keep in mind it is not as
- * efficient as a direct function call.
- *
- * \param binder binder with ownership retained by the client
- * \return platform binder object
+ * \param binder local server binder to request security contexts on
*/
-android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder);
+__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
+ __INTRODUCED_IN(31);
/**
- * Get libbinder_ndk version of binder from platform binder.
+ * Returns the selinux context of the callee.
*
- * WARNING: function calls to a local object on the other side of this function
- * will parcel. When converting between binders, keep in mind it is not as
- * efficient as a direct function call.
+ * In order for this to work, the following conditions must be met:
+ * - The kernel must be new enough to support this feature.
+ * - The server must have called AIBinder_setRequestingSid.
+ * - The callee must be a remote process.
*
- * \param binder platform binder which may be from anywhere (doesn't have to be
- * created with libbinder_ndK)
- * \return binder with one reference count of ownership given to the client. See
- * AIBinder_decStrong
+ * \return security context or null if unavailable. The lifetime of this context
+ * is the lifetime of the transaction.
*/
-AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
+__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
-#endif
+__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h
new file mode 100644
index 0000000..f0c00e8
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_libbinder.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+
+#include <android/binder_ibinder.h>
+#include <binder/IBinder.h>
+
+/**
+ * Get libbinder version of binder from AIBinder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder binder with ownership retained by the client
+ * \return platform binder object
+ */
+android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder);
+
+/**
+ * Get libbinder_ndk version of binder from platform binder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder platform binder which may be from anywhere (doesn't have to be
+ * created with libbinder_ndK)
+ * \return binder with one reference count of ownership given to the client. See
+ * AIBinder_decStrong
+ */
+AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
+
+#endif
diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
index 114a781..d54c1a1 100644
--- a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
@@ -33,4 +33,15 @@
*/
bool AParcel_getAllowFds(const AParcel*);
+/**
+ * Data written to the parcel will be zero'd before being deleted or realloced.
+ *
+ * The main use of this is marking a parcel that will be used in a transaction
+ * with FLAG_CLEAR_BUF. When FLAG_CLEAR_BUF is used, the reply parcel will
+ * automatically be marked as sensitive when it is created.
+ *
+ * \param parcel The parcel to clear associated data from.
+ */
+void AParcel_markSensitive(const AParcel* parcel);
+
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 1701fb5..9a93bf3 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -120,11 +120,21 @@
AServiceManager_isDeclared; # apex llndk
AServiceManager_registerLazyService; # llndk
AServiceManager_waitForService; # apex llndk
+
+ AIBinder_Class_getDescriptor;
+ AIBinder_Weak_clone;
+ AIBinder_Weak_lt;
+ AIBinder_lt;
+ AParcel_appendFrom;
+ AParcel_create;
+ AParcel_getDataSize;
+ AParcel_reset;
};
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
+ AParcel_markSensitive;
extern "C++" {
AIBinder_fromPlatformBinder*;
AIBinder_toPlatformBinder*;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 722ae23..ec7c7d8 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -226,6 +226,10 @@
return parcel->get()->dataPosition();
}
+void AParcel_markSensitive(const AParcel* parcel) {
+ return parcel->get()->markSensitive();
+}
+
binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) {
sp<IBinder> writeBinder = binder != nullptr ? binder->getBinder() : nullptr;
return parcel->get()->writeStrongBinder(writeBinder);
@@ -257,7 +261,7 @@
}
binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd) {
- std::unique_ptr<ParcelFileDescriptor> parcelFd;
+ std::optional<ParcelFileDescriptor> parcelFd;
status_t status = parcel->get()->readParcelable(&parcelFd);
if (status != STATUS_OK) return PruneStatusT(status);
@@ -647,4 +651,22 @@
return parcel->get()->allowFds();
}
+binder_status_t AParcel_reset(AParcel* parcel) {
+ parcel->get()->freeData();
+ return STATUS_OK;
+}
+
+int32_t AParcel_getDataSize(const AParcel* parcel) {
+ return parcel->get()->dataSize();
+}
+
+binder_status_t AParcel_appendFrom(const AParcel* from, AParcel* to, int32_t start, int32_t size) {
+ status_t status = to->get()->appendFrom(from->get(), start, size);
+ return PruneStatusT(status);
+}
+
+AParcel* AParcel_create() {
+ return new AParcel(nullptr);
+}
+
// @END
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index 64832f3..2afe5d2 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -25,7 +25,7 @@
const char* IFoo::kSomeInstanceName = "libbinder_ndk-test-IFoo";
const char* IFoo::kInstanceNameToDieFor = "libbinder_ndk-test-IFoo-to-die";
-const char* kIFooDescriptor = "my-special-IFoo-class";
+const char* IFoo::kIFooDescriptor = "my-special-IFoo-class";
struct IFoo_Class_Data {
sp<IFoo> foo;
@@ -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..7408d0c 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -27,9 +27,13 @@
public:
static const char* kSomeInstanceName;
static const char* kInstanceNameToDieFor;
+ static const char* kIFooDescriptor;
static AIBinder_Class* kClass;
+ // binder representing this interface with one reference count
+ AIBinder* getBinder();
+
// Takes ownership of IFoo
binder_status_t addService(const char* instance);
static ::android::sp<IFoo> getService(const char* instance, AIBinder** outBinder = nullptr);
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 44d8ebf..b7df115 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -18,9 +18,9 @@
#include <aidl/BnBinderNdkUnitTest.h>
#include <aidl/BnEmpty.h>
#include <android-base/logging.h>
-#include <android/binder_context.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>
@@ -39,6 +39,7 @@
#include <condition_variable>
#include <iostream>
#include <mutex>
+#include "android/binder_ibinder.h"
using namespace android;
@@ -185,6 +186,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);
@@ -523,6 +544,10 @@
EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"}));
}
+TEST(NdkBinder, GetClassInterfaceDescriptor) {
+ ASSERT_STREQ(IFoo::kIFooDescriptor, AIBinder_Class_getDescriptor(IFoo::kClass));
+}
+
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/libs/binder/parcel_fuzzer/Android.bp b/libs/binder/parcel_fuzzer/Android.bp
index c5b3d80..3e6fe99 100644
--- a/libs/binder/parcel_fuzzer/Android.bp
+++ b/libs/binder/parcel_fuzzer/Android.bp
@@ -52,6 +52,11 @@
cc_library_static {
name: "libbinder_random_parcel",
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
srcs: [
"random_fd.cpp",
"random_parcel.cpp",
diff --git a/libs/binder/parcel_fuzzer/binder.cpp b/libs/binder/parcel_fuzzer/binder.cpp
index e5c6333..394d222 100644
--- a/libs/binder/parcel_fuzzer/binder.cpp
+++ b/libs/binder/parcel_fuzzer/binder.cpp
@@ -86,6 +86,8 @@
PARCEL_READ_WITH_STATUS(T, FUN), \
PARCEL_READ_NO_STATUS(T, FUN)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// clang-format off
std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS {
PARCEL_READ_NO_STATUS(size_t, dataSize),
@@ -130,7 +132,6 @@
PARCEL_READ_OPT_STATUS(uint64_t, readUint64),
PARCEL_READ_OPT_STATUS(float, readFloat),
PARCEL_READ_OPT_STATUS(double, readDouble),
- PARCEL_READ_OPT_STATUS(intptr_t, readIntPtr),
PARCEL_READ_OPT_STATUS(bool, readBool),
PARCEL_READ_OPT_STATUS(char16_t, readChar),
PARCEL_READ_OPT_STATUS(int8_t, readByte),
@@ -290,3 +291,4 @@
PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable),
};
// clang-format on
+#pragma clang diagnostic pop
diff --git a/libs/binder/parcel_fuzzer/binder_ndk.cpp b/libs/binder/parcel_fuzzer/binder_ndk.cpp
index 29da8f7..008780c 100644
--- a/libs/binder/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/parcel_fuzzer/binder_ndk.cpp
@@ -18,6 +18,7 @@
#include "binder_ndk.h"
#include <android/binder_parcel_utils.h>
+#include <android/binder_parcelable_utils.h>
#include "util.h"
@@ -54,6 +55,25 @@
binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR());
FUZZ_LOG() << "read status header: " << status;
},
+ [](const NdkParcelAdapter& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to getDataSize the parcel";
+ AParcel_getDataSize(p.aParcel());
+ FUZZ_LOG() << "getDataSize done";
+ },
+ [](const NdkParcelAdapter& p, uint8_t data) {
+ FUZZ_LOG() << "about to read a ParcelableHolder";
+ ndk::AParcelableHolder ph {(data % 2 == 1) ? ndk::STABILITY_LOCAL : ndk::STABILITY_VINTF};
+ binder_status_t status = AParcel_readParcelable(p.aParcel(), &ph);
+ FUZZ_LOG() << "read the ParcelableHolder: " << status;
+ },
+ [](const NdkParcelAdapter& p, uint8_t data) {
+ FUZZ_LOG() << "about to appendFrom";
+ AParcel* parcel = AParcel_create();
+ binder_status_t status = AParcel_appendFrom(p.aParcel(), parcel, 0, data);
+ AParcel_delete(parcel);
+ FUZZ_LOG() << "appendFrom: " << status;
+ },
+
PARCEL_READ(int32_t, AParcel_readInt32),
PARCEL_READ(uint32_t, AParcel_readUint32),
PARCEL_READ(int64_t, AParcel_readInt64),
diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/parcel_fuzzer/main.cpp
index 386c70b..78606cc 100644
--- a/libs/binder/parcel_fuzzer/main.cpp
+++ b/libs/binder/parcel_fuzzer/main.cpp
@@ -20,12 +20,16 @@
#include "hwbinder.h"
#include "util.h"
+#include <iostream>
+
#include <android-base/logging.h>
#include <fuzzbinder/random_parcel.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <cstdlib>
#include <ctime>
+#include <sys/resource.h>
+#include <sys/time.h>
using android::fillRandomParcel;
@@ -77,7 +81,25 @@
}
}
+size_t getHardMemoryLimit() {
+ struct rlimit limit;
+ CHECK(0 == getrlimit(RLIMIT_AS, &limit)) << errno;
+ return limit.rlim_max;
+}
+
+void setMemoryLimit(size_t cur, size_t max) {
+ const struct rlimit kLimit = {
+ .rlim_cur = cur,
+ .rlim_max = max,
+ };
+ CHECK(0 == setrlimit(RLIMIT_AS, &kLimit)) << errno;
+}
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static constexpr size_t kMemLimit = 1 * 1024 * 1024;
+ size_t hardLimit = getHardMemoryLimit();
+ setMemoryLimit(std::min(kMemLimit, hardLimit), hardLimit);
+
if (size <= 1) return 0; // no use
// avoid timeouts, see b/142617274, b/142473153
@@ -102,5 +124,7 @@
provider.PickValueInArray(fuzzBackend)(std::move(provider));
+ setMemoryLimit(hardLimit, hardLimit);
+
return 0;
}
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index dc8270e..fd5f2f5 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -10,6 +10,11 @@
"libbinder_ndk_sys",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
}
rust_library {
@@ -23,6 +28,11 @@
"libbinder_ndk",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
}
rust_bindgen {
@@ -64,6 +74,9 @@
"-D__ANDROID_API__=10000",
],
},
+ darwin: {
+ enabled: false,
+ },
},
}
diff --git a/libs/binder/rust/TEST_MAPPING b/libs/binder/rust/TEST_MAPPING
deleted file mode 100644
index 50c474c..0000000
--- a/libs/binder/rust/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "presubmit": [
- {
- "name": "libbinder_rs-internal_test"
- }
- ],
- "postsubmit": [
- {
- "name": "rustBinderTest"
- }
- ]
-}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 6d0a369..ed3b9ec 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -21,7 +21,8 @@
use crate::proxy::{DeathRecipient, SpIBinder};
use crate::sys;
-use std::ffi::{c_void, CString};
+use std::ffi::{c_void, CStr, CString};
+use std::os::raw::c_char;
use std::os::unix::io::AsRawFd;
use std::ptr;
@@ -33,8 +34,7 @@
/// Additional operation flags.
///
-/// Can be either 0 for a normal RPC, or [`IBinder::FLAG_ONEWAY`] for a
-/// one-way RPC.
+/// `IBinder::FLAG_*` values.
pub type TransactionFlags = u32;
/// Super-trait for Binder interfaces.
@@ -91,6 +91,8 @@
/// Corresponds to TF_ONE_WAY -- an asynchronous call.
const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
+ /// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
+ const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
/// Is this object still alive?
fn is_binder_alive(&self) -> bool;
@@ -204,6 +206,22 @@
pub(crate) unsafe fn from_ptr(ptr: *const sys::AIBinder_Class) -> InterfaceClass {
InterfaceClass(ptr)
}
+
+ /// Get the interface descriptor string of this class.
+ pub fn get_descriptor(&self) -> String {
+ unsafe {
+ // SAFETY: The descriptor returned by AIBinder_Class_getDescriptor
+ // is always a two-byte null terminated sequence of u16s. Thus, we
+ // can continue reading from the pointer until we hit a null value,
+ // and this pointer can be a valid slice if the slice length is <=
+ // the number of u16 elements before the null terminator.
+
+ let raw_descriptor: *const c_char = sys::AIBinder_Class_getDescriptor(self.0);
+ CStr::from_ptr(raw_descriptor).to_str()
+ .expect("Expected valid UTF-8 string from AIBinder_Class_getDescriptor")
+ .into()
+ }
+ }
}
impl From<InterfaceClass> for *const sys::AIBinder_Class {
@@ -506,12 +524,7 @@
}
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)
- }
+ Ok(Self { binder, $($fname: $finit),* })
}
}
@@ -566,16 +579,35 @@
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 existing_class = ibinder.get_class();
+ if let Some(class) = existing_class {
+ if class != <$native as $crate::Remotable>::get_class() &&
+ class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+ {
+ // The binder object's descriptor string matches what we
+ // expect. We still need to treat this local or already
+ // associated object as remote, because we can't cast it
+ // into a Rust service object without a matching class
+ // pointer.
+ return Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?));
+ }
}
- 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)?))
+ if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
+ let service: $crate::Result<$crate::Binder<$native>> =
+ std::convert::TryFrom::try_from(ibinder.clone());
+ if let Ok(service) = service {
+ // We were able to associate with our expected class and
+ // the service is local.
+ return Ok(Box::new(service));
+ } else {
+ // Service is remote
+ return Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?));
+ }
}
+
+ Err($crate::StatusCode::BAD_TYPE.into())
}
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 8ee6a62..edfb56a 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -122,7 +122,7 @@
pub use super::parcel::ParcelFileDescriptor;
pub use super::{add_service, get_interface};
pub use super::{
- ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode,
+ ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, WpIBinder,
};
/// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 2c1e5a4..6c34824 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -100,6 +100,14 @@
// Data serialization methods
impl Parcel {
+ /// Data written to parcelable is zero'd before being deleted or reallocated.
+ pub fn mark_sensitive(&mut self) {
+ unsafe {
+ // Safety: guaranteed to have a parcel object, and this method never fails
+ sys::AParcel_markSensitive(self.as_native())
+ }
+ }
+
/// Write a type that implements [`Serialize`] to the `Parcel`.
pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
parcelable.serialize(self)
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 5002fc6..17af099 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -91,7 +91,7 @@
/// Return the interface class of this binder object, if associated with
/// one.
- pub(crate) fn get_class(&mut self) -> Option<InterfaceClass> {
+ pub 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
@@ -102,6 +102,11 @@
class.as_ref().map(|p| InterfaceClass::from_ptr(p))
}
}
+
+ /// Creates a new weak reference to this binder object.
+ pub fn downgrade(&mut self) -> WpIBinder {
+ WpIBinder::new(self)
+ }
}
/// An object that can be associate with an [`InterfaceClass`].
@@ -370,15 +375,25 @@
/// 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!`].
+/// This struct encapsulates the generic C++ `wp<IBinder>` class. This wrapper
+/// is untyped; typed interface access is implemented by the AIDL compiler.
pub struct WpIBinder(*mut sys::AIBinder_Weak);
+impl fmt::Debug for WpIBinder {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad("WpIBinder")
+ }
+}
+
+/// # Safety
+///
+/// A `WpIBinder` is a handle to a C++ IBinder, which is thread-safe.
+unsafe impl Send for WpIBinder {}
+
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 {
+ 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`.
@@ -401,6 +416,16 @@
}
}
+impl Drop for WpIBinder {
+ fn drop(&mut self) {
+ unsafe {
+ // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so we
+ // know this pointer is safe to pass to `AIBinder_Weak_delete` here.
+ sys::AIBinder_Weak_delete(self.0);
+ }
+ }
+}
+
/// Rust wrapper around DeathRecipient objects.
#[repr(C)]
pub struct DeathRecipient {
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index 303f4a5..ef142b5 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include <android/binder_context.h>
#include <android/binder_ibinder.h>
+#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
#include <android/binder_parcel.h>
+#include <android/binder_parcel_platform.h>
#include <android/binder_process.h>
#include <android/binder_shell.h>
#include <android/binder_status.h>
@@ -78,6 +79,7 @@
enum {
FLAG_ONEWAY = FLAG_ONEWAY,
+ FLAG_CLEAR_BUF = FLAG_CLEAR_BUF,
};
} // namespace consts
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index 3db40ba..5ae9c53 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -30,3 +30,52 @@
auto_gen_config: false,
test_suites: ["general-tests"],
}
+
+cc_test {
+ name: "binderRustNdkInteropTest",
+ srcs: [
+ "binderRustNdkInteropTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "IBinderRustNdkInteropTest-ndk_platform",
+ "libbinder_ndk_rust_interop",
+ ],
+ test_suites: ["general-tests"],
+ require_root: true,
+
+ // rustBinderTestService uses a custom config
+ auto_gen_config: true,
+}
+
+aidl_interface {
+ name: "IBinderRustNdkInteropTest",
+ unstable: true,
+ srcs: [
+ "IBinderRustNdkInteropTest.aidl",
+ "IBinderRustNdkInteropTestOther.aidl",
+ ],
+ backend: {
+ ndk: {
+ enabled: true,
+ },
+ rust: {
+ enabled: true,
+ },
+ },
+}
+
+rust_ffi_static {
+ name: "libbinder_ndk_rust_interop",
+ crate_name: "binder_ndk_rust_interop",
+ srcs: [
+ "ndk_rust_interop.rs",
+ ],
+ rustlibs: [
+ "libbinder_rs",
+ "IBinderRustNdkInteropTest-rust",
+ ],
+}
diff --git a/libs/binder/rust/tests/IBinderRustNdkInteropTest.aidl b/libs/binder/rust/tests/IBinderRustNdkInteropTest.aidl
new file mode 100644
index 0000000..7f5e837
--- /dev/null
+++ b/libs/binder/rust/tests/IBinderRustNdkInteropTest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface IBinderRustNdkInteropTest {
+ @utf8InCpp String echo(@utf8InCpp String str);
+}
diff --git a/libs/binder/rust/tests/IBinderRustNdkInteropTestOther.aidl b/libs/binder/rust/tests/IBinderRustNdkInteropTestOther.aidl
new file mode 100644
index 0000000..82a0323
--- /dev/null
+++ b/libs/binder/rust/tests/IBinderRustNdkInteropTestOther.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface IBinderRustNdkInteropTestOther {
+ @utf8InCpp String echo(@utf8InCpp String str);
+}
diff --git a/libs/binder/rust/tests/binderRustNdkInteropTest.cpp b/libs/binder/rust/tests/binderRustNdkInteropTest.cpp
new file mode 100644
index 0000000..59ca6ed
--- /dev/null
+++ b/libs/binder/rust/tests/binderRustNdkInteropTest.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/BnBinderRustNdkInteropTest.h>
+#include <aidl/IBinderRustNdkInteropTest.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/Status.h>
+#include <gtest/gtest.h>
+
+using namespace android;
+using ::ndk::ScopedAStatus;
+using ::ndk::SharedRefBase;
+using ::ndk::SpAIBinder;
+
+static const char* kNdkServerName = "NdkServer-BinderRustNdkInteropTest";
+static const char* kRustServerName = "RustServer-BinderRustNdkInteropTest";
+
+extern "C" {
+int rust_call_ndk(const char* service_name);
+int rust_start_service(const char* service_name);
+}
+
+class NdkServer : public aidl::BnBinderRustNdkInteropTest {
+ ScopedAStatus echo(const std::string& in, std::string* out) override {
+ *out = in;
+ return ScopedAStatus::ok();
+ }
+};
+
+TEST(RustNdkInterop, RustCanCallNdk) {
+ ASSERT_EQ(STATUS_OK, rust_call_ndk(kNdkServerName));
+}
+
+TEST(RustNdkInterop, NdkCanCallRust) {
+ ASSERT_EQ(STATUS_OK, rust_start_service(kRustServerName));
+
+ SpAIBinder binder = SpAIBinder(AServiceManager_checkService(kRustServerName));
+ ASSERT_NE(nullptr, binder.get());
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+
+ auto interface = aidl::IBinderRustNdkInteropTest::fromBinder(binder);
+ // TODO(b/167723746): this test requires that fromBinder allow association
+ // with an already associated local binder by treating it as remote.
+ EXPECT_EQ(interface, nullptr);
+
+ // std::string in("testing");
+ // std::string out;
+ // EXPECT_TRUE(interface->echo(in, &out).isOk());
+ // EXPECT_EQ(in, out);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // so we can host a client and service concurrently
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ std::shared_ptr<NdkServer> ndkServer = SharedRefBase::make<NdkServer>();
+ EXPECT_EQ(STATUS_OK, AServiceManager_addService(ndkServer->asBinder().get(), kNdkServerName));
+
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 953d328..bb8c492 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -173,6 +173,30 @@
}
}
+/// Trivial testing binder interface
+pub trait ITestSameDescriptor: Interface {}
+
+declare_binder_interface! {
+ ITestSameDescriptor["android.os.ITest"] {
+ native: BnTestSameDescriptor(on_transact_same_descriptor),
+ proxy: BpTestSameDescriptor,
+ }
+}
+
+fn on_transact_same_descriptor(
+ _service: &dyn ITestSameDescriptor,
+ _code: TransactionCode,
+ _data: &Parcel,
+ _reply: &mut Parcel,
+) -> binder::Result<()> {
+ Ok(())
+}
+
+impl ITestSameDescriptor for BpTestSameDescriptor {}
+
+impl ITestSameDescriptor for Binder<BnTestSameDescriptor> {}
+
+
#[cfg(test)]
mod tests {
use selinux_bindgen as selinux_sys;
@@ -185,9 +209,9 @@
use std::thread;
use std::time::Duration;
- use binder::{DeathRecipient, FromIBinder, IBinder, SpIBinder, StatusCode};
+ use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode};
- use super::{ITest, RUST_SERVICE_BINARY};
+ use super::{BnTest, ITest, ITestSameDescriptor, RUST_SERVICE_BINARY, TestService};
pub struct ScopedServiceProcess(Child);
@@ -435,4 +459,40 @@
assert_eq!(extension.test().unwrap(), extension_name);
}
}
+
+ /// Test re-associating a local binder object with a different class.
+ ///
+ /// This is needed because different binder service (e.g. NDK vs Rust)
+ /// implementations are incompatible and must not be interchanged. A local
+ /// service with the same descriptor string but a different class pointer
+ /// may have been created by an NDK service and is therefore incompatible
+ /// with the Rust service implementation. It must be treated as remote and
+ /// all API calls parceled and sent through transactions.
+ ///
+ /// Further tests of this behavior with the C NDK and Rust API are in
+ /// rust_ndk_interop.rs
+ #[test]
+ fn associate_existing_class() {
+ let service = Binder::new(BnTest(Box::new(TestService {
+ s: "testing_service".to_string(),
+ })));
+
+ // This should succeed although we will have to treat the service as
+ // remote.
+ let _interface: Box<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder())
+ .expect("Could not re-interpret service as the ITestSameDescriptor interface");
+ }
+
+ /// Test that we can round-trip a rust service through a generic IBinder
+ #[test]
+ fn reassociate_rust_binder() {
+ let service_name = "testing_service";
+ let service_ibinder = BnTest::new_binder(TestService { s: service_name.to_string() })
+ .as_binder();
+
+ let service: Box<dyn ITest> = service_ibinder.into_interface()
+ .expect("Could not reassociate the generic ibinder");
+
+ assert_eq!(service.test().unwrap(), service_name);
+ }
}
diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs
new file mode 100644
index 0000000..70a6dc0
--- /dev/null
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Rust Binder NDK interop tests
+
+use std::ffi::CStr;
+use std::os::raw::{c_char, c_int};
+use ::IBinderRustNdkInteropTest::binder::{self, Interface, StatusCode};
+use ::IBinderRustNdkInteropTest::aidl::IBinderRustNdkInteropTest::{
+ BnBinderRustNdkInteropTest, IBinderRustNdkInteropTest,
+};
+use ::IBinderRustNdkInteropTest::aidl::IBinderRustNdkInteropTestOther::{
+ IBinderRustNdkInteropTestOther,
+};
+
+/// Look up the provided AIDL service and call its echo method.
+///
+/// # Safety
+///
+/// service_name must be a valid, non-null C-style string (null-terminated).
+#[no_mangle]
+pub unsafe extern "C" fn rust_call_ndk(service_name: *const c_char) -> c_int {
+ let service_name = CStr::from_ptr(service_name).to_str().unwrap();
+
+ // The Rust class descriptor pointer will not match the NDK one, but the
+ // descriptor strings match so this needs to still associate.
+ let service: Box<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) {
+ Err(e) => {
+ eprintln!("Could not find Ndk service {}: {:?}", service_name, e);
+ return StatusCode::NAME_NOT_FOUND as c_int;
+ }
+ Ok(service) => service,
+ };
+
+ match service.echo("testing") {
+ Ok(s) => if s != "testing" {
+ return StatusCode::BAD_VALUE as c_int;
+ },
+ Err(e) => return e.into(),
+ }
+
+ // Try using the binder service through the wrong interface type
+ let wrong_service: Result<Box<dyn IBinderRustNdkInteropTestOther>, StatusCode> =
+ binder::get_interface(service_name);
+ match wrong_service {
+ Err(e) if e == StatusCode::BAD_TYPE => {}
+ Err(e) => {
+ eprintln!("Trying to use a service via the wrong interface errored with unexpected error {:?}", e);
+ return e as c_int;
+ }
+ Ok(_) => {
+ eprintln!("We should not be allowed to use a service via the wrong interface");
+ return StatusCode::BAD_TYPE as c_int;
+ }
+ }
+
+ StatusCode::OK as c_int
+}
+
+struct Service;
+
+impl Interface for Service {}
+
+impl IBinderRustNdkInteropTest for Service {
+ fn echo(&self, s: &str) -> binder::Result<String> {
+ Ok(s.to_string())
+ }
+}
+
+/// Start the interop Echo test service with the given service name.
+///
+/// # Safety
+///
+/// service_name must be a valid, non-null C-style string (null-terminated).
+#[no_mangle]
+pub unsafe extern "C" fn rust_start_service(service_name: *const c_char) -> c_int {
+ let service_name = CStr::from_ptr(service_name).to_str().unwrap();
+ let service = BnBinderRustNdkInteropTest::new_binder(Service);
+ match binder::add_service(&service_name, service.as_binder()) {
+ Ok(_) => StatusCode::OK as c_int,
+ Err(e) => e as c_int,
+ }
+}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index a03835b..988f7f3 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -60,6 +60,23 @@
require_root: true,
}
+// unit test only, which can run on host and doesn't use /dev/binder
+cc_test {
+ name: "binderParcelTest",
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ srcs: ["binderParcelTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ test_suites: ["general-tests"],
+}
+
cc_test {
name: "binderLibTest",
defaults: ["binder_test_defaults"],
@@ -140,6 +157,24 @@
require_root: true,
}
+cc_test {
+ name: "binderClearBufTest",
+ defaults: ["binder_test_defaults"],
+ srcs: [
+ "binderClearBufTest.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+
+ test_suites: ["general-tests"],
+ require_root: true,
+}
+
aidl_interface {
name: "binderStabilityTestIface",
unstable: true,
diff --git a/libs/binder/tests/binderClearBufTest.cpp b/libs/binder/tests/binderClearBufTest.cpp
new file mode 100644
index 0000000..a565e72
--- /dev/null
+++ b/libs/binder/tests/binderClearBufTest.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+#include <gtest/gtest.h>
+
+#include <sys/prctl.h>
+#include <thread>
+
+using namespace android;
+
+const String16 kServerName = String16("binderClearBuf");
+
+std::string hexString(const void* bytes, size_t len) {
+ if (bytes == nullptr) return "<null>";
+
+ const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+ char chars[] = "0123456789abcdef";
+ std::string result;
+ result.resize(len * 2);
+
+ for (size_t i = 0; i < len; i++) {
+ result[2 * i] = chars[bytes8[i] >> 4];
+ result[2 * i + 1] = chars[bytes8[i] & 0xf];
+ }
+
+ return result;
+}
+
+class FooBar : public BBinder {
+ public:
+ enum {
+ TRANSACTION_REPEAT_STRING = IBinder::FIRST_CALL_TRANSACTION,
+ };
+
+ std::mutex foo;
+ std::string last;
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ // not checking data, since there is no hook at the time this test is
+ // written to check values there are set to zero. Instead, we only check
+ // the reply parcel.
+
+ switch (code) {
+ case TRANSACTION_REPEAT_STRING: {
+ const char* str = data.readCString();
+ return reply->writeCString(str == nullptr ? "<null>" : str);
+ }
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ static std::string RepeatString(const sp<IBinder> binder,
+ const std::string& repeat,
+ std::string* outBuffer) {
+ Parcel data;
+ data.writeCString(repeat.c_str());
+ std::string result;
+ const uint8_t* lastReply;
+ size_t lastReplySize;
+ {
+ Parcel reply;
+ binder->transact(TRANSACTION_REPEAT_STRING, data, &reply, FLAG_CLEAR_BUF);
+ result = reply.readCString();
+ lastReply = reply.data();
+ lastReplySize = reply.dataSize();
+ }
+ IPCThreadState::self()->flushCommands();
+ *outBuffer = hexString(lastReply, lastReplySize);
+ return result;
+ }
+};
+
+TEST(BinderClearBuf, ClearKernelBuffer) {
+ sp<IBinder> binder = defaultServiceManager()->getService(kServerName);
+ ASSERT_NE(nullptr, binder);
+
+ std::string replyBuffer;
+ std::string result = FooBar::RepeatString(binder, "foo", &replyBuffer);
+ EXPECT_EQ("foo", result);
+
+ // the buffer must have at least some length for the string, but we will
+ // just check it has some length, to avoid assuming anything about the
+ // format
+ EXPECT_GT(replyBuffer.size(), 0);
+
+ for (size_t i = 0; i < replyBuffer.size(); i++) {
+ EXPECT_EQ(replyBuffer[i], '0') << "reply buffer at " << i;
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+
+ sp<IBinder> server = new FooBar;
+ android::defaultServiceManager()->addService(kServerName, server);
+
+ IPCThreadState::self()->joinThreadPool(true);
+ exit(1); // should not reach
+ }
+
+ // This is not racey. Just giving these services some time to register before we call
+ // getService which sleeps for much longer. One alternative would be to
+ // start a threadpool + use waitForService, but we want to have as few
+ // binder things going on in this test as possible, since we are checking
+ // memory is zero'd which the kernel has a right to change.
+ usleep(100000);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 98f0868..a5261e5 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -30,6 +30,7 @@
#include <binder/IServiceManager.h>
#include <private/binder/binder_module.h>
+#include <linux/sched.h>
#include <sys/epoll.h>
#include <sys/prctl.h>
@@ -53,6 +54,7 @@
static constexpr int kSchedPolicy = SCHED_RR;
static constexpr int kSchedPriority = 7;
+static constexpr int kSchedPriorityMore = 8;
static String16 binderLibTestServiceName = String16("test.binderLib");
@@ -402,6 +404,21 @@
EXPECT_EQ(NO_ERROR, ret);
}
+TEST_F(BinderLibTest, NopTransactionOneway) {
+ status_t ret;
+ Parcel data, reply;
+ ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY);
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, NopTransactionClear) {
+ status_t ret;
+ Parcel data, reply;
+ // make sure it accepts the transaction flag
+ ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF);
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
TEST_F(BinderLibTest, Freeze) {
status_t ret;
Parcel data, reply, replypid;
@@ -1080,6 +1097,25 @@
EXPECT_EQ(kSchedPriority, priority);
}
+TEST_F(BinderLibTest, InheritRt) {
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+
+ const struct sched_param param {
+ .sched_priority = kSchedPriorityMore,
+ };
+ EXPECT_EQ(0, sched_setscheduler(getpid(), SCHED_RR, ¶m));
+
+ Parcel data, reply;
+ status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ int policy = reply.readInt32();
+ int priority = reply.readInt32();
+
+ EXPECT_EQ(kSchedPolicy, policy & (~SCHED_RESET_ON_FORK));
+ EXPECT_EQ(kSchedPriorityMore, priority);
+}
TEST_F(BinderLibTest, VectorSent) {
Parcel data, reply;
@@ -1153,9 +1189,6 @@
virtual status_t onTransact(uint32_t code,
const Parcel& data, Parcel* reply,
uint32_t flags = 0) {
- //printf("%s: code %d\n", __func__, code);
- (void)flags;
-
if (getuid() != (uid_t)IPCThreadState::self()->getCallingUid()) {
return PERMISSION_DENIED;
}
@@ -1229,8 +1262,12 @@
return NO_ERROR;
case BINDER_LIB_TEST_NOP_TRANSACTION_WAIT:
usleep(5000);
- return NO_ERROR;
+ [[fallthrough]];
case BINDER_LIB_TEST_NOP_TRANSACTION:
+ // oneway error codes should be ignored
+ if (flags & TF_ONE_WAY) {
+ return UNKNOWN_ERROR;
+ }
return NO_ERROR;
case BINDER_LIB_TEST_DELAYED_CALL_BACK: {
// Note: this transaction is only designed for use with a
@@ -1452,6 +1489,8 @@
testService->setMinSchedulerPolicy(kSchedPolicy, kSchedPriority);
+ testService->setInheritRt(true);
+
/*
* Normally would also contain functionality as well, but we are only
* testing the extension mechanism.
diff --git a/libs/binder/tests/binderParcelTest.cpp b/libs/binder/tests/binderParcelTest.cpp
new file mode 100644
index 0000000..1764228
--- /dev/null
+++ b/libs/binder/tests/binderParcelTest.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <gtest/gtest.h>
+
+using android::IPCThreadState;
+using android::OK;
+using android::Parcel;
+using android::String16;
+using android::String8;
+using android::status_t;
+
+// Tests a second operation results in a parcel at the same location as it
+// started.
+void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
+ Parcel p;
+ a(&p);
+ size_t end = p.dataPosition();
+ p.setDataPosition(0);
+ b(&p);
+ EXPECT_EQ(end, p.dataPosition());
+}
+
+TEST(Parcel, InverseInterfaceToken) {
+ const String16 token = String16("asdf");
+ parcelOpSameLength([&] (Parcel* p) {
+ p->writeInterfaceToken(token);
+ }, [&] (Parcel* p) {
+ EXPECT_TRUE(p->enforceInterface(token, IPCThreadState::self()));
+ });
+}
+
+TEST(Parcel, Utf8FromUtf16Read) {
+ const char* token = "asdf";
+ parcelOpSameLength([&] (Parcel* p) {
+ p->writeString16(String16(token));
+ }, [&] (Parcel* p) {
+ std::string s;
+ EXPECT_EQ(OK, p->readUtf8FromUtf16(&s));
+ EXPECT_EQ(token, s);
+ });
+}
+
+TEST(Parcel, Utf8AsUtf16Write) {
+ std::string token = "asdf";
+ parcelOpSameLength([&] (Parcel* p) {
+ p->writeUtf8AsUtf16(token);
+ }, [&] (Parcel* p) {
+ String16 s;
+ EXPECT_EQ(OK, p->readString16(&s));
+ EXPECT_EQ(s, String16(token.c_str()));
+ });
+}
+
+template <typename T>
+using readFunc = status_t (Parcel::*)(T* out) const;
+template <typename T>
+using writeFunc = status_t (Parcel::*)(const T& in);
+template <typename T>
+using copyWriteFunc = status_t (Parcel::*)(T in);
+
+template <typename T, typename WRITE_FUNC>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, WRITE_FUNC w) {
+ for (const T& value : ts) {
+ parcelOpSameLength([&] (Parcel* p) {
+ (*p.*w)(value);
+ }, [&] (Parcel* p) {
+ T outValue;
+ EXPECT_EQ(OK, (*p.*r)(&outValue));
+ EXPECT_EQ(value, outValue);
+ });
+ }
+}
+
+template <typename T>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, writeFunc<T> w) {
+ readWriteInverse<T, writeFunc<T>>(std::move(ts), r, w);
+}
+template <typename T>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, copyWriteFunc<T> w) {
+ readWriteInverse<T, copyWriteFunc<T>>(std::move(ts), r, w);
+}
+
+#define TEST_READ_WRITE_INVERSE(type, name, ...) \
+ TEST(Parcel, Inverse##name) { \
+ readWriteInverse<type>(__VA_ARGS__, &Parcel::read##name, &Parcel::write##name); \
+ }
+
+TEST_READ_WRITE_INVERSE(int32_t, Int32, {-2, -1, 0, 1, 2});
+TEST_READ_WRITE_INVERSE(uint32_t, Uint32, {0, 1, 2});
+TEST_READ_WRITE_INVERSE(int64_t, Int64, {-2, -1, 0, 1, 2});
+TEST_READ_WRITE_INVERSE(uint64_t, Uint64, {0, 1, 2});
+TEST_READ_WRITE_INVERSE(float, Float, {-1.0f, 0.0f, 3.14f});
+TEST_READ_WRITE_INVERSE(double, Double, {-1.0, 0.0, 3.14});
+TEST_READ_WRITE_INVERSE(bool, Bool, {true, false});
+TEST_READ_WRITE_INVERSE(char16_t, Char, {u'a', u'\0'});
+TEST_READ_WRITE_INVERSE(int8_t, Byte, {-1, 0, 1});
+TEST_READ_WRITE_INVERSE(String8, String8, {String8(), String8("a"), String8("asdf")});
+TEST_READ_WRITE_INVERSE(String16, String16, {String16(), String16("a"), String16("asdf")});
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 09f58cc..ffb3ef2 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -36,12 +36,15 @@
#include <optional>
#include <sys/eventfd.h>
+#include <sys/prctl.h>
using namespace std::chrono_literals; // NOLINT - google-build-using-namespace
namespace android {
namespace tests {
+static const String16 kServiceName("SafeInterfaceTest");
+
enum class TestEnum : uint32_t {
INVALID = 0,
INITIAL = 1,
@@ -184,7 +187,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wexit-time-destructors"
-IMPLEMENT_META_INTERFACE(Callback, "android.gfx.tests.ICallback");
+IMPLEMENT_META_INTERFACE(Callback, "android.gfx.tests.ICallback")
#pragma clang diagnostic pop
class BnCallback : public SafeBnInterface<ICallback> {
@@ -373,7 +376,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wexit-time-destructors"
-IMPLEMENT_META_INTERFACE(SafeInterfaceTest, "android.gfx.tests.ISafeInterfaceTest");
+IMPLEMENT_META_INTERFACE(SafeInterfaceTest, "android.gfx.tests.ISafeInterfaceTest")
static sp<IBinder::DeathRecipient> getDeathRecipient() {
static sp<IBinder::DeathRecipient> recipient = new ExitOnDeath;
@@ -601,40 +604,13 @@
static constexpr const char* getLogTag() { return "SafeInterfaceTest"; }
sp<ISafeInterfaceTest> getRemoteService() {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wexit-time-destructors"
- static std::mutex sMutex;
- static sp<ISafeInterfaceTest> sService;
- static sp<IBinder> sDeathToken = new BBinder;
-#pragma clang diagnostic pop
+ sp<IBinder> binder = defaultServiceManager()->getService(kServiceName);
+ sp<ISafeInterfaceTest> iface = interface_cast<ISafeInterfaceTest>(binder);
+ EXPECT_TRUE(iface != nullptr);
- std::unique_lock<decltype(sMutex)> lock;
- if (sService == nullptr) {
- ALOG(LOG_INFO, getLogTag(), "Forking remote process");
- pid_t forkPid = fork();
- EXPECT_NE(forkPid, -1);
+ iface->setDeathToken(new BBinder);
- const String16 serviceName("SafeInterfaceTest");
-
- if (forkPid == 0) {
- ALOG(LOG_INFO, getLogTag(), "Remote process checking in");
- sp<ISafeInterfaceTest> nativeService = new BnSafeInterfaceTest;
- defaultServiceManager()->addService(serviceName,
- IInterface::asBinder(nativeService));
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
- // We shouldn't get to this point
- [&]() { FAIL(); }();
- }
-
- sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
- sService = interface_cast<ISafeInterfaceTest>(binder);
- EXPECT_TRUE(sService != nullptr);
-
- sService->setDeathToken(sDeathToken);
- }
-
- return sService;
+ return iface;
}
};
@@ -840,5 +816,23 @@
ASSERT_EQ(b + 1, bPlusOne);
}
+extern "C" int main(int argc, char **argv) {
+ testing::InitGoogleTest(&argc, argv);
+
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ sp<BnSafeInterfaceTest> nativeService = new BnSafeInterfaceTest;
+ status_t status = defaultServiceManager()->addService(kServiceName, nativeService);
+ if (status != OK) {
+ ALOG(LOG_INFO, "SafeInterfaceServer", "could not register");
+ return EXIT_FAILURE;
+ }
+ IPCThreadState::self()->joinThreadPool();
+ return EXIT_FAILURE;
+ }
+
+ return RUN_ALL_TESTS();
+}
+
} // namespace tests
} // namespace android
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp
index 46379fc..5531296 100644
--- a/libs/binder/tests/fuzzers/Android.bp
+++ b/libs/binder/tests/fuzzers/Android.bp
@@ -26,6 +26,11 @@
"libutils",
"libbase",
],
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
}
cc_fuzz {
@@ -64,3 +69,18 @@
defaults: ["binder_fuzz_defaults"],
srcs: ["TextOutputFuzz.cpp"],
}
+
+cc_fuzz {
+ name: "binder_bufferedTextOutputFuzz",
+ include_dirs: [
+ "frameworks/native/libs/binder",
+ ],
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["BufferedTextOutputFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_memoryDealerFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["MemoryDealerFuzz.cpp"],
+}
diff --git a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
index 9ac65bb..69f1b9d 100644
--- a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
+++ b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
@@ -37,8 +37,8 @@
bbinder->isRequestingSid();
},
[](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
- bool request_sid = fdp->ConsumeBool();
- bbinder->setRequestingSid(request_sid);
+ bool requestSid = fdp->ConsumeBool();
+ bbinder->setRequestingSid(requestSid);
},
[](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
bbinder->getExtension();
@@ -63,6 +63,13 @@
[](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
bbinder->getMinSchedulerPriority();
},
+ [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+ bool inheritRt = fdp->ConsumeBool();
+ bbinder->setInheritRt(inheritRt);
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->isInheritRt();
+ },
[](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
bbinder->getDebugPid();
}};
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
index c685b41..6ca0e2f 100644
--- a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
+++ b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
@@ -48,9 +48,7 @@
static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BpBinder>&,
const sp<IBinder::DeathRecipient>&)>>
gBPBinderOperations =
- {[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
- const sp<IBinder::DeathRecipient>&) -> void { bpbinder->handle(); },
- [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ {[](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
const sp<IBinder::DeathRecipient>& s_recipient) -> void {
// Clean up possible leftover memory.
wp<IBinder::DeathRecipient> outRecipient(nullptr);
diff --git a/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
new file mode 100644
index 0000000..09cb216
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <string>
+#include <vector>
+#include "BufferedTextOutput.h"
+
+namespace android {
+
+class FuzzBufferedTextOutput : public BufferedTextOutput {
+public:
+ FuzzBufferedTextOutput(uint32_t flags) : BufferedTextOutput(flags) {}
+ virtual status_t writeLines(const struct iovec& buf, size_t) {
+ size_t len = buf.iov_len;
+ void* tmp_buf = malloc(len);
+
+ if (tmp_buf == NULL) {
+ return status_t();
+ }
+
+ // This will attempt to read data from iov_base to ensure valid params were passed.
+ memcpy(tmp_buf, buf.iov_base, len);
+ free(tmp_buf);
+ return status_t();
+ }
+};
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ uint32_t flags = fdp.ConsumeIntegral<uint32_t>();
+ size_t push_count = 0;
+ std::shared_ptr<BufferedTextOutput> bTextOutput(new FuzzBufferedTextOutput(flags));
+
+ while (fdp.remaining_bytes() > 0) {
+ fdp.PickValueInArray<std::function<void()>>({
+ [&]() -> void {
+ bTextOutput->pushBundle();
+ push_count++;
+ },
+ [&]() -> void {
+ std::string txt = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ size_t len = fdp.ConsumeIntegralInRange<size_t>(0, txt.length());
+ bTextOutput->print(txt.c_str(), len);
+ },
+ [&]() -> void {
+ if (push_count == 0) return;
+
+ bTextOutput->popBundle();
+ push_count--;
+ },
+ })();
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp b/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
new file mode 100644
index 0000000..f9dda8c
--- /dev/null
+++ b/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/MemoryDealer.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <string>
+#include <unordered_set>
+
+namespace android {
+
+static constexpr size_t kMaxBufferSize = 10000;
+static constexpr size_t kMaxDealerSize = 1024 * 512;
+static constexpr size_t kMaxAllocSize = 1024;
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size > kMaxBufferSize) {
+ return 0;
+ }
+
+ FuzzedDataProvider fdp(data, size);
+ size_t dSize = fdp.ConsumeIntegralInRange<size_t>(0, kMaxDealerSize);
+ std::string name = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ uint32_t flags = fdp.ConsumeIntegral<uint32_t>();
+ sp<MemoryDealer> dealer = new MemoryDealer(dSize, name.c_str(), flags);
+
+ // This is used to track offsets that have been freed already to avoid an expected fatal log.
+ std::unordered_set<size_t> free_list;
+
+ while (fdp.remaining_bytes() > 0) {
+ fdp.PickValueInArray<std::function<void()>>({
+ [&]() -> void { dealer->getAllocationAlignment(); },
+ [&]() -> void { dealer->getMemoryHeap(); },
+ [&]() -> void {
+ size_t offset = fdp.ConsumeIntegral<size_t>();
+
+ // Offset has already been freed, so return instead.
+ if (free_list.find(offset) != free_list.end()) return;
+
+ dealer->deallocate(offset);
+ free_list.insert(offset);
+ },
+ [&]() -> void {
+ std::string randString = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ dealer->dump(randString.c_str());
+ },
+ [&]() -> void {
+ size_t allocSize = fdp.ConsumeIntegralInRange<size_t>(0, kMaxAllocSize);
+ sp<IMemory> allocated = dealer->allocate(allocSize);
+ // If the allocation was successful, try to write to it
+ if (allocated != nullptr && allocated->unsecurePointer() != nullptr) {
+ memset(allocated->unsecurePointer(), 1, allocated->size());
+
+ // Clear the address from freelist since it has been allocated over again.
+ free_list.erase(allocated->offset());
+ }
+ },
+ })();
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp
index ab4c56a..56d958c 100644
--- a/libs/binder/tests/schd-dbg.cpp
+++ b/libs/binder/tests/schd-dbg.cpp
@@ -245,7 +245,7 @@
double best = (double)m_best / 1.0E6;
double worst = (double)m_worst / 1.0E6;
double average = (double)m_total_time / m_transactions / 1.0E6;
- // FIXME: libjson?
+ // TODO: libjson?
int W = DUMP_PRESICION + 2;
cout << setprecision(DUMP_PRESICION) << "{ \"avg\":" << setw(W) << left
<< average << ",\"wst\":" << setw(W) << left << worst
@@ -376,7 +376,7 @@
if (is_client(num)) {
int no_trans = iterations * 2;
double sync_ratio = (1.0 - (double)no_sync / no_trans);
- // FIXME: libjson?
+ // TODO: libjson?
cout << "\"P" << (num - server_count) << "\":{\"SYNC\":\""
<< ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\","
<< "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << ","
@@ -466,7 +466,7 @@
}
vector<Pipe> pipes;
thread_dump("main");
- // FIXME: libjson?
+ // TODO: libjson?
cout << "{" << endl;
cout << "\"cfg\":{\"pair\":" << (no_process / 2)
<< ",\"iterations\":" << iterations << ",\"deadline_us\":" << deadline_us
@@ -495,7 +495,7 @@
// detected in the child process
no_inherent += status;
}
- // FIXME: libjson?
+ // TODO: libjson?
cout << "\"inheritance\": " << (no_inherent == 0 ? "\"PASS\"" : "\"FAIL\"")
<< endl;
cout << "}" << endl;
diff --git a/libs/binderdebug/Android.bp b/libs/binderdebug/Android.bp
new file mode 100644
index 0000000..343246a
--- /dev/null
+++ b/libs/binderdebug/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+ name: "libbinderdebug",
+ vendor_available: true,
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ ],
+ srcs: [
+ "BinderDebug.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+}
diff --git a/libs/binderdebug/BinderDebug.cpp b/libs/binderdebug/BinderDebug.cpp
new file mode 100644
index 0000000..b435dba
--- /dev/null
+++ b/libs/binderdebug/BinderDebug.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <binder/Binder.h>
+#include <sys/types.h>
+#include <fstream>
+#include <regex>
+
+#include <binderdebug/BinderDebug.h>
+
+namespace android {
+
+static std::string contextToString(BinderDebugContext context) {
+ switch (context) {
+ case BinderDebugContext::BINDER:
+ return "binder";
+ case BinderDebugContext::HWBINDER:
+ return "hwbinder";
+ case BinderDebugContext::VNDBINDER:
+ return "vndbinder";
+ default:
+ return std::string();
+ }
+}
+
+static status_t scanBinderContext(pid_t pid, const std::string& contextName,
+ std::function<void(const std::string&)> eachLine) {
+ std::ifstream ifs("/dev/binderfs/binder_logs/proc/" + std::to_string(pid));
+ if (!ifs.is_open()) {
+ ifs.open("/d/binder/proc/" + std::to_string(pid));
+ if (!ifs.is_open()) {
+ return -errno;
+ }
+ }
+ static const std::regex kContextLine("^context (\\w+)$");
+
+ bool isDesiredContext = false;
+ std::string line;
+ std::smatch match;
+ while (getline(ifs, line)) {
+ if (std::regex_search(line, match, kContextLine)) {
+ isDesiredContext = match.str(1) == contextName;
+ continue;
+ }
+ if (!isDesiredContext) {
+ continue;
+ }
+ eachLine(line);
+ }
+ return OK;
+}
+
+status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo) {
+ std::smatch match;
+ static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
+ static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
+ std::string contextStr = contextToString(context);
+ status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) {
+ if (std::regex_search(line, match, kReferencePrefix)) {
+ const std::string& ptrString = "0x" + match.str(2); // use number after c
+ uint64_t ptr;
+ if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
+ // Should not reach here, but just be tolerant.
+ return;
+ }
+ const std::string proc = " proc ";
+ auto pos = line.rfind(proc);
+ if (pos != std::string::npos) {
+ for (const std::string& pidStr : base::Split(line.substr(pos + proc.size()), " ")) {
+ int32_t pid;
+ if (!::android::base::ParseInt(pidStr, &pid)) {
+ return;
+ }
+ pidInfo->refPids[ptr].push_back(pid);
+ }
+ }
+
+ return;
+ }
+ if (std::regex_search(line, match, kThreadPrefix)) {
+ // "1" is waiting in binder driver
+ // "2" is poll. It's impossible to tell if these are in use.
+ // and HIDL default code doesn't use it.
+ bool isInUse = match.str(1) != "1";
+ // "0" is a thread that has called into binder
+ // "1" is looper thread
+ // "2" is main looper thread
+ bool isBinderThread = match.str(2) != "0";
+ if (!isBinderThread) {
+ return;
+ }
+ if (isInUse) {
+ pidInfo->threadUsage++;
+ }
+
+ pidInfo->threadCount++;
+ return;
+ }
+ return;
+ });
+ return ret;
+}
+
+} // namespace android
diff --git a/libs/binderdebug/TEST_MAPPING b/libs/binderdebug/TEST_MAPPING
new file mode 100644
index 0000000..2f3353e
--- /dev/null
+++ b/libs/binderdebug/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libbinderdebug_test"
+ }
+ ]
+}
diff --git a/libs/binderdebug/include/binderdebug/BinderDebug.h b/libs/binderdebug/include/binderdebug/BinderDebug.h
new file mode 100644
index 0000000..14a0ef3
--- /dev/null
+++ b/libs/binderdebug/include/binderdebug/BinderDebug.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <map>
+#include <vector>
+
+namespace android {
+
+struct BinderPidInfo {
+ std::map<uint64_t, std::vector<pid_t>> refPids; // cookie -> processes which hold binder
+ uint32_t threadUsage; // number of threads in use
+ uint32_t threadCount; // number of threads total
+};
+
+enum class BinderDebugContext {
+ BINDER,
+ HWBINDER,
+ VNDBINDER,
+};
+
+status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo);
+
+} // namespace android
diff --git a/libs/binderdebug/tests/Android.bp b/libs/binderdebug/tests/Android.bp
new file mode 100644
index 0000000..4c06b1d
--- /dev/null
+++ b/libs/binderdebug/tests/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "libbinderdebug_test",
+ test_suites: ["general-tests"],
+ srcs: [
+ "binderdebug_test.cpp",
+ "android/binderdebug/test/IControl.aidl",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libutils",
+ ],
+ static_libs: ["libbinderdebug"],
+ cflags: ["-Wall", "-Werror"],
+ require_root: true,
+}
diff --git a/libs/binderdebug/tests/android/binderdebug/test/IControl.aidl b/libs/binderdebug/tests/android/binderdebug/test/IControl.aidl
new file mode 100644
index 0000000..8efeb63
--- /dev/null
+++ b/libs/binderdebug/tests/android/binderdebug/test/IControl.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.binderdebug.test;
+
+interface IControl {
+ // Notifies the service to continue execution
+ void Continue();
+}
diff --git a/libs/binderdebug/tests/binderdebug_test.cpp b/libs/binderdebug/tests/binderdebug_test.cpp
new file mode 100644
index 0000000..ea799c0
--- /dev/null
+++ b/libs/binderdebug/tests/binderdebug_test.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/IPCThreadState.h>
+#include <binderdebug/BinderDebug.h>
+#include <gtest/gtest.h>
+#include <semaphore.h>
+#include <thread>
+
+#include <android/binderdebug/test/BnControl.h>
+#include <android/binderdebug/test/IControl.h>
+
+namespace android {
+namespace binderdebug {
+namespace test {
+
+class Control : public BnControl {
+public:
+ Control() {sem_init(&s, 1, 0);};
+ ::android::binder::Status Continue() override;
+ sem_t s;
+};
+
+::android::binder::Status Control::Continue() {
+ IPCThreadState::self()->flushCommands();
+ sem_post(&s);
+ return binder::Status::ok();
+}
+
+TEST(BinderDebugTests, BinderPid) {
+ BinderPidInfo pidInfo;
+ const auto& status = getBinderPidInfo(BinderDebugContext::BINDER, getpid(), &pidInfo);
+ ASSERT_EQ(status, OK);
+ // There should be one referenced PID for servicemanager
+ EXPECT_TRUE(!pidInfo.refPids.empty());
+}
+
+TEST(BinderDebugTests, BinderThreads) {
+ BinderPidInfo pidInfo;
+ const auto& status = getBinderPidInfo(BinderDebugContext::BINDER, getpid(), &pidInfo);
+ ASSERT_EQ(status, OK);
+ EXPECT_TRUE(pidInfo.threadUsage <= pidInfo.threadCount);
+ // The second looper thread can sometimes take longer to spawn.
+ EXPECT_GE(pidInfo.threadCount, 1);
+}
+
+extern "C" {
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // Create a child/client process to call into the main process so we can ensure
+ // looper thread has been registered before attempting to get the BinderPidInfo
+ pid_t pid = fork();
+ if (pid == 0) {
+ sp<IBinder> binder = android::defaultServiceManager()->getService(String16("binderdebug"));
+ sp<IControl> service;
+ if (binder != nullptr) {
+ service = android::interface_cast<IControl>(binder);
+ }
+ service->Continue();
+ exit(0);
+ }
+ sp<Control> iface = new Control;
+ android::defaultServiceManager()->addService(String16("binderdebug"), iface);
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(8);
+ ProcessState::self()->startThreadPool();
+ sem_wait(&iface->s);
+
+ return RUN_ALL_TESTS();
+}
+} // extern "C"
+} // namespace test
+} // namespace binderdebug
+} // namespace android
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 88752ee..08c62df 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -19,6 +19,11 @@
double_loadable: true,
vendor_available: true,
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
shared_libs: [
"libbinder",
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
index b1943a4..e3cd085 100644
--- a/libs/cputimeinstate/Android.bp
+++ b/libs/cputimeinstate/Android.bp
@@ -33,5 +33,6 @@
"-Wall",
"-Wextra",
],
+ require_root: true,
}
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 5e785b6..2e72cc4 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -56,9 +56,11 @@
static std::vector<std::vector<uint32_t>> gPolicyFreqs;
static std::vector<std::vector<uint32_t>> gPolicyCpus;
static std::set<uint32_t> gAllFreqs;
+static unique_fd gTisTotalMapFd;
static unique_fd gTisMapFd;
static unique_fd gConcurrentMapFd;
static unique_fd gUidLastUpdateMapFd;
+static unique_fd gPidTisMapFd;
static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) {
std::string data;
@@ -128,6 +130,10 @@
gPolicyCpus.emplace_back(*cpus);
}
+ gTisTotalMapFd =
+ unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_total_time_in_state_map")};
+ if (gTisTotalMapFd < 0) return false;
+
gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
if (gTisMapFd < 0) return false;
@@ -139,6 +145,12 @@
unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_last_update_map")};
if (gUidLastUpdateMapFd < 0) return false;
+ gPidTisMapFd = unique_fd{mapRetrieveRO(BPF_FS_PATH "map_time_in_state_pid_time_in_state_map")};
+ if (gPidTisMapFd < 0) return false;
+
+ unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_map"));
+ if (trackedPidMapFd < 0) return false;
+
gInitialized = true;
return true;
}
@@ -222,7 +234,8 @@
}
gTracking = attachTracepointProgram("sched", "sched_switch") &&
- attachTracepointProgram("power", "cpu_frequency");
+ attachTracepointProgram("power", "cpu_frequency") &&
+ attachTracepointProgram("sched", "sched_process_free");
return gTracking;
}
@@ -231,6 +244,31 @@
return gPolicyFreqs;
}
+std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes() {
+ if (!gInitialized && !initGlobals()) return {};
+
+ std::vector<std::vector<uint64_t>> out;
+ uint32_t maxFreqCount = 0;
+ for (const auto &freqList : gPolicyFreqs) {
+ if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size();
+ out.emplace_back(freqList.size(), 0);
+ }
+
+ std::vector<uint64_t> vals(gNCpus);
+ const uint32_t freqCount = maxFreqCount <= MAX_FREQS_FOR_TOTAL ? maxFreqCount :
+ MAX_FREQS_FOR_TOTAL;
+ for (uint32_t freqIdx = 0; freqIdx < freqCount; ++freqIdx) {
+ if (findMapEntry(gTisTotalMapFd, &freqIdx, vals.data())) return {};
+ for (uint32_t policyIdx = 0; policyIdx < gNPolicies; ++policyIdx) {
+ if (freqIdx >= gPolicyFreqs[policyIdx].size()) continue;
+ for (const auto &cpu : gPolicyCpus[policyIdx]) {
+ out[policyIdx][freqIdx] += vals[cpu];
+ }
+ }
+ }
+
+ return out;
+}
// Retrieve the times in ns that uid spent running at each CPU frequency.
// Return contains no value on error, otherwise it contains a vector of vectors using the format:
// [[t0_0, t0_1, ...],
@@ -425,6 +463,7 @@
uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0;
do {
+ if (key.bucket > (gNCpus - 1) / CPUS_PER_ENTRY) return {};
if (lastUpdate) {
auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate);
if (!uidUpdated.has_value()) return {};
@@ -501,5 +540,106 @@
return true;
}
+bool startTrackingProcessCpuTimes(pid_t pid) {
+ if (!gInitialized && !initGlobals()) return false;
+
+ unique_fd trackedPidHashMapFd(
+ mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_hash_map"));
+ if (trackedPidHashMapFd < 0) return false;
+
+ unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_map"));
+ if (trackedPidMapFd < 0) return false;
+
+ for (uint32_t index = 0; index < MAX_TRACKED_PIDS; index++) {
+ // Find first available [index, pid] entry in the pid_tracked_hash_map map
+ if (writeToMapEntry(trackedPidHashMapFd, &index, &pid, BPF_NOEXIST) != 0) {
+ if (errno != EEXIST) {
+ return false;
+ }
+ continue; // This index is already taken
+ }
+
+ tracked_pid_t tracked_pid = {.pid = pid, .state = TRACKED_PID_STATE_ACTIVE};
+ if (writeToMapEntry(trackedPidMapFd, &index, &tracked_pid, BPF_ANY) != 0) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+// Marks the specified task identified by its PID (aka TID) for CPU time-in-state tracking
+// aggregated with other tasks sharing the same TGID and aggregation key.
+bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) {
+ if (!gInitialized && !initGlobals()) return false;
+
+ unique_fd taskAggregationMapFd(
+ mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_task_aggregation_map"));
+ if (taskAggregationMapFd < 0) return false;
+
+ return writeToMapEntry(taskAggregationMapFd, &pid, &aggregationKey, BPF_ANY) == 0;
+}
+
+// Retrieves the times in ns that each thread spent running at each CPU freq, aggregated by
+// aggregation key.
+// Return contains no value on error, otherwise it contains a map from aggregation keys
+// to vectors of vectors using the format:
+// { aggKey0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
+// aggKey1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... }
+// where ti_j_k is the ns tid i spent running on the jth cluster at the cluster's kth lowest freq.
+std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+getAggregatedTaskCpuFreqTimes(pid_t tgid, const std::vector<uint16_t> &aggregationKeys) {
+ if (!gInitialized && !initGlobals()) return {};
+
+ uint32_t maxFreqCount = 0;
+ std::vector<std::vector<uint64_t>> mapFormat;
+ for (const auto &freqList : gPolicyFreqs) {
+ if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size();
+ mapFormat.emplace_back(freqList.size(), 0);
+ }
+
+ bool dataCollected = false;
+ std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map;
+ std::vector<tis_val_t> vals(gNCpus);
+ for (uint16_t aggregationKey : aggregationKeys) {
+ map.emplace(aggregationKey, mapFormat);
+
+ aggregated_task_tis_key_t key{.tgid = tgid, .aggregation_key = aggregationKey};
+ for (key.bucket = 0; key.bucket <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++key.bucket) {
+ if (findMapEntry(gPidTisMapFd, &key, vals.data()) != 0) {
+ if (errno != ENOENT) {
+ return {};
+ }
+ continue;
+ } else {
+ dataCollected = true;
+ }
+
+ // Combine data by aggregating time-in-state data grouped by CPU cluster aka policy.
+ uint32_t offset = key.bucket * FREQS_PER_ENTRY;
+ uint32_t nextOffset = offset + FREQS_PER_ENTRY;
+ for (uint32_t j = 0; j < gNPolicies; ++j) {
+ if (offset >= gPolicyFreqs[j].size()) continue;
+ auto begin = map[key.aggregation_key][j].begin() + offset;
+ auto end = nextOffset < gPolicyFreqs[j].size() ? begin + FREQS_PER_ENTRY
+ : map[key.aggregation_key][j].end();
+ for (const auto &cpu : gPolicyCpus[j]) {
+ std::transform(begin, end, std::begin(vals[cpu].ar), begin,
+ std::plus<uint64_t>());
+ }
+ }
+ }
+ }
+
+ if (!dataCollected) {
+ // Check if eBPF is supported on this device. If it is, gTisMap should not be empty.
+ time_key_t key;
+ if (getFirstMapKey(gTisMapFd, &key) != 0) {
+ return {};
+ }
+ }
+ return map;
+}
+
} // namespace bpf
} // namespace android
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
index b7600f5..46de669 100644
--- a/libs/cputimeinstate/cputimeinstate.h
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -23,6 +23,7 @@
namespace bpf {
bool startTrackingUidTimes();
+std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes();
std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid);
std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
getUidsCpuFreqTimes();
@@ -41,5 +42,10 @@
getUidsUpdatedConcurrentTimes(uint64_t *lastUpdate);
bool clearUidTimes(unsigned int uid);
+bool startTrackingProcessCpuTimes(pid_t pid);
+bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey);
+std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+getAggregatedTaskCpuFreqTimes(pid_t pid, const std::vector<uint16_t> &aggregationKeys);
+
} // namespace bpf
} // namespace android
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index ea2a200..d25b2e9 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -19,6 +19,8 @@
#include <sys/sysinfo.h>
+#include <pthread.h>
+#include <semaphore.h>
#include <numeric>
#include <unordered_map>
#include <vector>
@@ -38,6 +40,12 @@
using std::vector;
+TEST(TimeInStateTest, TotalTimeInState) {
+ auto times = getTotalCpuFreqTimes();
+ ASSERT_TRUE(times.has_value());
+ EXPECT_FALSE(times->empty());
+}
+
TEST(TimeInStateTest, SingleUidTimeInState) {
auto times = getUidCpuFreqTimes(0);
ASSERT_TRUE(times.has_value());
@@ -184,6 +192,31 @@
}
}
+TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) {
+ auto allUid = getUidsCpuFreqTimes();
+ auto total = getTotalCpuFreqTimes();
+
+ ASSERT_TRUE(allUid.has_value() && total.has_value());
+
+ // Check the number of policies.
+ ASSERT_EQ(allUid->at(0).size(), total->size());
+
+ for (uint32_t policyIdx = 0; policyIdx < total->size(); ++policyIdx) {
+ std::vector<uint64_t> totalTimes = total->at(policyIdx);
+ uint32_t totalFreqsCount = totalTimes.size();
+ std::vector<uint64_t> allUidTimes(totalFreqsCount, 0);
+ for (auto const &[uid, uidTimes]: *allUid) {
+ for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) {
+ allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx];
+ }
+ }
+
+ for (uint32_t freqIdx = 0; freqIdx < totalFreqsCount; ++freqIdx) {
+ ASSERT_LE(allUidTimes[freqIdx], totalTimes[freqIdx]);
+ }
+ }
+}
+
TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
uint64_t zero = 0;
auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
@@ -290,6 +323,22 @@
ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf());
}
+TEST(TimeInStateTest, TotalTimeInStateMonotonic) {
+ auto before = getTotalCpuFreqTimes();
+ ASSERT_TRUE(before.has_value());
+ sleep(1);
+ auto after = getTotalCpuFreqTimes();
+ ASSERT_TRUE(after.has_value());
+
+ for (uint32_t policyIdx = 0; policyIdx < after->size(); ++policyIdx) {
+ auto timesBefore = before->at(policyIdx);
+ auto timesAfter = after->at(policyIdx);
+ for (uint32_t freqIdx = 0; freqIdx < timesAfter.size(); ++freqIdx) {
+ ASSERT_NO_FATAL_FAILURE(TestCheckDelta(timesBefore[freqIdx], timesAfter[freqIdx]));
+ }
+ }
+}
+
TEST(TimeInStateTest, AllUidTimeInStateMonotonic) {
auto map1 = getUidsCpuFreqTimes();
ASSERT_TRUE(map1.has_value());
@@ -387,6 +436,28 @@
}
}
+TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) {
+ uint32_t uid = 0;
+ {
+ // Find an unused UID
+ auto map = getUidsConcurrentTimes();
+ ASSERT_TRUE(map.has_value());
+ ASSERT_FALSE(map->empty());
+ for (const auto &kv : *map) uid = std::max(uid, kv.first);
+ ++uid;
+ }
+ android::base::unique_fd fd{
+ bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
+ ASSERT_GE(fd, 0);
+ uint32_t nCpus = get_nprocs_conf();
+ uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY;
+ time_key_t key = {.uid = uid, .bucket = maxBucket + 1};
+ std::vector<concurrent_val_t> vals(nCpus);
+ ASSERT_FALSE(writeToMapEntry(fd, &key, vals.data(), BPF_NOEXIST));
+ EXPECT_FALSE(getUidsConcurrentTimes().has_value());
+ ASSERT_FALSE(deleteMapEntry(fd, &key));
+}
+
TEST(TimeInStateTest, AllUidTimesConsistent) {
auto tisMap = getUidsCpuFreqTimes();
ASSERT_TRUE(tisMap.has_value());
@@ -482,5 +553,85 @@
for (size_t i = 0; i < freqs->size(); ++i) EXPECT_EQ((*freqs)[i].size(), (*times)[i].size());
}
+uint64_t timeNanos() {
+ struct timespec spec;
+ clock_gettime(CLOCK_MONOTONIC, &spec);
+ return spec.tv_sec * 1000000000 + spec.tv_nsec;
+}
+
+// Keeps CPU busy with some number crunching
+void useCpu() {
+ long sum = 0;
+ for (int i = 0; i < 100000; i++) {
+ sum *= i;
+ }
+}
+
+sem_t pingsem, pongsem;
+
+void *testThread(void *) {
+ for (int i = 0; i < 10; i++) {
+ sem_wait(&pingsem);
+ useCpu();
+ sem_post(&pongsem);
+ }
+ return nullptr;
+}
+
+TEST(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) {
+ uint64_t startTimeNs = timeNanos();
+
+ sem_init(&pingsem, 0, 1);
+ sem_init(&pongsem, 0, 0);
+
+ pthread_t thread;
+ ASSERT_EQ(pthread_create(&thread, NULL, &testThread, NULL), 0);
+
+ // This process may have been running for some time, so when we start tracking
+ // CPU time, the very first switch may include the accumulated time.
+ // Yield the remainder of this timeslice to the newly created thread.
+ sem_wait(&pongsem);
+ sem_post(&pingsem);
+
+ pid_t tgid = getpid();
+ startTrackingProcessCpuTimes(tgid);
+
+ pid_t tid = pthread_gettid_np(thread);
+ startAggregatingTaskCpuTimes(tid, 42);
+
+ // Play ping-pong with the other thread to ensure that both threads get
+ // some CPU time.
+ for (int i = 0; i < 9; i++) {
+ sem_wait(&pongsem);
+ useCpu();
+ sem_post(&pingsem);
+ }
+
+ pthread_join(thread, NULL);
+
+ std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> optionalMap =
+ getAggregatedTaskCpuFreqTimes(tgid, {0, 42});
+ ASSERT_TRUE(optionalMap);
+
+ std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map = *optionalMap;
+ ASSERT_EQ(map.size(), 2u);
+
+ uint64_t testDurationNs = timeNanos() - startTimeNs;
+ for (auto pair : map) {
+ uint16_t aggregationKey = pair.first;
+ ASSERT_TRUE(aggregationKey == 0 || aggregationKey == 42);
+
+ std::vector<std::vector<uint64_t>> timesInState = pair.second;
+ uint64_t totalCpuTime = 0;
+ for (size_t i = 0; i < timesInState.size(); i++) {
+ for (size_t j = 0; j < timesInState[i].size(); j++) {
+ totalCpuTime += timesInState[i][j];
+ }
+ }
+ ASSERT_GT(totalCpuTime, 0ul);
+ ASSERT_LE(totalCpuTime, testDurationNs);
+ }
+}
+
} // namespace bpf
} // namespace android
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 2e14408..3faf792 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -56,6 +56,7 @@
"android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.audio@5.0::IDevicesFactory",
"android.hardware.audio@6.0::IDevicesFactory",
+ "android.hardware.audio@7.0::IDevicesFactory",
"android.hardware.automotive.audiocontrol@1.0::IAudioControl",
"android.hardware.automotive.audiocontrol@2.0::IAudioControl",
"android.hardware.automotive.evs@1.0::IEvsCamera",
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 6909637..76518c1 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -9,6 +9,11 @@
"libbinder",
"libutils",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
cc_library {
diff --git a/libs/gralloc/OWNERS b/libs/gralloc/OWNERS
index 4a95778..93879d8 100644
--- a/libs/gralloc/OWNERS
+++ b/libs/gralloc/OWNERS
@@ -1,2 +1 @@
chrisforbes@google.com
-vhau@google.com
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 66fb295..243d7f1 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -21,6 +21,11 @@
"-Wno-enum-compare",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
vendor_available: true,
vndk: {
@@ -38,14 +43,14 @@
],
shared_libs: [
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
"liblog",
],
export_shared_lib_headers: [
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
],
diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp
index 8444883..8933dc3 100644
--- a/libs/gralloc/types/fuzzer/Android.bp
+++ b/libs/gralloc/types/fuzzer/Android.bp
@@ -2,6 +2,11 @@
name: "libgralloctypes_fuzzer",
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
fuzz_config: {
cc: ["marissaw@google.com"],
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 1a7c2c9..2f418ac 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -32,202 +32,6 @@
#include <android/hardware/graphics/mapper/4.0/IMapper.h>
namespace android {
-
-/**
- * Define equality operators for Stable AIDL types.
- */
-inline bool operator==(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
- const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
- return !std::strcmp(lhs.name.c_str(), rhs.name.c_str()) && lhs.value == rhs.value;
-}
-
-inline bool operator!=(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
- const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
- return !(lhs == rhs);
-}
-
-inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
- const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
- if (lhs.type.name != rhs.type.name) {
- return false;
- }
- if (lhs.type.value != rhs.type.value) {
- return false;
- }
- if (lhs.sizeInBits != rhs.sizeInBits) {
- return false;
- }
- if (lhs.offsetInBits != rhs.offsetInBits) {
- return false;
- }
- return true;
-}
-
-inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
- const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
- return !(lhs == rhs);
-}
-
-inline bool operator==(const aidl::android::hardware::graphics::common::Rect& lhs,
- const aidl::android::hardware::graphics::common::Rect& rhs) {
- if (lhs.left != rhs.left) {
- return false;
- }
- if (lhs.top != rhs.top) {
- return false;
- }
- if (lhs.right != rhs.right) {
- return false;
- }
- if (lhs.bottom != rhs.bottom) {
- return false;
- }
- return true;
-}
-
-inline bool operator!=(const aidl::android::hardware::graphics::common::Rect& lhs,
- const aidl::android::hardware::graphics::common::Rect& rhs) {
- return !(lhs == rhs);
-}
-
-inline bool operator==(const std::vector<aidl::android::hardware::graphics::common::Rect>& lhs,
- const std::vector<aidl::android::hardware::graphics::common::Rect>& rhs) {
- if (lhs.size() != rhs.size()) {
- return false;
- }
- for (size_t i = 0; i < lhs.size(); i++) {
- if (lhs[i] != rhs[i]) {
- return false;
- }
- }
- return true;
-}
-
-inline bool operator!=(const std::vector<aidl::android::hardware::graphics::common::Rect>& lhs,
- const std::vector<aidl::android::hardware::graphics::common::Rect>& rhs) {
- return !(lhs == rhs);
-}
-
-inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
- const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
- if (lhs.offsetInBytes != rhs.offsetInBytes) {
- return false;
- }
- if (lhs.sampleIncrementInBits != rhs.sampleIncrementInBits) {
- return false;
- }
- if (lhs.strideInBytes != rhs.strideInBytes) {
- return false;
- }
- if (lhs.widthInSamples != rhs.widthInSamples) {
- return false;
- }
- if (lhs.heightInSamples != rhs.heightInSamples) {
- return false;
- }
- if (lhs.totalSizeInBytes != rhs.totalSizeInBytes) {
- return false;
- }
- if (lhs.horizontalSubsampling != rhs.horizontalSubsampling) {
- return false;
- }
- if (lhs.verticalSubsampling != rhs.verticalSubsampling) {
- return false;
- }
- if (lhs.components.size() != rhs.components.size()) {
- return false;
- }
- for (size_t i = 0; i < lhs.components.size(); i++) {
- if (lhs.components[i] != rhs.components[i]) {
- return false;
- }
- }
- return true;
-}
-
-inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
- const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
- return !(lhs == rhs);
-}
-
-inline bool operator==(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
- const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
- if (lhs.size() != rhs.size()) {
- return false;
- }
- for (size_t i = 0; i < lhs.size(); i++) {
- if (lhs[i] != rhs[i]) {
- return false;
- }
- }
- return true;
-}
-
-inline bool operator!=(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
- const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
- return !(lhs == rhs);
-}
-
-inline bool operator==(const aidl::android::hardware::graphics::common::XyColor& lhs,
- const aidl::android::hardware::graphics::common::XyColor& rhs) {
- if (lhs.x != rhs.x) {
- return false;
- }
- if (lhs.y != rhs.y) {
- return false;
- }
- return true;
-}
-
-inline bool operator!=(const aidl::android::hardware::graphics::common::XyColor& lhs,
- const aidl::android::hardware::graphics::common::XyColor& rhs) {
- return !(lhs == rhs);
-}
-
-inline bool operator==(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
- const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
- if (lhs.primaryRed != rhs.primaryRed) {
- return false;
- }
- if (lhs.primaryGreen != rhs.primaryGreen) {
- return false;
- }
- if (lhs.primaryBlue != rhs.primaryBlue) {
- return false;
- }
- if (lhs.whitePoint != rhs.whitePoint) {
- return false;
- }
- if (lhs.maxLuminance != rhs.maxLuminance) {
- return false;
- }
- if (lhs.minLuminance != rhs.minLuminance) {
- return false;
- }
- return true;
-}
-
-inline bool operator!=(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
- const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
- return !(lhs == rhs);
-}
-
-inline bool operator==(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
- const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
- if (lhs.maxContentLightLevel != rhs.maxContentLightLevel) {
- return false;
- }
- if (lhs.maxFrameAverageLightLevel != rhs.maxFrameAverageLightLevel) {
- return false;
- }
- return true;
-}
-
-inline bool operator!=(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
- const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
- return !(lhs == rhs);
-}
-
namespace gralloc4 {
#define GRALLOC4_STANDARD_METADATA_TYPE "android.hardware.graphics.common.StandardMetadataType"
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 4a4510e..18ade37 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -32,9 +32,10 @@
cc_library_shared {
name: "libgui",
- vendor_available: false,
+ vendor_available: true,
vndk: {
enabled: true,
+ private: true,
},
double_loadable: true,
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index ecccf29..1667fb0 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -7,9 +7,7 @@
lpy@google.com
mathias@google.com
racarr@google.com
-steventhomas@google.com
stoza@google.com
-vhau@google.com
vishnun@google.com
per-file EndToEndNativeInputTest.cpp = svv@google.com
@@ -22,4 +20,4 @@
per-file include/gui/BufferQueue* = set noparent
per-file include/gui/BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com
per-file include/gui/IGraphicBuffer* = set noparent
-per-file include/gui/IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com
\ No newline at end of file
+per-file include/gui/IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index c3323fe..d6f9e63 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -732,6 +732,8 @@
mSharedBufferHasBeenQueued = false;
}
+ mDequeuedSlots.insert(buf);
+
return OK;
}
@@ -760,6 +762,8 @@
mSharedBufferHasBeenQueued = true;
}
+ mDequeuedSlots.erase(i);
+
return OK;
}
@@ -895,6 +899,8 @@
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
+ mDequeuedSlots.erase(i);
+
if (mEnableFrameTimestamps) {
mFrameEventHistory->applyDelta(output.frameTimestamps);
// Update timestamps with the local acquire fence.
@@ -1660,6 +1666,7 @@
mRemovedBuffers.push_back(mSlots[attachedSlot].buffer);
}
mSlots[attachedSlot].buffer = graphicBuffer;
+ mDequeuedSlots.insert(attachedSlot);
return NO_ERROR;
}
@@ -1926,6 +1933,10 @@
}
void Surface::freeAllBuffers() {
+ if (!mDequeuedSlots.empty()) {
+ ALOGE("%s: %zu buffers were freed while being dequeued!",
+ __FUNCTION__, mDequeuedSlots.size());
+ }
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].buffer = nullptr;
}
@@ -1947,6 +1958,10 @@
ALOGW("%s: Discarded slot %d doesn't contain buffer!", __FUNCTION__, i);
continue;
}
+ // Don't flush currently dequeued buffers
+ if (mDequeuedSlots.count(i) > 0) {
+ continue;
+ }
outBuffers->push_back(mSlots[i].buffer);
mSlots[i].buffer = nullptr;
}
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 49c83da..55b4101 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -30,6 +30,7 @@
#include <utils/RefBase.h>
#include <shared_mutex>
+#include <unordered_set>
namespace android {
@@ -543,8 +544,15 @@
int mMaxBufferCount;
sp<IProducerListener> mListenerProxy;
+
+ // Get and flush the buffers of given slots, if the buffer in the slot
+ // is currently dequeued then it won't be flushed and won't be returned
+ // in outBuffers.
status_t getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots,
std::vector<sp<GraphicBuffer>>* outBuffers);
+
+ // Buffers that are successfully dequeued/attached and handed to clients
+ std::unordered_set<int> mDequeuedSlots;
};
} // namespace android
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index d64dfd5..d98ffc6 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -72,13 +72,9 @@
}
String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
- size_t len;
- const char16_t* str = parcel->readString16Inplace(&len);
- if (str != nullptr) {
- return String16(str, len);
- } else {
- return String16();
- }
+ std::optional<String16> str;
+ parcel->readString16(&str);
+ return str.value_or(String16());
}
} // namespace view
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 52d73e0..07e5d86 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -40,6 +40,7 @@
cc_library {
name: "libnativewindow",
+ llndk_stubs: "libnativewindow.llndk",
export_include_dirs: [
"include",
"include-private",
@@ -95,7 +96,7 @@
}
llndk_library {
- name: "libnativewindow",
+ name: "libnativewindow.llndk",
symbol_file: "libnativewindow.map.txt",
unversioned: true,
export_include_dirs: ["include"],
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp
index b9cea10..245c9e1 100644
--- a/libs/renderengine/Description.cpp
+++ b/libs/renderengine/Description.cpp
@@ -52,5 +52,10 @@
return colorMatrix != identity;
}
+bool Description::hasDisplayColorMatrix() const {
+ const mat4 identity;
+ return displayColorMatrix != identity;
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 2139acb..56d470e 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -409,6 +409,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 +440,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 +607,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 +667,7 @@
}
bindExternalTextureImage(texName, *cachedImage->second);
+ mTextureView.insert_or_assign(texName, buffer->getId());
}
// Wait for the new buffer to be ready.
@@ -887,7 +909,7 @@
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
-bool GLESRenderEngine::cleanupPostRender() {
+bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) {
ATRACE_CALL();
if (mPriorResourcesCleaned ||
@@ -896,6 +918,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);
@@ -1048,6 +1094,7 @@
setOutputDataSpace(display.outputDataspace);
setDisplayMaxLuminance(display.maxLuminance);
+ setDisplayColorTransform(display.colorTransform);
const mat4 projectionMatrix =
ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
@@ -1114,7 +1161,7 @@
position[3] = vec2(bounds.right, bounds.top);
setupLayerCropping(*layer, mesh);
- setColorTransform(display.colorTransform * layer->colorTransform);
+ setColorTransform(layer->colorTransform);
bool usePremultipliedAlpha = true;
bool disableTexture = true;
@@ -1271,6 +1318,10 @@
mState.colorMatrix = colorTransform;
}
+void GLESRenderEngine::setDisplayColorTransform(const mat4& colorTransform) {
+ mState.displayColorMatrix = colorTransform;
+}
+
void GLESRenderEngine::disableTexturing() {
mState.textureEnabled = false;
}
@@ -1617,6 +1668,16 @@
return cachedImage != mImageCache.end();
}
+bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) {
+ const auto& entry = mTextureView.find(texName);
+ return entry != mTextureView.end();
+}
+
+std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) {
+ const auto& entry = mTextureView.find(texName);
+ return entry != mTextureView.end() ? entry->second : std::nullopt;
+}
+
bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 61986ff..d554041 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -75,7 +75,7 @@
const std::vector<const LayerSettings*>& layers,
ANativeWindowBuffer* 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);
@@ -159,6 +165,7 @@
void setupLayerTexturing(const Texture& texture);
void setupFillWithColor(float r, float g, float b, float a);
void setColorTransform(const mat4& colorTransform);
+ void setDisplayColorTransform(const mat4& colorTransform);
void disableTexturing();
void disableBlending();
void setupCornerRadiusCropSize(float width, float height);
@@ -224,6 +231,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 +246,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/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index f4fbf35..a172c56 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -66,6 +66,7 @@
mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
mSamplerLoc = glGetUniformLocation(programId, "sampler");
mColorLoc = glGetUniformLocation(programId, "color");
+ mDisplayColorMatrixLoc = glGetUniformLocation(programId, "displayColorMatrix");
mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
@@ -129,6 +130,9 @@
const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
glUniform4fv(mColorLoc, 1, color);
}
+ if (mDisplayColorMatrixLoc >= 0) {
+ glUniformMatrix4fv(mDisplayColorMatrixLoc, 1, GL_FALSE, desc.displayColorMatrix.asArray());
+ }
if (mInputTransformMatrixLoc >= 0) {
mat4 inputTransformMatrix = desc.inputTransformMatrix;
glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index fc3755e..4292645 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -104,6 +104,7 @@
/* location of transform matrix */
GLint mInputTransformMatrixLoc;
GLint mOutputTransformMatrixLoc;
+ GLint mDisplayColorMatrixLoc;
/* location of corner radius uniform */
GLint mCornerRadiusLoc;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 3ae35ec..611755e 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -180,6 +180,10 @@
description.hasOutputTransformMatrix() || description.hasColorMatrix()
? Key::OUTPUT_TRANSFORM_MATRIX_ON
: Key::OUTPUT_TRANSFORM_MATRIX_OFF)
+ .set(Key::Key::DISPLAY_COLOR_TRANSFORM_MATRIX_MASK,
+ description.hasDisplayColorMatrix()
+ ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON
+ : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF)
.set(Key::ROUNDED_CORNERS_MASK,
description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
.set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
@@ -661,7 +665,9 @@
)__SHADER__";
}
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ if (needs.hasTransformMatrix() ||
+ (needs.getInputTF() != needs.getOutputTF()) ||
+ needs.hasDisplayColorMatrix()) {
if (needs.needsToneMapping()) {
fs << "uniform float displayMaxLuminance;";
fs << "uniform float maxMasteringLuminance;";
@@ -700,6 +706,21 @@
)__SHADER__";
}
+ if (needs.hasDisplayColorMatrix()) {
+ fs << "uniform mat4 displayColorMatrix;";
+ fs << R"__SHADER__(
+ highp vec3 DisplayColorMatrix(const highp vec3 color) {
+ return clamp(vec3(displayColorMatrix * vec4(color, 1.0)), 0.0, 1.0);
+ }
+ )__SHADER__";
+ } else {
+ fs << R"__SHADER__(
+ highp vec3 DisplayColorMatrix(const highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ }
+
generateEOTF(fs, needs);
generateOOTF(fs, needs);
generateOETF(fs, needs);
@@ -721,6 +742,31 @@
if (needs.isOpaque()) {
fs << "gl_FragColor.a = 1.0;";
}
+ }
+
+ if (needs.hasTransformMatrix() ||
+ (needs.getInputTF() != needs.getOutputTF()) ||
+ needs.hasDisplayColorMatrix()) {
+ if (!needs.isOpaque() && needs.isPremultiplied()) {
+ // un-premultiply if needed before linearization
+ // avoid divide by 0 by adding 0.5/256 to the alpha channel
+ fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
+ }
+ fs << "gl_FragColor.rgb = "
+ "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb))))));";
+
+ if (!needs.isOpaque() && needs.isPremultiplied()) {
+ // and re-premultiply if needed after gamma correction
+ fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
+ }
+ }
+
+ /*
+ * Whether applying layer alpha before or after color transform doesn't matter,
+ * as long as we can undo premultiplication. But we cannot un-premultiply
+ * for color transform if the layer alpha = 0, e.g. 0 / (0 + 0.0019) = 0.
+ */
+ if (!needs.drawShadows()) {
if (needs.hasAlpha()) {
// modulate the current alpha value with alpha set
if (needs.isPremultiplied()) {
@@ -732,20 +778,6 @@
}
}
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
- if (!needs.isOpaque() && needs.isPremultiplied()) {
- // un-premultiply if needed before linearization
- // avoid divide by 0 by adding 0.5/256 to the alpha channel
- fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
- }
- fs << "gl_FragColor.rgb = "
- "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
- if (!needs.isOpaque() && needs.isPremultiplied()) {
- // and re-premultiply if needed after gamma correction
- fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
- }
- }
-
if (needs.hasRoundedCorners()) {
if (needs.isPremultiplied()) {
fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));";
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 901e631..b492cb3 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -117,6 +117,11 @@
SHADOW_MASK = 1 << SHADOW_SHIFT,
SHADOW_OFF = 0 << SHADOW_SHIFT,
SHADOW_ON = 1 << SHADOW_SHIFT,
+
+ DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT = 14,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_MASK = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_OFF = 0 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_ON = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
};
inline Key() : mKey(0) {}
@@ -143,6 +148,9 @@
inline bool hasOutputTransformMatrix() const {
return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
}
+ inline bool hasDisplayColorMatrix() const {
+ return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) == DISPLAY_COLOR_TRANSFORM_MATRIX_ON;
+ }
inline bool hasTransformMatrix() const {
return hasInputTransformMatrix() || hasOutputTransformMatrix();
}
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index e06e128..74bc44b 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -111,14 +111,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 df0f17a..101cbb7 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*>&,
ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*));
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index a62161a..fa6ec10 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -44,6 +44,7 @@
bool hasInputTransformMatrix() const;
bool hasOutputTransformMatrix() const;
bool hasColorMatrix() const;
+ bool hasDisplayColorMatrix() const;
// whether textures are premultiplied
bool isPremultipliedAlpha = false;
@@ -79,6 +80,8 @@
// The color matrix will be applied in linear space right before OETF.
mat4 colorMatrix;
+ // The display color matrix will be applied in gamma space after OETF
+ mat4 displayColorMatrix;
mat4 inputTransformMatrix;
mat4 outputTransformMatrix;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 0b5b1e4..1ec9412 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -38,22 +38,27 @@
struct RenderEngineTest : public ::testing::Test {
static void SetUpTestSuite() {
- sRE = renderengine::gl::GLESRenderEngine::create(
- renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
- .setImageCacheSize(1)
- .setUseColorManagerment(false)
- .setEnableProtectedContext(false)
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(true)
- .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .build());
+ renderengine::RenderEngineCreationArgs reCreationArgs =
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setUseColorManagerment(false)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .build();
+ sRE = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+
+ reCreationArgs.useColorManagement = true;
+ sRECM = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
}
static void TearDownTestSuite() {
// The ordering here is important - sCurrentBuffer must live longer
// than RenderEngine to avoid a null reference on tear-down.
sRE = nullptr;
+ sRECM = nullptr;
sCurrentBuffer = nullptr;
}
@@ -81,6 +86,10 @@
}
for (uint32_t texName : mTexNames) {
sRE->deleteTextures(1, &texName);
+ EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
+ }
+ for (uint32_t texName : mTexNamesCM) {
+ sRECM->deleteTextures(1, &texName);
}
}
@@ -250,9 +259,13 @@
void invokeDraw(renderengine::DisplaySettings settings,
std::vector<const renderengine::LayerSettings*> layers,
- sp<GraphicBuffer> buffer) {
+ sp<GraphicBuffer> buffer,
+ bool useColorManagement = false) {
base::unique_fd fence;
- status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
+ status_t status = useColorManagement ?
+ sRECM ->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
+ base::unique_fd(), &fence) :
+ sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
base::unique_fd(), &fence);
sCurrentBuffer = buffer;
@@ -264,7 +277,11 @@
ASSERT_EQ(NO_ERROR, status);
if (layers.size() > 0) {
- ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ if (useColorManagement) {
+ ASSERT_TRUE(sRECM->isFramebufferImageCachedForTesting(buffer->getId()));
+ } else {
+ ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ }
}
}
@@ -319,12 +336,21 @@
void fillBufferLayerTransform();
template <typename SourceVariant>
- void fillBufferWithColorTransform();
+ void fillBufferWithColorTransform(bool useColorManagement = false);
template <typename SourceVariant>
void fillBufferColorTransform();
template <typename SourceVariant>
+ void fillBufferColorTransformCM();
+
+ template <typename SourceVariant>
+ void fillBufferWithColorTransformZeroLayerAlpha();
+
+ template <typename SourceVariant>
+ void fillBufferColorTransformZeroLayerAlpha();
+
+ template <typename SourceVariant>
void fillRedBufferWithRoundedCorners();
template <typename SourceVariant>
@@ -363,6 +389,8 @@
// For now, exercise the GL backend directly so that some caching specifics
// can be tested without changing the interface.
static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
+ // renderengine object with Color Management enabled
+ static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRECM;
// Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
// be freed *after* RenderEngine is destroyed, so that the EGL image is
// destroyed first.
@@ -371,14 +399,17 @@
sp<GraphicBuffer> mBuffer;
std::vector<uint32_t> mTexNames;
+ std::vector<uint32_t> mTexNamesCM;
};
std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
+std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRECM = nullptr;
+
sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
struct ColorSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
- RenderEngineTest* /*fixture*/) {
+ RenderEngineTest* /*fixture*/, bool /*useColorManagement*/ = false) {
layer.source.solidColor = half3(r, g, b);
}
};
@@ -406,11 +437,17 @@
template <typename OpaquenessVariant>
struct BufferSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
- RenderEngineTest* fixture) {
+ RenderEngineTest* fixture,
+ bool useColorManagement = false) {
sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
uint32_t texName;
- fixture->sRE->genTextures(1, &texName);
- fixture->mTexNames.push_back(texName);
+ if (useColorManagement) {
+ fixture->sRECM->genTextures(1, &texName);
+ fixture->mTexNamesCM.push_back(texName);
+ } else {
+ fixture->sRE->genTextures(1, &texName);
+ fixture->mTexNames.push_back(texName);
+ }
uint8_t* pixels;
buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -640,7 +677,46 @@
}
template <typename SourceVariant>
-void RenderEngineTest::fillBufferWithColorTransform() {
+void RenderEngineTest::fillBufferWithColorTransform(bool useColorManagement) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this, useColorManagement);
+ layer.alpha = 1.0f;
+
+ // construct a fake color matrix
+ // annihilate green and blue channels
+ settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
+ // set red channel to red + green
+ layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(&layer);
+
+ invokeDraw(settings, layers, mBuffer, useColorManagement);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransform() {
+ fillBufferWithColorTransform<SourceVariant>();
+ expectBufferColor(fullscreenRect(), 172, 0, 0, 255, 1);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformCM() {
+ fillBufferWithColorTransform<SourceVariant>(true);
+ expectBufferColor(fullscreenRect(), 126, 0, 0, 255, 1);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = Rect(1, 1);
@@ -650,15 +726,15 @@
renderengine::LayerSettings layer;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
- layer.alpha = 1.0f;
+ layer.alpha = 0;
// construct a fake color matrix
- // annihilate green and blue channels
- settings.colorTransform = mat4::scale(vec4(1, 0, 0, 1));
- // set red channel to red + green
- layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ // simple inverse color
+ settings.colorTransform = mat4(-1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, -1, 0,
+ 1, 1, 1, 1);
- layer.alpha = 1.0f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
layers.push_back(&layer);
@@ -667,9 +743,9 @@
}
template <typename SourceVariant>
-void RenderEngineTest::fillBufferColorTransform() {
- fillBufferWithColorTransform<SourceVariant>();
- expectBufferColor(fullscreenRect(), 191, 0, 0, 255);
+void RenderEngineTest::fillBufferColorTransformZeroLayerAlpha() {
+ fillBufferWithColorTransformZeroLayerAlpha<SourceVariant>();
+ expectBufferColor(fullscreenRect(), 0, 0, 0, 0);
}
template <typename SourceVariant>
@@ -1072,7 +1148,15 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
- fillBufferLayerTransform<ColorSourceVariant>();
+ fillBufferColorTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_colorSource) {
+ fillBufferColorTransformCM<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) {
+ fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
@@ -1128,7 +1212,15 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
- fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+ fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_opaqueBufferSource) {
+ fillBufferColorTransformCM<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) {
+ fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
@@ -1184,7 +1276,15 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
- fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+ fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_bufferSource) {
+ fillBufferColorTransformCM<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) {
+ fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
@@ -1424,10 +1524,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->getNativeBuffer(), 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/sensorprivacy/OWNERS b/libs/sensorprivacy/OWNERS
new file mode 100644
index 0000000..be955f5
--- /dev/null
+++ b/libs/sensorprivacy/OWNERS
@@ -0,0 +1,3 @@
+cbrubaker@google.com
+evanseverson@google.com
+mpgroover@google.com
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 1ee8c71..47eb59f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -72,7 +72,7 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.allocator@4.0",
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.1",
@@ -89,7 +89,7 @@
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libgralloctypes",
],
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index e95a080..33ab8ba 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-cc_library_shared {
+cc_library {
name: "libvibrator",
shared_libs: [
@@ -45,4 +45,11 @@
],
export_include_dirs: ["include"],
+
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/libs/vibrator/OWNERS b/libs/vibrator/OWNERS
new file mode 100644
index 0000000..0997e9f
--- /dev/null
+++ b/libs/vibrator/OWNERS
@@ -0,0 +1,2 @@
+lsandrade@google.com
+michaelwr@google.com
diff --git a/libs/vibrator/fuzzer/Android.bp b/libs/vibrator/fuzzer/Android.bp
new file mode 100644
index 0000000..8020151
--- /dev/null
+++ b/libs/vibrator/fuzzer/Android.bp
@@ -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.
+ *
+ *****************************************************************************
+ */
+
+cc_fuzz {
+ name: "vibrator_fuzzer",
+
+ host_supported: true,
+
+ srcs: [
+ "vibrator_fuzzer.cpp",
+ ],
+
+ static_libs: [
+ "liblog",
+ "libvibrator",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libbase",
+ "libutils",
+ ],
+
+ fuzz_config: {
+ componentid: 155276,
+ },
+}
diff --git a/libs/vibrator/fuzzer/README.md b/libs/vibrator/fuzzer/README.md
new file mode 100644
index 0000000..43eb2d2
--- /dev/null
+++ b/libs/vibrator/fuzzer/README.md
@@ -0,0 +1,65 @@
+# Fuzzer for libvibrator
+
+## Plugin Design Considerations
+This fuzzer fuzzes native code present in libvibrator and does not cover the Java implementation ExternalVibration
+The fuzzer plugin is designed based on the understanding of the
+library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libvibrator supports the following parameters:
+1. Uid (parameter name: `uid`)
+2. Package Name (parameter name: `pkg`)
+3. Audio Content Type (parameter name: `content_type`)
+4. Audio Usage (parameter name: `usage`)
+5. Audio Source (parameter name: `source`)
+6. Audio flags (parameter name: `flags`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `uid` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `pkg` | Any std::string value | Value obtained from FuzzedDataProvider |
+| `content_type` | 0.`AUDIO_CONTENT_TYPE_UNKNOWN` 1.`AUDIO_CONTENT_TYPE_SPEECH` 2.`AUDIO_CONTENT_TYPE_MUSIC` 3.`AUDIO_CONTENT_TYPE_MOVIE` 4.`AUDIO_CONTENT_TYPE_SONIFICATION`| Value obtained from FuzzedDataProvider in the range 0 to 4|
+| `usage` | 0.`AUDIO_USAGE_UNKNOWN` 1.`AUDIO_USAGE_MEDIA` 2.`AUDIO_USAGE_VOICE_COMMUNICATION` 3.`AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING` 4.`AUDIO_USAGE_ALARM` 5.`AUDIO_USAGE_NOTIFICATION` 6.`AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE` 7.`AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST` 8.`AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT` 9.`AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED` 10.`AUDIO_USAGE_NOTIFICATION_EVENT` 11.`AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY` 12.`AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE` 13.`AUDIO_USAGE_ASSISTANCE_SONIFICATION` 14.`AUDIO_USAGE_GAME` 15.`AUDIO_USAGE_VIRTUAL_SOURCE` 16.`AUDIO_USAGE_ASSISTANT` 17.`AUDIO_USAGE_CALL_ASSISTANT` 18.`AUDIO_USAGE_EMERGENCY` 19.`AUDIO_USAGE_SAFETY` 20.`AUDIO_USAGE_VEHICLE_STATUS` 21.`AUDIO_USAGE_ANNOUNCEMENT`| Value obtained from FuzzedDataProvider in the range 0 to 21|
+| `source` | 0.`AUDIO_SOURCE_DEFAULT` 1.`AUDIO_SOURCE_MIC` 2.`AUDIO_SOURCE_VOICE_UPLINK` 3.`AUDIO_SOURCE_VOICE_DOWNLINK` 4.`AUDIO_SOURCE_VOICE_CALL` 5.`AUDIO_SOURCE_CAMCORDER` 6.`AUDIO_SOURCE_VOICE_RECOGNITION` 7.`AUDIO_SOURCE_VOICE_COMMUNICATION` 8.`AUDIO_SOURCE_REMOTE_SUBMIX` 9.`AUDIO_SOURCE_UNPROCESSED` 10.`AUDIO_SOURCE_VOICE_PERFORMANCE` 11.`AUDIO_SOURCE_ECHO_REFERENCE` 12.`AUDIO_SOURCE_FM_TUNER` | Value obtained from FuzzedDataProvider in the range 0 to 12 |
+| `flags` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesn't `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build vibrator_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) vibrator_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some files to that folder
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/vibrator_fuzzer/vibrator_fuzzer CORPUS_DIR
+```
+
+To run on host
+```
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/vibrator_fuzzer/vibrator_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/libs/vibrator/fuzzer/vibrator_fuzzer.cpp b/libs/vibrator/fuzzer/vibrator_fuzzer.cpp
new file mode 100644
index 0000000..68b3ca6
--- /dev/null
+++ b/libs/vibrator/fuzzer/vibrator_fuzzer.cpp
@@ -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.
+ *
+ *****************************************************************************
+ */
+
+#include <binder/Parcel.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <vibrator/ExternalVibration.h>
+
+using namespace android;
+
+constexpr size_t MAX_STRING_LENGTH = 100;
+constexpr audio_content_type_t AUDIO_CONTENT_TYPE[] = {AUDIO_CONTENT_TYPE_UNKNOWN,
+ AUDIO_CONTENT_TYPE_SPEECH,
+ AUDIO_CONTENT_TYPE_MUSIC,
+ AUDIO_CONTENT_TYPE_MOVIE,
+ AUDIO_CONTENT_TYPE_SONIFICATION};
+constexpr audio_usage_t AUDIO_USAGE[] = {
+ AUDIO_USAGE_UNKNOWN,
+ AUDIO_USAGE_MEDIA,
+ AUDIO_USAGE_VOICE_COMMUNICATION,
+ AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING,
+ AUDIO_USAGE_ALARM,
+ AUDIO_USAGE_NOTIFICATION,
+ AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
+ AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
+ AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
+ AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
+ AUDIO_USAGE_NOTIFICATION_EVENT,
+ AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
+ AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ AUDIO_USAGE_ASSISTANCE_SONIFICATION,
+ AUDIO_USAGE_GAME,
+ AUDIO_USAGE_VIRTUAL_SOURCE,
+ AUDIO_USAGE_ASSISTANT,
+ AUDIO_USAGE_CALL_ASSISTANT,
+ AUDIO_USAGE_EMERGENCY,
+ AUDIO_USAGE_SAFETY,
+ AUDIO_USAGE_VEHICLE_STATUS,
+ AUDIO_USAGE_ANNOUNCEMENT,
+};
+constexpr audio_source_t AUDIO_SOURCE[] = {
+ AUDIO_SOURCE_DEFAULT, AUDIO_SOURCE_MIC,
+ AUDIO_SOURCE_VOICE_UPLINK, AUDIO_SOURCE_VOICE_DOWNLINK,
+ AUDIO_SOURCE_VOICE_CALL, AUDIO_SOURCE_CAMCORDER,
+ AUDIO_SOURCE_VOICE_RECOGNITION, AUDIO_SOURCE_VOICE_COMMUNICATION,
+ AUDIO_SOURCE_REMOTE_SUBMIX, AUDIO_SOURCE_UNPROCESSED,
+ AUDIO_SOURCE_VOICE_PERFORMANCE, AUDIO_SOURCE_ECHO_REFERENCE,
+ AUDIO_SOURCE_FM_TUNER,
+};
+constexpr size_t NUM_AUDIO_CONTENT_TYPE = std::size(AUDIO_CONTENT_TYPE);
+constexpr size_t NUM_AUDIO_USAGE = std::size(AUDIO_USAGE);
+constexpr size_t NUM_AUDIO_SOURCE = std::size(AUDIO_SOURCE);
+
+class TestVibrationController : public os::IExternalVibrationController {
+public:
+ explicit TestVibrationController() {}
+ IBinder *onAsBinder() override { return nullptr; }
+ binder::Status mute(/*out*/ bool *ret) override {
+ *ret = false;
+ return binder::Status::ok();
+ };
+ binder::Status unmute(/*out*/ bool *ret) override {
+ *ret = false;
+ return binder::Status::ok();
+ };
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 1) {
+ return 0;
+ }
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ // Initialize the parameters using FuzzedDataProvider
+ int32_t uid = fdp.ConsumeIntegral<int32_t>();
+ std::string pkg = fdp.ConsumeRandomLengthString(MAX_STRING_LENGTH);
+ audio_attributes_t attributes;
+ attributes.content_type =
+ AUDIO_CONTENT_TYPE[fdp.ConsumeIntegralInRange<uint32_t>(0, NUM_AUDIO_CONTENT_TYPE - 1)];
+ attributes.usage = AUDIO_USAGE[fdp.ConsumeIntegralInRange<uint32_t>(0, NUM_AUDIO_USAGE - 1)];
+ attributes.source = AUDIO_SOURCE[fdp.ConsumeIntegralInRange<uint32_t>(0, NUM_AUDIO_SOURCE - 1)];
+ attributes.flags = static_cast<audio_flags_mask_t>(fdp.ConsumeIntegral<uint32_t>());
+
+ // Create an instance of TestVibrationController
+ sp<TestVibrationController> vibrationController = new TestVibrationController();
+ if (!vibrationController) {
+ return 0;
+ }
+
+ // Set all the parameters in the constructor call
+ sp<os::ExternalVibration> extVibration =
+ new os::ExternalVibration(uid, pkg, attributes, vibrationController);
+ if (!extVibration) {
+ return 0;
+ }
+
+ // Get all the parameters that were previously set
+ extVibration->getUid();
+ extVibration->getPackage();
+ extVibration->getAudioAttributes();
+ extVibration->getController();
+
+ // Set the parameters in a Parcel object and send it to libvibrator
+ // This parcel shall be read by libvibrator
+ Parcel parcel;
+ parcel.writeInt32(uid);
+ parcel.writeString16(String16(pkg.c_str()));
+ parcel.writeStrongBinder(IInterface::asBinder(vibrationController));
+ parcel.setDataPosition(0);
+ extVibration->readFromParcel(&parcel);
+
+ // Send a Parcel to libvibrator
+ // Parameters shall be written to this parcel by libvibrator
+ extVibration->writeToParcel(&parcel);
+ return 0;
+}
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 340d7bf..d5a19d3 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -80,6 +80,7 @@
cc_library_shared {
name: "libdvr.google",
+ system_ext_specific: true,
owner: "google",
cflags: cflags,
header_libs: ["libdvr_headers"],
diff --git a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
index afde5f7..f5c5a5a 100644
--- a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
+++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
@@ -52,7 +52,7 @@
// Fuzzer for Serialization operations, this is mostly just lifted from the
// existing test cases to use fuzzed values as inputs.
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+void FuzzSerializeDeserialize(const uint8_t* data, size_t size) {
FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
Payload result;
@@ -106,6 +106,183 @@
Deserialize(&vec_val, &result);
Serialize(t1_val, &result);
Deserialize(&t1_val, &result);
+}
+
+void FuzzDeserializeUint8(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT8, fdp.ConsumeIntegral<uint8_t>()};
+ std::uint8_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint16(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT16, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint16_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint32_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {
+ ENCODING_TYPE_UINT64, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint64_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt8(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT8, fdp.ConsumeIntegral<uint8_t>()};
+ std::int8_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt16(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT16, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int16_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int32_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT64,
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int64_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFloat32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_FLOAT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ float floatResult;
+ Deserialize(&floatResult, &buffer);
+
+ buffer.Rewind();
+ double doubleResult;
+ Deserialize(&doubleResult, &buffer);
+}
+
+void FuzzDeserializeFloat64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {
+ ENCODING_TYPE_FLOAT64, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ double result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixstr(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ std::string s_val = fdp.ConsumeRemainingBytesAsString();
+ Payload buffer = {ENCODING_TYPE_FIXSTR_MAX};
+ for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+ iter++) {
+ buffer.Append(1, *iter);
+ }
+ std::string result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixmap(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_FIXMAP_MAX};
+ // Fill the map with the fuzzed data, not attempting to
+ // make a valid map
+ while (fdp.remaining_bytes() > 0) {
+ buffer.Append(1, fdp.ConsumeIntegral<uint8_t>());
+ }
+
+ std::map<std::uint32_t, std::uint32_t> result;
+ Deserialize(&result, &buffer);
+
+ buffer.Rewind();
+ std::unordered_map<std::uint32_t, std::uint32_t> unorderedResult;
+ Deserialize(&unorderedResult, &buffer);
+}
+
+void FuzzDeserializeVariant(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT16,
+ ENCODING_TYPE_FLOAT32,
+ ENCODING_TYPE_FIXSTR_MAX,
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ // Add the rest of the data as a string
+ std::string s_val = fdp.ConsumeRemainingBytesAsString();
+ for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+ iter++) {
+ buffer.Append(1, *iter);
+ }
+ Variant<int, float, std::string> result;
+ Deserialize(&result, &buffer);
+}
+
+// Attempts to deserialize fuzzed data as various types
+void FuzzDeserialize(const uint8_t* data, size_t size) {
+ FuzzDeserializeUint8(data, size);
+ FuzzDeserializeUint16(data, size);
+ FuzzDeserializeUint32(data, size);
+ FuzzDeserializeUint64(data, size);
+ FuzzDeserializeInt8(data, size);
+ FuzzDeserializeInt16(data, size);
+ FuzzDeserializeInt32(data, size);
+ FuzzDeserializeInt64(data, size);
+ FuzzDeserializeFloat32(data, size);
+ FuzzDeserializeFloat64(data, size);
+ FuzzDeserializeFixstr(data, size);
+ FuzzDeserializeFixmap(data, size);
+ FuzzDeserializeVariant(data, size);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzSerializeDeserialize(data, size);
+ FuzzDeserialize(data, size);
return 0;
}
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index 1ce9c99..b3534de 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -45,6 +45,7 @@
cc_binary {
name: "pdx_tool",
+ system_ext_specific: true,
defaults: ["pdx_default_transport_compiler_defaults"],
srcs: [
"pdx_tool.cpp",
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 9ca8b0b..48abdce 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -52,36 +52,17 @@
license: "include/KHR/NOTICE",
}
-llndk_library {
- name: "libEGL",
- symbol_file: "libs/libEGL.map.txt",
- export_include_dirs: ["include"],
-}
-
-llndk_library {
- name: "libGLESv1_CM",
- symbol_file: "libs/libGLESv1_CM.map.txt",
- export_include_dirs: ["include"],
-}
-
-llndk_library {
- name: "libGLESv2",
- symbol_file: "libs/libGLESv2.map.txt",
- export_include_dirs: ["include"],
-}
-
-llndk_library {
- name: "libGLESv3",
- symbol_file: "libs/libGLESv3.map.txt",
- export_include_dirs: ["include"],
-}
-
cc_library_headers {
name: "gl_headers",
vendor_available: true,
export_include_dirs: ["include"],
}
+llndk_headers {
+ name: "gl_llndk_headers",
+ export_include_dirs: ["include"],
+}
+
subdirs = [
"*",
]
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index e8d3684..ed2ed83 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -133,6 +133,7 @@
cc_library_shared {
name: "libEGL",
defaults: ["egl_libs_defaults"],
+ llndk_stubs: "libEGL.llndk",
srcs: [
"EGL/egl_tls.cpp",
"EGL/egl_cache.cpp",
@@ -198,6 +199,7 @@
cc_library_shared {
name: "libGLESv1_CM",
defaults: ["gles_libs_defaults"],
+ llndk_stubs: "libGLESv1_CM.llndk",
srcs: ["GLES_CM/gl.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv1\""],
version_script: "libGLESv1_CM.map.txt",
@@ -209,6 +211,7 @@
cc_library_shared {
name: "libGLESv2",
defaults: ["gles_libs_defaults"],
+ llndk_stubs: "libGLESv2.llndk",
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv2\""],
@@ -223,6 +226,31 @@
cc_library_shared {
name: "libGLESv3",
defaults: ["gles_libs_defaults"],
+ llndk_stubs: "libGLESv3.llndk",
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv3\""],
}
+
+llndk_library {
+ name: "libEGL.llndk",
+ symbol_file: "libEGL.map.txt",
+ export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+ name: "libGLESv1_CM.llndk",
+ symbol_file: "libGLESv1_CM.map.txt",
+ export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+ name: "libGLESv2.llndk",
+ symbol_file: "libGLESv2.map.txt",
+ export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+ name: "libGLESv3.llndk",
+ symbol_file: "libGLESv3.map.txt",
+ export_llndk_headers: ["gl_llndk_headers"],
+}
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index 9c80212..59ef3c8 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -15,6 +15,7 @@
*/
import java.io.PrintStream;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -201,6 +202,33 @@
out.println(iii + ");");
}
+ // Function to automatically generate properly formatted function calls that
+ // comply with clang format rules
+ public static String formatFunctionCall(String indent, String functionCall) {
+ final int MAXLEN = 100;
+ String tokens[] = functionCall.split("\\(|\\)", 2);
+ String params[] = tokens[1].split(",\\s*");
+ String formatted = indent + tokens[0] + "(";
+ char[] chars = new char[indent.length() + tokens[0].length() + 1];
+ Arrays.fill(chars, ' ');
+ String multiIndent = new String(chars);
+ ArrayList<String> lines = new ArrayList<String>();
+ for(int i = 0; i < params.length; i++) {
+ String terminator = ((i == params.length - 1) ? "" : ",");
+ if(indent.length() + formatted.length() + params[i].length() > MAXLEN) {
+ lines.add(formatted);
+ if (!indent.equals(multiIndent)) {
+ indent = multiIndent;
+ }
+ formatted = indent + params[i] + terminator;
+ } else {
+ formatted += (i == 0 ? "" : " ") + params[i] + terminator;
+ }
+ }
+ lines.add(formatted);
+ return String.join("\n", lines);
+ }
+
void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
String iii) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
@@ -1538,14 +1566,19 @@
"_exception ? JNI_ABORT : 0" : "0")) +
");");
} else {
- out.println(indent + indent +
+ String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
+ "_" + cfunc.getArgName(cIndex) + "BufferOffset";
+ String typeCast = "(char *)" + cfunc.getArgName(cIndex);
+ String withOffset = "(void *)(" + typeCast + " - " + bufferOffset + ")";
+ String releasePointerCall = (
"releasePointer(_env, " + array + ", " +
- cfunc.getArgName(cIndex) +
+ withOffset +
", " +
(cfunc.getArgType(cIndex).isConst() ?
"JNI_FALSE" : (emitExceptionCheck ?
"_exception ? JNI_FALSE : JNI_TRUE" : "JNI_TRUE")) +
");");
+ out.println(formatFunctionCall(indent + indent, releasePointerCall));
}
out.println(indent + "}");
}
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 4da6db3..9a9bca1 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -21,6 +21,8 @@
"libbinder",
"libcutils",
"libgfxstats",
+ "libgpumem",
+ "libgpumemtracer",
"libgraphicsenv",
"liblog",
"libutils",
@@ -85,6 +87,10 @@
name: "gpuservice",
defaults: ["libgpuservice_binary"],
init_rc: ["gpuservice.rc"],
+ required: [
+ "bpfloader",
+ "gpu_mem.o",
+ ],
srcs: [":gpuservice_binary_sources"],
shared_libs: [
"libgpuservice",
diff --git a/services/gpuservice/CleanSpec.mk b/services/gpuservice/CleanSpec.mk
new file mode 100644
index 0000000..482fc6d
--- /dev/null
+++ b/services/gpuservice/CleanSpec.mk
@@ -0,0 +1,52 @@
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# Remove gpu_mem.o
+$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/frameworks/native/services/gpuservice/bpf)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpu_mem.o_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpu_mem.o_gpu_mem.o_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpu_mem.o)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpu_mem.o-timestamp)
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 81b0a46..52d5d4f 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,13 +24,16 @@
#include <binder/Parcel.h>
#include <binder/PermissionCache.h>
#include <cutils/properties.h>
+#include <gpumem/GpuMem.h>
#include <gpustats/GpuStats.h>
#include <private/android_filesystem_config.h>
+#include <tracing/GpuMemTracer.h>
#include <utils/String8.h>
#include <utils/Trace.h>
-
#include <vkjson.h>
+#include <thread>
+
namespace android {
using base::StringAppendF;
@@ -45,7 +48,16 @@
const char* const GpuService::SERVICE_NAME = "gpu";
-GpuService::GpuService() : mGpuStats(std::make_unique<GpuStats>()){};
+GpuService::GpuService()
+ : mGpuMem(std::make_shared<GpuMem>()),
+ mGpuStats(std::make_unique<GpuStats>()),
+ mGpuMemTracer(std::make_unique<GpuMemTracer>()) {
+ std::thread asyncInitThread([this]() {
+ mGpuMem->initialize();
+ mGpuMemTracer->initialize(mGpuMem);
+ });
+ asyncInitThread.detach();
+};
void GpuService::setGpuStats(const std::string& driverPackageName,
const std::string& driverVersionName, uint64_t driverVersionCode,
@@ -110,6 +122,7 @@
} else {
bool dumpAll = true;
bool dumpDriverInfo = false;
+ bool dumpMem = false;
bool dumpStats = false;
size_t numArgs = args.size();
@@ -119,15 +132,21 @@
dumpStats = true;
} else if (args[index] == String16("--gpudriverinfo")) {
dumpDriverInfo = true;
+ } else if (args[index] == String16("--gpumem")) {
+ dumpMem = true;
}
}
- dumpAll = !(dumpDriverInfo || dumpStats);
+ dumpAll = !(dumpDriverInfo || dumpMem || dumpStats);
}
if (dumpAll || dumpDriverInfo) {
dumpGameDriverInfo(&result);
result.append("\n");
}
+ if (dumpAll || dumpMem) {
+ mGpuMem->dump(args, &result);
+ result.append("\n");
+ }
if (dumpAll || dumpStats) {
mGpuStats->dump(args, &result);
result.append("\n");
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index d1c3aab..409084b 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -28,7 +28,9 @@
namespace android {
+class GpuMem;
class GpuStats;
+class GpuMemTracer;
class GpuService : public BnGpuService, public PriorityDumper {
public:
@@ -74,7 +76,9 @@
/*
* Attributes
*/
+ std::shared_ptr<GpuMem> mGpuMem;
std::unique_ptr<GpuStats> mGpuStats;
+ std::unique_ptr<GpuMemTracer> mGpuMemTracer;
std::mutex mLock;
std::string mDeveloperDriverPath;
};
diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp
new file mode 100644
index 0000000..b875814
--- /dev/null
+++ b/services/gpuservice/bpfprogs/Android.bp
@@ -0,0 +1,22 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+bpf {
+ name: "gpu_mem.o",
+ srcs: ["gpu_mem.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpu_mem.c
new file mode 100644
index 0000000..c75213b
--- /dev/null
+++ b/services/gpuservice/bpfprogs/gpu_mem.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <bpf_helpers.h>
+
+/*
+ * On Android the number of active processes using gpu is limited.
+ * So this is assumed to be true: SUM(num_procs_using_gpu[i]) <= 1024
+ */
+#define GPU_MEM_TOTAL_MAP_SIZE 1024
+
+/*
+ * This map maintains the global and per process gpu memory total counters.
+ *
+ * The KEY is ((gpu_id << 32) | pid) while VAL is the size in bytes.
+ * Use HASH type here since key is not int.
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ */
+DEFINE_BPF_MAP_GRO(gpu_mem_total_map, HASH, uint64_t, uint64_t, GPU_MEM_TOTAL_MAP_SIZE,
+ AID_GRAPHICS);
+
+/* This struct aligns with the fields offsets of the raw tracepoint format */
+struct gpu_mem_total_args {
+ uint64_t ignore;
+ /* Actual fields start at offset 8 */
+ uint32_t gpu_id;
+ uint32_t pid;
+ uint64_t size;
+};
+
+/*
+ * This program parses the gpu_mem/gpu_mem_total tracepoint's data into
+ * {KEY, VAL} pair used to update the corresponding bpf map.
+ *
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ * Upon seeing size 0, the corresponding KEY needs to be cleaned up.
+ */
+DEFINE_BPF_PROG("tracepoint/gpu_mem/gpu_mem_total", AID_ROOT, AID_GRAPHICS, tp_gpu_mem_total)
+(struct gpu_mem_total_args* args) {
+ uint64_t key = 0;
+ uint64_t cur_val = 0;
+ uint64_t* prev_val = NULL;
+
+ /* The upper 32 bits are for gpu_id while the lower is the pid */
+ key = ((uint64_t)args->gpu_id << 32) | args->pid;
+ cur_val = args->size;
+
+ if (!cur_val) {
+ bpf_gpu_mem_total_map_delete_elem(&key);
+ return 0;
+ }
+
+ prev_val = bpf_gpu_mem_total_map_lookup_elem(&key);
+ if (prev_val) {
+ *prev_val = cur_val;
+ } else {
+ bpf_gpu_mem_total_map_update_elem(&key, &cur_val, BPF_NOEXIST);
+ }
+ return 0;
+}
+
+char _license[] SEC("license") = "Apache 2.0";
diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp
new file mode 100644
index 0000000..b2230b6
--- /dev/null
+++ b/services/gpuservice/gpumem/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libgpumem",
+ srcs: [
+ "GpuMem.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: [
+ "libbase",
+ "libbpf_android",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
new file mode 100644
index 0000000..3aa862f
--- /dev/null
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "GpuMem"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "gpumem/GpuMem.h"
+
+#include <android-base/stringprintf.h>
+#include <libbpf.h>
+#include <libbpf_android.h>
+#include <log/log.h>
+#include <unistd.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+using base::StringAppendF;
+
+GpuMem::~GpuMem() {
+ bpf_detach_tracepoint(kGpuMemTraceGroup, kGpuMemTotalTracepoint);
+}
+
+void GpuMem::initialize() {
+ // Make sure bpf programs are loaded
+ bpf::waitForProgsLoaded();
+
+ errno = 0;
+ int fd = bpf::retrieveProgram(kGpuMemTotalProgPath);
+ if (fd < 0) {
+ ALOGE("Failed to retrieve pinned program from %s [%d(%s)]", kGpuMemTotalProgPath, errno,
+ strerror(errno));
+ return;
+ }
+
+ // Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
+ errno = 0;
+ int count = 0;
+ while (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
+ if (++count > kGpuWaitTimeout) {
+ ALOGE("Failed to attach bpf program to %s/%s tracepoint [%d(%s)]", kGpuMemTraceGroup,
+ kGpuMemTotalTracepoint, errno, strerror(errno));
+ return;
+ }
+ // Retry until GPU driver loaded or timeout.
+ sleep(1);
+ }
+
+ // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
+ errno = 0;
+ auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath);
+ if (!map.isValid()) {
+ ALOGE("Failed to create bpf map from %s [%d(%s)]", kGpuMemTotalMapPath, errno,
+ strerror(errno));
+ return;
+ }
+ setGpuMemTotalMap(map);
+
+ mInitialized.store(true);
+}
+
+void GpuMem::setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+ mGpuMemTotalMap = std::move(map);
+}
+
+// Dump the snapshots of global and per process memory usage on all gpus
+void GpuMem::dump(const Vector<String16>& /* args */, std::string* result) {
+ ATRACE_CALL();
+
+ if (!mInitialized.load() || !mGpuMemTotalMap.isValid()) {
+ result->append("Failed to initialize GPU memory eBPF\n");
+ return;
+ }
+
+ auto res = mGpuMemTotalMap.getFirstKey();
+ if (!res.ok()) {
+ result->append("GPU memory total usage map is empty\n");
+ return;
+ }
+ uint64_t key = res.value();
+ // unordered_map<gpu_id, vector<pair<pid, size>>>
+ std::unordered_map<uint32_t, std::vector<std::pair<uint32_t, uint64_t>>> dumpMap;
+ while (true) {
+ uint32_t gpu_id = key >> 32;
+ uint32_t pid = key;
+
+ res = mGpuMemTotalMap.readValue(key);
+ if (!res.ok()) break;
+ uint64_t size = res.value();
+
+ dumpMap[gpu_id].emplace_back(pid, size);
+
+ res = mGpuMemTotalMap.getNextKey(key);
+ if (!res.ok()) break;
+ key = res.value();
+ }
+
+ for (auto& gpu : dumpMap) {
+ if (gpu.second.empty()) continue;
+ StringAppendF(result, "Memory snapshot for GPU %u:\n", gpu.first);
+
+ std::sort(gpu.second.begin(), gpu.second.end(),
+ [](auto& l, auto& r) { return l.first < r.first; });
+
+ int i = 0;
+ if (gpu.second[0].first != 0) {
+ StringAppendF(result, "Global total: N/A\n");
+ } else {
+ StringAppendF(result, "Global total: %" PRIu64 "\n", gpu.second[0].second);
+ i++;
+ }
+ for (; i < gpu.second.size(); i++) {
+ StringAppendF(result, "Proc %u total: %" PRIu64 "\n", gpu.second[i].first,
+ gpu.second[i].second);
+ }
+ }
+}
+
+void GpuMem::traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid,
+ uint64_t size)>& callback) {
+ auto res = mGpuMemTotalMap.getFirstKey();
+ if (!res.ok()) return;
+ uint64_t key = res.value();
+ while (true) {
+ uint32_t gpu_id = key >> 32;
+ uint32_t pid = key;
+
+ res = mGpuMemTotalMap.readValue(key);
+ if (!res.ok()) break;
+ uint64_t size = res.value();
+
+ callback(systemTime(), gpu_id, pid, size);
+ res = mGpuMemTotalMap.getNextKey(key);
+ if (!res.ok()) break;
+ key = res.value();
+ }
+}
+
+} // namespace android
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
new file mode 100644
index 0000000..de691e2
--- /dev/null
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <bpf/BpfMap.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <functional>
+
+namespace android {
+
+class GpuMem {
+public:
+ GpuMem() = default;
+ ~GpuMem();
+
+ // initialize eBPF program and map
+ void initialize();
+ // dumpsys interface
+ void dump(const Vector<String16>& args, std::string* result);
+ bool isInitialized() { return mInitialized.load(); }
+
+ // Traverse the gpu memory total map to feed the callback function.
+ void traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid,
+ uint64_t size)>& callback);
+
+private:
+ // Friend class for testing.
+ friend class TestableGpuMem;
+
+ // set gpu memory total map
+ void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map);
+
+ // indicate whether ebpf has been initialized
+ std::atomic<bool> mInitialized = false;
+ // bpf map for GPU memory total data
+ android::bpf::BpfMap<uint64_t, uint64_t> mGpuMemTotalMap;
+
+ // gpu memory tracepoint event category
+ static constexpr char kGpuMemTraceGroup[] = "gpu_mem";
+ // gpu memory total tracepoint
+ static constexpr char kGpuMemTotalTracepoint[] = "gpu_mem_total";
+ // pinned gpu memory total bpf c program path in bpf sysfs
+ static constexpr char kGpuMemTotalProgPath[] =
+ "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total";
+ // pinned gpu memory total bpf map path in bpf sysfs
+ static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
+ // 30 seconds timeout for trying to attach bpf program to tracepoint
+ static constexpr int kGpuWaitTimeout = 30;
+};
+
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 538506d..940a26b 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -19,11 +19,16 @@
address: true,
},
srcs: [
+ "GpuMemTest.cpp",
"GpuStatsTest.cpp",
],
shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
"libcutils",
"libgfxstats",
+ "libgpumem",
"libgraphicsenv",
"liblog",
"libstatslog",
@@ -33,4 +38,5 @@
static_libs: [
"libgmock",
],
+ require_root: true,
}
diff --git a/services/gpuservice/tests/unittests/AndroidTest.xml b/services/gpuservice/tests/unittests/AndroidTest.xml
index 66f51c7..72976a8 100644
--- a/services/gpuservice/tests/unittests/AndroidTest.xml
+++ b/services/gpuservice/tests/unittests/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="cleanup" value="true" />
<option name="push" value="gpuservice_unittest->/data/local/tmp/gpuservice_unittest" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
new file mode 100644
index 0000000..e916221
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "gpuservice_unittest"
+
+#include <android-base/stringprintf.h>
+#include <bpf/BpfMap.h>
+#include <gmock/gmock.h>
+#include <gpumem/GpuMem.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include "TestableGpuMem.h"
+
+namespace android {
+namespace {
+
+using base::StringPrintf;
+using testing::HasSubstr;
+
+constexpr uint32_t TEST_MAP_SIZE = 10;
+constexpr uint64_t TEST_GLOBAL_KEY = 0;
+constexpr uint64_t TEST_GLOBAL_VAL = 123;
+constexpr uint64_t TEST_PROC_KEY_1 = 1;
+constexpr uint64_t TEST_PROC_VAL_1 = 234;
+constexpr uint64_t TEST_PROC_KEY_2 = 4294967298; // (1 << 32) + 2
+constexpr uint64_t TEST_PROC_VAL_2 = 345;
+constexpr uint32_t TEST_KEY_MASK = 0x1 | 0x2 | 0x4;
+constexpr uint32_t TEST_KEY_COUNT = 3;
+
+class GpuMemTest : public testing::Test {
+public:
+ GpuMemTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~GpuMemTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ void SetUp() override {
+ bpf::setrlimitForTest();
+
+ mGpuMem = std::make_unique<GpuMem>();
+ mTestableGpuMem = TestableGpuMem(mGpuMem.get());
+ mTestableGpuMem.setInitialized();
+ errno = 0;
+ mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
+ BPF_F_NO_PREALLOC);
+
+ EXPECT_EQ(0, errno);
+ EXPECT_LE(0, mTestMap.getMap().get());
+ EXPECT_TRUE(mTestMap.isValid());
+ }
+
+ std::string dumpsys() {
+ std::string result;
+ Vector<String16> args;
+ mGpuMem->dump(args, &result);
+ return result;
+ }
+
+ std::unique_ptr<GpuMem> mGpuMem;
+ TestableGpuMem mTestableGpuMem;
+ bpf::BpfMap<uint64_t, uint64_t> mTestMap;
+};
+
+TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) {
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(),
+ "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map");
+}
+
+TEST_F(GpuMemTest, bpfInitializationFailed) {
+ EXPECT_EQ(dumpsys(), "Failed to initialize GPU memory eBPF\n");
+}
+
+TEST_F(GpuMemTest, gpuMemTotalMapEmpty) {
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_EQ(dumpsys(), "GPU memory total usage map is empty\n");
+}
+
+TEST_F(GpuMemTest, globalMemTotal) {
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(), HasSubstr(StringPrintf("Global total: %" PRIu64 "\n", TEST_GLOBAL_VAL)));
+}
+
+TEST_F(GpuMemTest, missingGlobalMemTotal) {
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(), HasSubstr("Global total: N/A"));
+}
+
+TEST_F(GpuMemTest, procMemTotal) {
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n",
+ (uint32_t)(TEST_PROC_KEY_1 >> 32))));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_1,
+ TEST_PROC_VAL_1)));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n",
+ (uint32_t)(TEST_PROC_KEY_2 >> 32))));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_2,
+ TEST_PROC_VAL_2)));
+}
+
+TEST_F(GpuMemTest, traverseGpuMemTotals) {
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ static uint32_t sMask = 0;
+ static uint32_t sCount = 0;
+ mGpuMem->traverseGpuMemTotals([](int64_t, uint32_t gpuId, uint32_t pid, uint64_t size) {
+ const uint64_t key = ((uint64_t)gpuId << 32) | pid;
+ switch (key) {
+ case TEST_GLOBAL_KEY:
+ EXPECT_EQ(size, TEST_GLOBAL_VAL);
+ sMask |= 0x1;
+ break;
+ case TEST_PROC_KEY_1:
+ EXPECT_EQ(size, TEST_PROC_VAL_1);
+ sMask |= 0x2;
+ break;
+ case TEST_PROC_KEY_2:
+ EXPECT_EQ(size, TEST_PROC_VAL_2);
+ sMask |= 0x4;
+ break;
+ }
+ sCount++;
+ });
+
+ EXPECT_EQ(sMask, TEST_KEY_MASK);
+ EXPECT_EQ(sCount, TEST_KEY_COUNT);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/TestableGpuMem.h b/services/gpuservice/tests/unittests/TestableGpuMem.h
new file mode 100644
index 0000000..6c8becb
--- /dev/null
+++ b/services/gpuservice/tests/unittests/TestableGpuMem.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <bpf/BpfMap.h>
+#include <gpumem/GpuMem.h>
+
+namespace android {
+
+class TestableGpuMem {
+public:
+ TestableGpuMem() = default;
+ explicit TestableGpuMem(GpuMem *gpuMem) : mGpuMem(gpuMem) {}
+
+ void setInitialized() { mGpuMem->mInitialized.store(true); }
+
+ void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+ mGpuMem->setGpuMemTotalMap(map);
+ }
+
+ std::string getGpuMemTraceGroup() { return mGpuMem->kGpuMemTraceGroup; }
+
+ std::string getGpuMemTotalTracepoint() { return mGpuMem->kGpuMemTotalTracepoint; }
+
+ std::string getGpuMemTotalProgPath() { return mGpuMem->kGpuMemTotalProgPath; }
+
+ std::string getGpuMemTotalMapPath() { return mGpuMem->kGpuMemTotalMapPath; }
+
+private:
+ GpuMem *mGpuMem;
+};
+
+} // namespace android
diff --git a/services/gpuservice/tracing/Android.bp b/services/gpuservice/tracing/Android.bp
new file mode 100644
index 0000000..919fed3
--- /dev/null
+++ b/services/gpuservice/tracing/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libgpumemtracer",
+ srcs: [
+ "GpuMemTracer.cpp",
+ ],
+ shared_libs: [
+ "libgpumem",
+ "libbase",
+ "liblog",
+ "libutils",
+ ],
+ static_libs: [
+ "libperfetto_client_experimental",
+ ],
+ export_include_dirs: ["include"],
+ export_static_lib_headers: [
+ "libperfetto_client_experimental",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
new file mode 100644
index 0000000..6366e1d
--- /dev/null
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "GpuMemTracer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "tracing/GpuMemTracer.h"
+
+#include <gpumem/GpuMem.h>
+#include <perfetto/trace/android/gpu_mem_event.pbzero.h>
+#include <unistd.h>
+
+#include <thread>
+
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::GpuMemTracer::GpuMemDataSource);
+
+namespace android {
+
+std::mutex GpuMemTracer::sTraceMutex;
+std::condition_variable GpuMemTracer::sCondition;
+bool GpuMemTracer::sTraceStarted;
+
+void GpuMemTracer::initialize(std::shared_ptr<GpuMem> gpuMem) {
+ if (!gpuMem->isInitialized()) {
+ ALOGE("Cannot initialize GpuMemTracer before GpuMem");
+ return;
+ }
+ mGpuMem = gpuMem;
+ perfetto::TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ perfetto::Tracing::Initialize(args);
+ registerDataSource();
+ std::thread tracerThread(&GpuMemTracer::threadLoop, this);
+ pthread_setname_np(tracerThread.native_handle(), "GpuMemTracerThread");
+ tracerThread.detach();
+}
+
+void GpuMemTracer::registerDataSource() {
+ perfetto::DataSourceDescriptor dsd;
+ dsd.set_name(kGpuMemDataSource);
+ GpuMemDataSource::Register(dsd);
+}
+
+void GpuMemTracer::threadLoop() {
+ while (true) {
+ {
+ std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ while (!sTraceStarted) {
+ sCondition.wait(lock);
+ }
+ }
+ traceInitialCounters();
+ {
+ std::lock_guard<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ sTraceStarted = false;
+ }
+ }
+}
+
+void GpuMemTracer::traceInitialCounters() {
+ if (!mGpuMem->isInitialized()) {
+ // This should never happen.
+ ALOGE("Cannot trace without GpuMem initialization");
+ return;
+ }
+ mGpuMem->traverseGpuMemTotals([](int64_t ts, uint32_t gpuId, uint32_t pid, uint64_t size) {
+ GpuMemDataSource::Trace([&](GpuMemDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp(ts);
+ auto* event = packet->set_gpu_mem_total_event();
+ event->set_gpu_id(gpuId);
+ event->set_pid(pid);
+ event->set_size(size);
+ });
+ });
+ // Flush the TraceContext. The last packet in the above loop will go
+ // missing without this flush.
+ GpuMemDataSource::Trace([](GpuMemDataSource::TraceContext ctx) { ctx.Flush(); });
+}
+
+} // namespace android
diff --git a/services/gpuservice/tracing/include/tracing/GpuMemTracer.h b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
new file mode 100644
index 0000000..40deb4c
--- /dev/null
+++ b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <perfetto/tracing.h>
+
+#include <mutex>
+
+namespace android {
+
+class GpuMem;
+
+class GpuMemTracer {
+public:
+ class GpuMemDataSource : public perfetto::DataSource<GpuMemDataSource> {
+ virtual void OnSetup(const SetupArgs&) override{};
+ virtual void OnStart(const StartArgs&) override {
+ std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ sTraceStarted = true;
+ sCondition.notify_all();
+ }
+ virtual void OnStop(const StopArgs&) override{};
+ };
+
+ ~GpuMemTracer() = default;
+
+ // Sets up the perfetto tracing backend and data source.
+ void initialize(std::shared_ptr<GpuMem>);
+ // Registers the data source with the perfetto backend. Called as part of initialize()
+ // and should not be called manually outside of tests. Public to allow for substituting a
+ // perfetto::kInProcessBackend in tests.
+ void registerDataSource();
+
+ static constexpr char kGpuMemDataSource[] = "android.gpu.memory";
+ static std::condition_variable sCondition;
+ static std::mutex sTraceMutex;
+ static bool sTraceStarted;
+
+private:
+ void traceInitialCounters();
+ void threadLoop();
+
+ std::shared_ptr<GpuMem> mGpuMem;
+};
+
+} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index fe016af..2517060 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -482,6 +482,33 @@
}
/**
+ * Raise ANR if there is no focused window.
+ * Before the ANR is raised, do a final state check:
+ * 1. The currently focused application must be the same one we are waiting for.
+ * 2. Ensure we still don't have a focused window.
+ */
+void InputDispatcher::processNoFocusedWindowAnrLocked() {
+ // Check if the application that we are waiting for is still focused.
+ sp<InputApplicationHandle> focusedApplication =
+ getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
+ if (focusedApplication == nullptr ||
+ focusedApplication->getApplicationToken() !=
+ mAwaitedFocusedApplication->getApplicationToken()) {
+ // Unexpected because we should have reset the ANR timer when focused application changed
+ ALOGE("Waited for a focused window, but focused application has already changed to %s",
+ focusedApplication->getName().c_str());
+ return; // The focused application has changed.
+ }
+
+ const sp<InputWindowHandle>& focusedWindowHandle =
+ getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
+ if (focusedWindowHandle != nullptr) {
+ return; // We now have a focused window. No need for ANR.
+ }
+ onAnrLocked(mAwaitedFocusedApplication);
+}
+
+/**
* Check if any of the connections' wait queues have events that are too old.
* If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
* Return the time at which we should wake up next.
@@ -492,8 +519,9 @@
// Check if we are waiting for a focused window to appear. Raise ANR if waited too long
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
if (currentTime >= *mNoFocusedWindowTimeoutTime) {
- onAnrLocked(mAwaitedFocusedApplication);
+ processNoFocusedWindowAnrLocked();
mAwaitedFocusedApplication.clear();
+ mNoFocusedWindowTimeoutTime = std::nullopt;
return LONG_LONG_MIN;
} else {
// Keep waiting
@@ -518,7 +546,7 @@
connection->responsive = false;
// Stop waking up for this unresponsive connection
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
- onAnrLocked(connection);
+ onAnrLocked(*connection);
return LONG_LONG_MIN;
}
@@ -1477,6 +1505,7 @@
DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
mNoFocusedWindowTimeoutTime = currentTime + timeout;
mAwaitedFocusedApplication = focusedApplicationHandle;
+ mAwaitedApplicationDisplayId = displayId;
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout));
@@ -3567,6 +3596,10 @@
return getValueByKey(mWindowHandlesByDisplay, displayId);
}
+sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+ return getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+}
+
sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
const sp<IBinder>& windowHandleToken) const {
if (windowHandleToken == nullptr) {
@@ -4542,12 +4575,12 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+void InputDispatcher::onAnrLocked(const Connection& connection) {
// Since we are allowing the policy to extend the timeout, maybe the waitQueue
// is already healthy again. Don't raise ANR in this situation
- if (connection->waitQueue.empty()) {
+ if (connection.waitQueue.empty()) {
ALOGI("Not raising ANR because the connection %s has recovered",
- connection->inputChannel->getName().c_str());
+ connection.inputChannel->getName().c_str());
return;
}
/**
@@ -4558,21 +4591,21 @@
* processes the events linearly. So providing information about the oldest entry seems to be
* most useful.
*/
- DispatchEntry* oldestEntry = *connection->waitQueue.begin();
+ DispatchEntry* oldestEntry = *connection.waitQueue.begin();
const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
std::string reason =
android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
- connection->inputChannel->getName().c_str(),
+ connection.inputChannel->getName().c_str(),
ns2ms(currentWait),
oldestEntry->eventEntry->getDescription().c_str());
- updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()),
+ updateLastAnrStateLocked(getWindowHandleLocked(connection.inputChannel->getConnectionToken()),
reason);
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = nullptr;
- commandEntry->inputChannel = connection->inputChannel;
+ commandEntry->inputChannel = connection.inputChannel;
commandEntry->reason = std::move(reason);
postCommandLocked(std::move(commandEntry));
}
@@ -4672,15 +4705,15 @@
void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
const sp<IBinder>& connectionToken,
nsecs_t timeoutExtension) {
+ if (connectionToken == nullptr && application != nullptr) {
+ // The ANR happened because there's no focused window
+ mNoFocusedWindowTimeoutTime = now() + timeoutExtension;
+ mAwaitedFocusedApplication = application;
+ }
+
sp<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
- if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) {
- // Maybe ANR happened because there's no focused window?
- mNoFocusedWindowTimeoutTime = now() + timeoutExtension;
- mAwaitedFocusedApplication = application;
- } else {
- // It's also possible that the connection already disappeared. No action necessary.
- }
+ // It's possible that the connection already disappeared. No action necessary.
return;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e679c6b..31fad1f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -300,6 +300,7 @@
REQUIRES(mLock);
sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
REQUIRES(mLock);
+ sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
@@ -366,6 +367,11 @@
* Used to raise an ANR when we have no focused window.
*/
sp<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+ /**
+ * The displayId that the focused application is associated with.
+ */
+ int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock);
+ void processNoFocusedWindowAnrLocked() REQUIRES(mLock);
// Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
// AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -489,7 +495,7 @@
REQUIRES(mLock);
void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
- void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void onAnrLocked(const Connection& connection) REQUIRES(mLock);
void onAnrLocked(const sp<InputApplicationHandle>& application) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h
index 5a32443..7de9b830 100644
--- a/services/inputflinger/reader/include/TouchVideoDevice.h
+++ b/services/inputflinger/reader/include/TouchVideoDevice.h
@@ -102,7 +102,7 @@
* How many buffers to keep for the internal queue. When the internal buffer
* exceeds this capacity, oldest frames will be dropped.
*/
- static constexpr size_t MAX_QUEUE_SIZE = 10;
+ static constexpr size_t MAX_QUEUE_SIZE = 20;
std::vector<TouchVideoFrame> mFrames;
/**
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 8091036..cbdb1d0 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -781,12 +781,17 @@
mOrientedRanges.clear();
}
- // Create pointer controller if needed.
+ // Create pointer controller if needed, and keep it around if Pointer Capture is enabled to
+ // preserve the cursor position.
if (mDeviceMode == DeviceMode::POINTER ||
- (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches)) {
+ (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
+ (mParameters.deviceType == Parameters::DeviceType::POINTER && mConfig.pointerCapture)) {
if (mPointerController == nullptr) {
mPointerController = getContext()->getPointerController(getDeviceId());
}
+ if (mConfig.pointerCapture) {
+ mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
} else {
mPointerController.reset();
}
@@ -3433,7 +3438,6 @@
void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down,
bool hovering) {
int32_t metaState = getContext()->getGlobalMetaState();
- int32_t displayId = mViewport.displayId;
if (down || hovering) {
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -3443,7 +3447,7 @@
} else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
}
- displayId = mPointerController->getDisplayId();
+ int32_t displayId = mPointerController->getDisplayId();
float xCursorPosition;
float yCursorPosition;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 1a133dc..668a7ab 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -150,6 +150,7 @@
const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
ADD_FAILURE() << "Did not receive ANR callback";
+ return {};
}
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
// the dispatcher started counting before this function was called
@@ -3031,4 +3032,64 @@
mFocusedWindow->assertNoEvents();
}
+/**
+ * If we have no focused window, and a key comes in, we start the ANR timer.
+ * The focused application should add a focused window before the timer runs out to prevent ANR.
+ *
+ * If the user touches another application during this time, the key should be dropped.
+ * Next, if a new focused window comes in, without toggling the focused application,
+ * then no ANR should occur.
+ *
+ * Normally, we would expect the new focused window to be accompanied by 'setFocusedApplication',
+ * but in some cases the policy may not update the focused application.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
+ sp<FakeApplicationHandle> focusedApplication = new FakeApplicationHandle();
+ focusedApplication->setDispatchingTimeout(60ms);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
+ // The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
+ mFocusedWindow->setFocus(false);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mFocusedWindow->consumeFocusEvent(false);
+
+ // Send a key. The ANR timer should start because there is no focused window.
+ // 'focusedApplication' will get blamed if this timer completes.
+ // Key will not be sent anywhere because we have no focused window. It will remain pending.
+ int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+
+ // Wait until dispatcher starts the "no focused window" timer. If we don't wait here,
+ // then the injected touches won't cause the focused event to get dropped.
+ // The dispatcher only checks for whether the queue should be pruned upon queueing.
+ // If we inject the touch right away and the ANR timer hasn't started, the touch event would
+ // simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'.
+ // For this test, it means that the key would get delivered to the window once it becomes
+ // focused.
+ std::this_thread::sleep_for(10ms);
+
+ // Touch unfocused window. This should force the pending key to get dropped.
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {UNFOCUSED_WINDOW_LOCATION});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // We do not consume the motion right away, because that would require dispatcher to first
+ // process (== drop) the key event, and by that time, ANR will be raised.
+ // Set the focused window first.
+ mFocusedWindow->setFocus(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mFocusedWindow->consumeFocusEvent(true);
+ // We do not call "setFocusedApplication" here, even though the newly focused window belongs
+ // to another application. This could be a bug / behaviour in the policy.
+
+ mUnfocusedWindow->consumeMotionDown();
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // Should not ANR because we actually have a focused window. It was just added too slowly.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled());
+}
+
} // namespace android::inputdispatcher
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index b4b5f98..3cccaf9 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <log/log.h>
#include <sys/socket.h>
#include <utils/threads.h>
@@ -28,6 +29,12 @@
#define UNUSED(x) (void)(x)
namespace android {
+namespace {
+
+// Used as the default value for the target SDK until it's obtained via getTargetSdkVersion.
+constexpr int kTargetSdkUnknown = 0;
+
+} // namespace
SensorService::SensorEventConnection::SensorEventConnection(
const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode,
@@ -35,9 +42,9 @@
: mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false),
mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr),
mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
- mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false) {
+ mPackageName(packageName), mOpPackageName(opPackageName), mTargetSdk(kTargetSdkUnknown),
+ mDestroyed(false) {
mChannel = new BitTube(mService->mSocketBufferSize);
- mTargetSdk = SensorService::getTargetSdkVersion(opPackageName);
#if DEBUG_CONNECTIONS
mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
mTotalAcksNeeded = mTotalAcksReceived = 0;
@@ -47,20 +54,13 @@
SensorService::SensorEventConnection::~SensorEventConnection() {
ALOGD_IF(DEBUG_CONNECTIONS, "~SensorEventConnection(%p)", this);
destroy();
-}
-
-void SensorService::SensorEventConnection::destroy() {
- Mutex::Autolock _l(mDestroyLock);
-
- // destroy once only
- if (mDestroyed) {
- return;
- }
-
mService->cleanupConnection(this);
if (mEventCache != nullptr) {
delete[] mEventCache;
}
+}
+
+void SensorService::SensorEventConnection::destroy() {
mDestroyed = true;
}
@@ -445,6 +445,14 @@
bool success = true;
const auto iter = mHandleToAppOp.find(event.sensor);
if (iter != mHandleToAppOp.end()) {
+ if (mTargetSdk == kTargetSdkUnknown) {
+ // getTargetSdkVersion returns -1 if it fails so this operation should only be run once
+ // per connection and then cached. Perform this here as opposed to in the constructor to
+ // avoid log spam for NDK/VNDK clients that don't use sensors guarded with permissions
+ // and pass in invalid op package names.
+ mTargetSdk = SensorService::getTargetSdkVersion(mOpPackageName);
+ }
+
// Special handling for step count/detect backwards compatibility: if the app's target SDK
// is pre-Q, still permit delivering events to the app even if permission isn't granted
// (since this permission was only introduced in Q)
@@ -665,6 +673,11 @@
int handle, bool enabled, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs,
int reservedFlags)
{
+ if (mDestroyed) {
+ android_errorWriteLog(0x534e4554, "168211968");
+ return DEAD_OBJECT;
+ }
+
status_t err;
if (enabled) {
err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs,
@@ -679,10 +692,19 @@
status_t SensorService::SensorEventConnection::setEventRate(
int handle, nsecs_t samplingPeriodNs)
{
+ if (mDestroyed) {
+ android_errorWriteLog(0x534e4554, "168211968");
+ return DEAD_OBJECT;
+ }
+
return mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
}
status_t SensorService::SensorEventConnection::flush() {
+ if (mDestroyed) {
+ return DEAD_OBJECT;
+ }
+
return mService->flushSensor(this, mOpPackageName);
}
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 8f2d5db..9487a39 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SENSOR_EVENT_CONNECTION_H
#define ANDROID_SENSOR_EVENT_CONNECTION_H
+#include <atomic>
#include <stdint.h>
#include <sys/types.h>
#include <unordered_map>
@@ -182,8 +183,8 @@
int mTotalAcksNeeded, mTotalAcksReceived;
#endif
- mutable Mutex mDestroyLock;
- bool mDestroyed;
+ // Used to track if this object was inappropriately used after destroy().
+ std::atomic_bool mDestroyed;
// Store a mapping of sensor handles to required AppOp for a sensor. This map only contains a
// valid mapping for sensors that require a permission in order to reduce the lookup time.
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 60f9cd9..3ca34bb 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -79,6 +79,8 @@
bool SensorService::sHmacGlobalKeyIsValid = false;
std::map<String16, int> SensorService::sPackageTargetVersion;
Mutex SensorService::sPackageTargetVersionLock;
+String16 SensorService::sSensorInterfaceDescriptorPrefix =
+ String16("android.frameworks.sensorservice@");
AppOpsManager SensorService::sAppOpsManager;
#define SENSOR_SERVICE_DIR "/data/system/sensor_service"
@@ -1847,6 +1849,13 @@
}
int SensorService::getTargetSdkVersion(const String16& opPackageName) {
+ // Don't query the SDK version for the ISensorManager descriptor as it doesn't have one. This
+ // descriptor tends to be used for VNDK clients, but can technically be set by anyone so don't
+ // give it elevated privileges.
+ if (opPackageName.startsWith(sSensorInterfaceDescriptorPrefix)) {
+ return -1;
+ }
+
Mutex::Autolock packageLock(sPackageTargetVersionLock);
int targetSdkVersion = -1;
auto entry = sPackageTargetVersion.find(opPackageName);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 3bb8421..052cbfe 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -424,6 +424,7 @@
static AppOpsManager sAppOpsManager;
static std::map<String16, int> sPackageTargetVersion;
static Mutex sPackageTargetVersionLock;
+ static String16 sSensorInterfaceDescriptorPrefix;
};
} // namespace android
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index 80c3b65..ae0a984 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -58,7 +58,7 @@
std::vector<int32_t> buckets = chargeCycles.cycleBucket;
int initialSize = buckets.size();
for (int i = 0; i < 10 - initialSize; i++) {
- buckets.push_back(-1); // Push -1 for buckets that do not exist.
+ buckets.push_back(0); // Push 0 for buckets that do not exist.
}
android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1],
buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8],
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 34dc536..d3247e1 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -835,6 +835,8 @@
needsProtected == renderEngine.isProtected()) {
mRenderSurface->setProtected(needsProtected);
}
+ } else if (!outputState.isSecure && renderEngine.isProtected()) {
+ renderEngine.useProtectedContext(false);
}
base::unique_fd fd;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 09f37fb..29fd4b9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -159,6 +159,7 @@
EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
}
DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 7a06400..8f76afa 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2871,6 +2871,7 @@
mOutput.mState.usesClientComposition = false;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
@@ -2883,6 +2884,7 @@
mOutput.mState.flipClientTarget = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
@@ -2892,6 +2894,7 @@
TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFailsForClientComposition) {
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
@@ -2904,6 +2907,7 @@
mOutput.mState.flipClientTarget = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
@@ -2914,6 +2918,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -2936,6 +2941,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -2963,6 +2969,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -2991,6 +2998,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3019,6 +3027,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3050,6 +3059,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r2}))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r3}));
@@ -3072,6 +3082,7 @@
struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComposeSurfacesTest {
OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3219,6 +3230,8 @@
mOutput.mState.isSecure = false;
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+ EXPECT_CALL(mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
}
@@ -3311,6 +3324,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index cb467ea..ca7119e 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -313,7 +313,7 @@
logicalOrientation = ui::Rotation::Rotation90;
}
- const ui::Rotation orientation = device->getPhysicalOrientation() + logicalOrientation;
+ const ui::Rotation orientation = logicalOrientation;
switch (orientation) {
case ui::ROTATION_0:
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index fba3261..c8cd442 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -73,6 +73,7 @@
mOutputUsage(GRALLOC_USAGE_HW_COMPOSER),
mProducerSlotSource(0),
mProducerBuffers(),
+ mProducerSlotNeedReallocation(0),
mQueueBufferOutput(),
mSinkBufferWidth(0),
mSinkBufferHeight(0),
@@ -335,10 +336,14 @@
dbgSourceStr(source), *sslot, pslot, result);
uint64_t sourceBit = static_cast<uint64_t>(source) << pslot;
+ // reset producer slot reallocation flag
+ mProducerSlotNeedReallocation &= ~(1ULL << pslot);
+
if ((mProducerSlotSource & (1ULL << pslot)) != sourceBit) {
// This slot was previously dequeued from the other source; must
// re-request the buffer.
- result |= BUFFER_NEEDS_REALLOCATION;
+ mProducerSlotNeedReallocation |= 1ULL << pslot;
+
mProducerSlotSource &= ~(1ULL << pslot);
mProducerSlotSource |= sourceBit;
}
@@ -360,6 +365,9 @@
dbgSourceStr(source), pslot, mProducerBuffers[pslot].get(),
mProducerBuffers[pslot]->getPixelFormat(),
mProducerBuffers[pslot]->getUsage());
+
+ // propagate reallocation to VDS consumer
+ mProducerSlotNeedReallocation |= 1ULL << pslot;
}
return result;
@@ -434,6 +442,11 @@
if (outBufferAge) {
*outBufferAge = 0;
}
+
+ if ((mProducerSlotNeedReallocation & (1ULL << *pslot)) != 0) {
+ result |= BUFFER_NEEDS_REALLOCATION;
+ }
+
return result;
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 3cbad8f..22b5528 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -180,6 +180,10 @@
uint64_t mProducerSlotSource;
sp<GraphicBuffer> mProducerBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ // Need to propagate reallocation to VDS consumer.
+ // Each bit corresponds to a producer slot.
+ uint64_t mProducerSlotNeedReallocation;
+
// The QueueBufferOutput with the latest info from the sink, and with the
// transform hint cleared. Since we defer queueBuffer from the GPU driver
// to the sink, we have to return the previous version.
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 03903f6..55af849 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -793,7 +793,9 @@
// to be applied as per normal (no synchronization).
mCurrentState.barrierLayer_legacy = nullptr;
} else {
- auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber_legacy, this);
+ auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber_legacy,
+ this,
+ barrierLayer);
if (barrierLayer->addSyncPoint(syncPoint)) {
std::stringstream ss;
ss << "Adding sync point " << mCurrentState.frameNumber_legacy;
@@ -818,7 +820,7 @@
ATRACE_CALL();
*stateToCommit = mPendingStates[0];
- mPendingStates.removeAt(0);
+ mPendingStates.pop_front();
ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
}
@@ -857,6 +859,7 @@
mRemoteSyncPoints.pop_front();
} else {
ATRACE_NAME("!frameIsAvailable");
+ mRemoteSyncPoints.front()->checkTimeoutAndLog();
break;
}
} else {
@@ -1445,6 +1448,13 @@
void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
ATRACE_CALL();
+ if (mLayerDetached) {
+ // If the layer is detached, then we don't defer this transaction since we will not
+ // commit the pending state while the layer is detached. Adding sync points may cause
+ // the barrier layer to wait for the states to be committed before dequeuing a buffer.
+ return;
+ }
+
mCurrentState.barrierLayer_legacy = barrierLayer;
mCurrentState.frameNumber_legacy = frameNumber;
// We don't set eTransactionNeeded, because just receiving a deferral
@@ -2403,7 +2413,15 @@
xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0;
ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0;
- layerBounds.inset(xSurfaceInset, ySurfaceInset, xSurfaceInset, ySurfaceInset);
+ // inset while protecting from overflow TODO(b/161235021): What is going wrong
+ // in the overflow scenario?
+ {
+ int32_t tmp;
+ if (!__builtin_add_overflow(layerBounds.left, xSurfaceInset, &tmp)) layerBounds.left = tmp;
+ if (!__builtin_sub_overflow(layerBounds.right, xSurfaceInset, &tmp)) layerBounds.right = tmp;
+ if (!__builtin_add_overflow(layerBounds.top, ySurfaceInset, &tmp)) layerBounds.top = tmp;
+ if (!__builtin_sub_overflow(layerBounds.bottom, ySurfaceInset, &tmp)) layerBounds.bottom = tmp;
+ }
// Input coordinate should match the layer bounds.
info.frameLeft = layerBounds.left;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2c90c92..d071710 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -35,6 +35,7 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
+#include <chrono>
#include <cstdint>
#include <list>
#include <optional>
@@ -881,12 +882,14 @@
class SyncPoint {
public:
- explicit SyncPoint(uint64_t frameNumber, wp<Layer> requestedSyncLayer)
+ explicit SyncPoint(uint64_t frameNumber,
+ wp<Layer> requestedSyncLayer,
+ wp<Layer> barrierLayer_legacy)
: mFrameNumber(frameNumber),
mFrameIsAvailable(false),
mTransactionIsApplied(false),
- mRequestedSyncLayer(requestedSyncLayer) {}
-
+ mRequestedSyncLayer(requestedSyncLayer),
+ mBarrierLayer_legacy(barrierLayer_legacy) {}
uint64_t getFrameNumber() const { return mFrameNumber; }
bool frameIsAvailable() const { return mFrameIsAvailable; }
@@ -899,11 +902,41 @@
sp<Layer> getRequestedSyncLayer() { return mRequestedSyncLayer.promote(); }
+ sp<Layer> getBarrierLayer() const { return mBarrierLayer_legacy.promote(); }
+
+ bool isTimeout() const {
+ using namespace std::chrono_literals;
+ static constexpr std::chrono::nanoseconds TIMEOUT_THRESHOLD = 1s;
+
+ return std::chrono::steady_clock::now() - mCreateTimeStamp > TIMEOUT_THRESHOLD;
+ }
+
+ void checkTimeoutAndLog() {
+ using namespace std::chrono_literals;
+ static constexpr std::chrono::nanoseconds LOG_PERIOD = 1s;
+
+ if (!frameIsAvailable() && isTimeout()) {
+ const auto now = std::chrono::steady_clock::now();
+ if (now - mLastLogTime > LOG_PERIOD) {
+ mLastLogTime = now;
+ sp<Layer> requestedSyncLayer = getRequestedSyncLayer();
+ sp<Layer> barrierLayer = getBarrierLayer();
+ ALOGW("[%s] sync point %" PRIu64 " wait timeout %lld for %s",
+ requestedSyncLayer ? requestedSyncLayer->getDebugName() : "Removed",
+ mFrameNumber, (now - mCreateTimeStamp).count(),
+ barrierLayer ? barrierLayer->getDebugName() : "Removed");
+ }
+ }
+ }
private:
const uint64_t mFrameNumber;
std::atomic<bool> mFrameIsAvailable;
std::atomic<bool> mTransactionIsApplied;
wp<Layer> mRequestedSyncLayer;
+ wp<Layer> mBarrierLayer_legacy;
+ const std::chrono::time_point<std::chrono::steady_clock> mCreateTimeStamp =
+ std::chrono::steady_clock::now();
+ std::chrono::time_point<std::chrono::steady_clock> mLastLogTime;
};
// SyncPoints which will be signaled when the correct frame is at the head
@@ -984,12 +1017,12 @@
State mDrawingState;
// Store a copy of the pending state so that the drawing thread can access the
// states without a lock.
- Vector<State> mPendingStatesSnapshot;
+ std::deque<State> mPendingStatesSnapshot;
// these are protected by an external lock (mStateLock)
State mCurrentState;
std::atomic<uint32_t> mTransactionFlags{0};
- Vector<State> mPendingStates;
+ std::deque<State> mPendingStates;
// Timestamp history for UIAutomation. Thread safe.
FrameTracker mFrameTracker;
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index c5a4689..f273725 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -4,7 +4,5 @@
chaviw@google.com
lpy@google.com
racarr@google.com
-steventhomas@google.com
stoza@google.com
-vhau@google.com
vishnun@google.com
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index ab5773d..61f3fbb 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -255,21 +255,9 @@
}
}
-bool VSyncPredictor::needsMoreSamples(nsecs_t now) const {
- using namespace std::literals::chrono_literals;
+bool VSyncPredictor::needsMoreSamples() const {
std::lock_guard<std::mutex> lk(mMutex);
- bool needsMoreSamples = true;
- if (mTimestamps.size() >= kMinimumSamplesForPrediction) {
- nsecs_t constexpr aLongTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
- if (!(mLastTimestampIndex < 0 || mTimestamps.empty())) {
- auto const lastTimestamp = mTimestamps[mLastTimestampIndex];
- needsMoreSamples = !((lastTimestamp + aLongTime) > now);
- }
- }
-
- ATRACE_INT("VSP-moreSamples", needsMoreSamples);
- return needsMoreSamples;
+ return mTimestamps.size() < kMinimumSamplesForPrediction;
}
void VSyncPredictor::resetModel() {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 3ca878d..5f3c418 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -52,11 +52,10 @@
*/
void setPeriod(nsecs_t period) final;
- /* Query if the model is in need of more samples to make a prediction at timePoint.
- * \param [in] timePoint The timePoint to inquire of.
+ /* Query if the model is in need of more samples to make a prediction.
* \return True, if model would benefit from more samples, False if not.
*/
- bool needsMoreSamples(nsecs_t timePoint) const;
+ bool needsMoreSamples() const final;
std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index c743de0..efa8bab 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -233,6 +233,7 @@
}
void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+ ATRACE_CALL();
mPeriodConfirmationInProgress = true;
mPeriodTransitioningTo = newPeriod;
mMoreSamplesNeeded = true;
@@ -240,8 +241,7 @@
}
void VSyncReactor::endPeriodTransition() {
- setIgnorePresentFencesInternal(false);
- mMoreSamplesNeeded = false;
+ ATRACE_CALL();
mPeriodTransitioningTo.reset();
mPeriodConfirmationInProgress = false;
mLastHwVsync.reset();
@@ -254,6 +254,8 @@
if (!mSupportKernelIdleTimer && period == getPeriod()) {
endPeriodTransition();
+ setIgnorePresentFencesInternal(false);
+ mMoreSamplesNeeded = false;
} else {
startPeriodTransition(period);
}
@@ -303,6 +305,7 @@
std::lock_guard<std::mutex> lk(mMutex);
if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
+ ATRACE_NAME("VSR: period confirmed");
if (mPeriodTransitioningTo) {
mTracker->setPeriod(*mPeriodTransitioningTo);
for (auto& entry : mCallbacks) {
@@ -310,17 +313,29 @@
}
*periodFlushed = true;
}
+
+ if (mLastHwVsync) {
+ mTracker->addVsyncTimestamp(*mLastHwVsync);
+ }
+ mTracker->addVsyncTimestamp(timestamp);
+
endPeriodTransition();
+ mMoreSamplesNeeded = mTracker->needsMoreSamples();
} else if (mPeriodConfirmationInProgress) {
+ ATRACE_NAME("VSR: still confirming period");
mLastHwVsync = timestamp;
mMoreSamplesNeeded = true;
*periodFlushed = false;
} else {
- mMoreSamplesNeeded = false;
+ ATRACE_NAME("VSR: adding sample");
*periodFlushed = false;
+ mTracker->addVsyncTimestamp(timestamp);
+ mMoreSamplesNeeded = mTracker->needsMoreSamples();
}
- mTracker->addVsyncTimestamp(timestamp);
+ if (!mMoreSamplesNeeded) {
+ setIgnorePresentFencesInternal(false);
+ }
return mMoreSamplesNeeded;
}
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 05a6fc3..107c540 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -66,6 +66,8 @@
/* Inform the tracker that the samples it has are not accurate for prediction. */
virtual void resetModel() = 0;
+ virtual bool needsMoreSamples() const = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/StartPropertySetThread.cpp b/services/surfaceflinger/StartPropertySetThread.cpp
index db82772..f42cd53 100644
--- a/services/surfaceflinger/StartPropertySetThread.cpp
+++ b/services/surfaceflinger/StartPropertySetThread.cpp
@@ -31,6 +31,7 @@
property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
// Clear BootAnimation exit flag
property_set("service.bootanim.exit", "0");
+ property_set("service.bootanim.progress", "0");
// Start BootAnimation if not started
property_set("ctl.start", "bootanim");
// Exit immediately
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1342cfc..02b5324 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1567,7 +1567,7 @@
mEventQueue->refresh();
}
-nsecs_t SurfaceFlinger::getVsyncPeriod() const {
+nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
const auto displayId = getInternalDisplayIdLocked();
if (!displayId || !getHwComposer().isConnected(*displayId)) {
return 0;
@@ -1774,7 +1774,7 @@
setPowerModeInternal(display, currentDisplayPowerMode);
// Reset the timing values to account for the period of the swapped in HWC
- const nsecs_t vsyncPeriod = getVsyncPeriod();
+ const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
// The present fences returned from vr_hwc are not an accurate
@@ -2122,7 +2122,8 @@
mTimeStats->incrementCompositionStrategyChanges();
}
- mVSyncModulator->onRefreshed(mHadClientComposition);
+ // TODO: b/160583065 Enable skip validation when SF caches all client composition layers
+ mVSyncModulator->onRefreshed(mHadClientComposition || mReusedClientComposition);
mLayersWithQueuedFrames.clear();
if (mVisibleRegionsDirty) {
@@ -4192,8 +4193,7 @@
{});
setPowerModeInternal(display, hal::PowerMode::ON);
-
- const nsecs_t vsyncPeriod = getVsyncPeriod();
+ const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
// Use phase of 0 since phase is not known.
@@ -4228,7 +4228,7 @@
if (mInterceptor->isEnabled()) {
mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
}
-
+ const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
if (currentMode == hal::PowerMode::OFF) {
if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
@@ -4237,7 +4237,7 @@
if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) {
getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
mScheduler->onScreenAcquired(mAppConnectionHandle);
- mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
}
mVisibleRegionsDirty = true;
@@ -4264,7 +4264,7 @@
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && currentMode == hal::PowerMode::DOZE_SUSPEND) {
mScheduler->onScreenAcquired(mAppConnectionHandle);
- mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
}
} else if (mode == hal::PowerMode::DOZE_SUSPEND) {
// Leave display going to doze
@@ -4377,7 +4377,7 @@
}
void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
- StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriod());
+ StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC());
if (args.size() > 1) {
const auto name = String8(args[1]);
@@ -4442,7 +4442,7 @@
mPhaseConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
- dispSyncPresentTimeOffset, getVsyncPeriod());
+ dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy();
StringAppendF(&result,
@@ -4501,12 +4501,9 @@
void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) {
result.append("Layer frame timestamps:\n");
-
- const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
- const size_t count = currentLayers.size();
- for (size_t i=0 ; i<count ; i++) {
- currentLayers[i]->dumpFrameEvents(result);
- }
+ // Traverse all layers to dump frame-events for each layer
+ mCurrentState.traverseInZOrder(
+ [&] (Layer* layer) { layer->dumpFrameEvents(result); });
}
void SurfaceFlinger::dumpBufferingStats(std::string& result) const {
@@ -5746,7 +5743,7 @@
// TODO(b/116112787) Make buffer usage a parameter.
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
- GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER;
*outBuffer =
getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
static_cast<android_pixel_format>(reqPixelFormat), 1,
@@ -6228,6 +6225,11 @@
Mutex::Autolock lock(mStateLock);
if (authenticateSurfaceTextureLocked(surface)) {
sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+ if (layer == nullptr) {
+ ALOGE("Attempt to set frame rate on a layer that no longer exists");
+ return BAD_VALUE;
+ }
+
if (layer->setFrameRate(
Layer::FrameRate(frameRate,
Layer::FrameRate::convertCompatibility(compatibility)))) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ccaeb2d..c727574 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -852,7 +852,7 @@
/* ------------------------------------------------------------------------
* VSync
*/
- nsecs_t getVsyncPeriod() const REQUIRES(mStateLock);
+ nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
// Sets the refresh rate by switching active configs, if they are available for
// the desired refresh rate.
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index fe2af80..1532855 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -44,7 +44,7 @@
"libtrace_proto",
],
shared_libs: [
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.composer@2.1",
"libandroid",
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 2861013..3535fbb 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -8,12 +8,12 @@
"FakeComposerUtils.cpp",
"SFFakeHwc_test.cpp"
],
+ require_root: true,
shared_libs: [
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "android.hardware.graphics.composer@2.1-resources",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
@@ -33,6 +33,7 @@
"libutils",
],
static_libs: [
+ "android.hardware.graphics.composer@2.1-resources",
"libcompositionengine",
"libgmock",
"libperfetto_client_experimental",
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index ce5f35c..06bdcdc 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -85,7 +85,7 @@
using HotplugEvent = TestableSurfaceFlinger::HotplugEvent;
using HWC2Display = TestableSurfaceFlinger::HWC2Display;
-constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
+constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'667;
constexpr int32_t DEFAULT_DPI = 320;
constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index be49ef3..c2a7752 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -51,6 +51,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
+ bool needsMoreSamples() const final { return false; }
void dump(std::string&) const final {}
private:
@@ -86,6 +87,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
+ bool needsMoreSamples() const final { return false; }
void dump(std::string&) const final {}
private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index fa5cc32..f630e3b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -47,6 +47,7 @@
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index fc39235..d4cd11d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -73,27 +73,27 @@
TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) {
for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
- EXPECT_TRUE(tracker.needsMoreSamples(mNow += mPeriod));
- tracker.addVsyncTimestamp(mNow);
+ EXPECT_TRUE(tracker.needsMoreSamples());
+ tracker.addVsyncTimestamp(mNow += mPeriod);
}
- EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+ EXPECT_FALSE(tracker.needsMoreSamples());
}
TEST_F(VSyncPredictorTest, reportsSamplesNeededAfterExplicitRateChange) {
for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
tracker.addVsyncTimestamp(mNow += mPeriod);
}
- EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+ EXPECT_FALSE(tracker.needsMoreSamples());
auto const changedPeriod = mPeriod * 2;
tracker.setPeriod(changedPeriod);
- EXPECT_TRUE(tracker.needsMoreSamples(mNow));
+ EXPECT_TRUE(tracker.needsMoreSamples());
for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
- EXPECT_TRUE(tracker.needsMoreSamples(mNow += changedPeriod));
- tracker.addVsyncTimestamp(mNow);
+ EXPECT_TRUE(tracker.needsMoreSamples());
+ tracker.addVsyncTimestamp(mNow += changedPeriod);
}
- EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+ EXPECT_FALSE(tracker.needsMoreSamples());
}
TEST_F(VSyncPredictorTest, transitionsToModelledPointsAfterSynthetic) {
@@ -320,20 +320,6 @@
EXPECT_THAT(intercept, Eq(0));
}
-TEST_F(VSyncPredictorTest, willBecomeInaccurateAfterA_longTimeWithNoSamples) {
- auto const simulatedVsyncs = generateVsyncTimestamps(kMinimumSamplesForPrediction, mPeriod, 0);
-
- for (auto const& timestamp : simulatedVsyncs) {
- tracker.addVsyncTimestamp(timestamp);
- }
- auto const mNow = *simulatedVsyncs.rbegin();
- EXPECT_FALSE(tracker.needsMoreSamples(mNow));
-
- // TODO: would be better to decay this as a result of the variance of the samples
- static auto constexpr aLongTimeOut = 1000000000;
- EXPECT_TRUE(tracker.needsMoreSamples(mNow + aLongTimeOut));
-}
-
TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
auto const simulatedVsyncs =
generateVsyncTimestamps(kMinimumSamplesForPrediction + 1, mPeriod, 0);
@@ -443,7 +429,7 @@
// When VsyncPredictor returns the period it means that it doesn't know how to predict and
// it needs to get more samples
if (slope == mPeriod && intercept == 0) {
- EXPECT_TRUE(tracker.needsMoreSamples(now));
+ EXPECT_TRUE(tracker.needsMoreSamples());
}
}
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index a972562..6856612 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -41,6 +41,7 @@
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD1(dump, void(std::string&));
};
@@ -57,6 +58,7 @@
nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
void resetModel() final { mTracker->resetModel(); }
+ bool needsMoreSamples() const final { return mTracker->needsMoreSamples(); }
void dump(std::string& result) const final { mTracker->dump(result); }
private:
@@ -455,6 +457,83 @@
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
+TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTracker) {
+ auto time = 0;
+ bool periodFlushed = false;
+ nsecs_t const newPeriod = 4000;
+ mReactor.setPeriod(newPeriod);
+
+ static auto constexpr numSamplesWithNewPeriod = 4;
+ Sequence seq;
+ EXPECT_CALL(*mMockTracker, needsMoreSamples())
+ .Times(numSamplesWithNewPeriod - 2)
+ .InSequence(seq)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mMockTracker, needsMoreSamples())
+ .Times(1)
+ .InSequence(seq)
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(numSamplesWithNewPeriod);
+
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ // confirmed period, but predictor wants numRequest samples. This one and prior are valid.
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+}
+
+TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) {
+ auto time = 0;
+ bool periodFlushed = false;
+ nsecs_t const newPeriod = 4000;
+ mReactor.setPeriod(newPeriod);
+
+ Sequence seq;
+ EXPECT_CALL(*mMockTracker, needsMoreSamples())
+ .Times(1)
+ .InSequence(seq)
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+}
+
+TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) {
+ auto time = 0;
+ bool periodFlushed = false;
+ nsecs_t const newPeriod1 = 4000;
+ nsecs_t const newPeriod2 = 7000;
+
+ mReactor.setPeriod(newPeriod1);
+
+ Sequence seq;
+ EXPECT_CALL(*mMockTracker, needsMoreSamples())
+ .Times(4)
+ .InSequence(seq)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mMockTracker, needsMoreSamples())
+ .Times(1)
+ .InSequence(seq)
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(7);
+
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ // confirmed period, but predictor wants numRequest samples. This one and prior are valid.
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
+
+ mReactor.setPeriod(newPeriod2);
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
+}
+
static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
return period - phase;
}
@@ -648,7 +727,7 @@
TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
bool periodFlushed = true;
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3);
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
@@ -672,7 +751,7 @@
kPendingLimit, true /* supportKernelIdleTimer */);
bool periodFlushed = true;
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(5);
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4);
idleReactor.setIgnorePresentFences(true);
// First, set the same period, which should only be confirmed when we receive two
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index afb3004..7097e7a 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -15,7 +15,6 @@
sharedLibraries = [
"libbase",
"libcutils",
- "libgtest_prod",
"libgui",
"liblog",
"libpdx_default_transport",
@@ -48,6 +47,7 @@
cc_binary {
srcs: ["bufferhubd.cpp"],
+ system_ext_specific: true,
cflags: [
"-DLOG_TAG=\"bufferhubd\"",
"-DTRACE=0",
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 4df7b7c..3728731 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -1,6 +1,8 @@
cc_library_shared {
name: "libvr_hwc-hal",
+ system_ext_specific: true,
+
srcs: [
"impl/vr_hwc.cpp",
"impl/vr_composer_client.cpp",
@@ -97,6 +99,7 @@
cc_binary {
name: "vr_hwc",
+ system_ext_specific: true,
vintf_fragments: ["manifest_vr_hwc.xml"],
srcs: [
"vr_hardware_composer_service.cpp",
diff --git a/services/vr/performanced/Android.bp b/services/vr/performanced/Android.bp
index 20301f6..0ef8cc4 100644
--- a/services/vr/performanced/Android.bp
+++ b/services/vr/performanced/Android.bp
@@ -30,6 +30,7 @@
cc_binary {
name: "performanced",
+ system_ext_specific: true,
defaults: ["performanced_defaults"],
srcs: [
"cpu_set.cpp",
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index f69de1f..1d29bab 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -21,7 +21,7 @@
}
llndk_library {
- name: "libvulkan",
+ name: "libvulkan.llndk",
symbol_file: "libvulkan.map.txt",
export_llndk_headers: [
"vulkan_headers_llndk",
@@ -30,6 +30,7 @@
cc_library_shared {
name: "libvulkan",
+ llndk_stubs: "libvulkan.llndk",
clang: true,
sanitize: {
misc_undefined: ["integer"],
@@ -89,7 +90,6 @@
"libhardware",
"libsync",
"libbase",
- "libdl_android",
"libhidlbase",
"liblog",
"libui",
@@ -100,6 +100,7 @@
"libnativebridge_lazy",
"libnativeloader_lazy",
"libnativewindow",
+ "libvndksupport",
"android.hardware.graphics.common@1.0",
"libSurfaceFlingerProp",
],
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index d9a9427..26052fb 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -226,6 +226,7 @@
INIT_PROC(true, dev, CreateQueryPool);
INIT_PROC(true, dev, DestroyQueryPool);
INIT_PROC(true, dev, GetQueryPoolResults);
+ INIT_PROC(false, dev, ResetQueryPool);
INIT_PROC(true, dev, CreateBuffer);
INIT_PROC(true, dev, DestroyBuffer);
INIT_PROC(true, dev, CreateBufferView);
@@ -337,8 +338,20 @@
INIT_PROC(false, dev, DestroySamplerYcbcrConversion);
INIT_PROC(false, dev, GetDeviceQueue2);
INIT_PROC(false, dev, GetDescriptorSetLayoutSupport);
+ INIT_PROC(false, dev, CreateRenderPass2);
+ INIT_PROC(false, dev, CmdBeginRenderPass2);
+ INIT_PROC(false, dev, CmdNextSubpass2);
+ INIT_PROC(false, dev, CmdEndRenderPass2);
+ INIT_PROC(false, dev, GetSemaphoreCounterValue);
+ INIT_PROC(false, dev, WaitSemaphores);
+ INIT_PROC(false, dev, SignalSemaphore);
INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetAndroidHardwareBufferPropertiesANDROID);
INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetMemoryAndroidHardwareBufferANDROID);
+ INIT_PROC(false, dev, CmdDrawIndirectCount);
+ INIT_PROC(false, dev, CmdDrawIndexedIndirectCount);
+ INIT_PROC(false, dev, GetBufferOpaqueCaptureAddress);
+ INIT_PROC(false, dev, GetBufferDeviceAddress);
+ INIT_PROC(false, dev, GetDeviceMemoryOpaqueCaptureAddress);
// clang-format on
return success;
@@ -391,6 +404,7 @@
VKAPI_ATTR VkResult CreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool);
VKAPI_ATTR void DestroyQueryPool(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult GetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags);
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
VKAPI_ATTR void DestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult CreateBufferView(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView);
@@ -520,8 +534,20 @@
VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo);
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue);
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout);
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo);
VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties);
VKAPI_ATTR VkResult GetMemoryAndroidHardwareBufferANDROID(VkDevice device, const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, struct AHardwareBuffer** pBuffer);
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) {
return GetData(instance).dispatch.EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
@@ -551,6 +577,7 @@
"vkEnumerateInstanceVersion",
"vkEnumeratePhysicalDeviceGroups",
"vkEnumeratePhysicalDeviceGroupsKHR",
+ "vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR",
"vkEnumeratePhysicalDevices",
"vkGetDisplayModeProperties2KHR",
"vkGetDisplayPlaneCapabilities2KHR",
@@ -571,7 +598,7 @@
"vkGetPhysicalDeviceFormatProperties",
"vkGetPhysicalDeviceFormatProperties2",
"vkGetPhysicalDeviceFormatProperties2KHR",
- "vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX",
+ "vkGetPhysicalDeviceFragmentShadingRatesKHR",
"vkGetPhysicalDeviceImageFormatProperties",
"vkGetPhysicalDeviceImageFormatProperties2",
"vkGetPhysicalDeviceImageFormatProperties2KHR",
@@ -583,6 +610,7 @@
"vkGetPhysicalDeviceProperties",
"vkGetPhysicalDeviceProperties2",
"vkGetPhysicalDeviceProperties2KHR",
+ "vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR",
"vkGetPhysicalDeviceQueueFamilyProperties",
"vkGetPhysicalDeviceQueueFamilyProperties2",
"vkGetPhysicalDeviceQueueFamilyProperties2KHR",
@@ -595,6 +623,7 @@
"vkGetPhysicalDeviceSurfaceFormatsKHR",
"vkGetPhysicalDeviceSurfacePresentModesKHR",
"vkGetPhysicalDeviceSurfaceSupportKHR",
+ "vkGetPhysicalDeviceToolPropertiesEXT",
"vkSubmitDebugUtilsMessageEXT",
};
// clang-format on
@@ -646,6 +675,7 @@
{ "vkBindImageMemory2", reinterpret_cast<PFN_vkVoidFunction>(BindImageMemory2) },
{ "vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginQuery) },
{ "vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass) },
+ { "vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass2) },
{ "vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(CmdBindDescriptorSets) },
{ "vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdBindIndexBuffer) },
{ "vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(CmdBindPipeline) },
@@ -665,12 +695,16 @@
{ "vkCmdDraw", reinterpret_cast<PFN_vkVoidFunction>(CmdDraw) },
{ "vkCmdDrawIndexed", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexed) },
{ "vkCmdDrawIndexedIndirect", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexedIndirect) },
+ { "vkCmdDrawIndexedIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexedIndirectCount) },
{ "vkCmdDrawIndirect", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndirect) },
+ { "vkCmdDrawIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndirectCount) },
{ "vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdEndQuery) },
{ "vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass) },
+ { "vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass2) },
{ "vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(CmdExecuteCommands) },
{ "vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdFillBuffer) },
{ "vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(CmdNextSubpass) },
+ { "vkCmdNextSubpass2", reinterpret_cast<PFN_vkVoidFunction>(CmdNextSubpass2) },
{ "vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier) },
{ "vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(CmdPushConstants) },
{ "vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent) },
@@ -709,6 +743,7 @@
{ "vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(CreatePipelineLayout) },
{ "vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(CreateQueryPool) },
{ "vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass) },
+ { "vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass2) },
{ "vkCreateSampler", reinterpret_cast<PFN_vkVoidFunction>(CreateSampler) },
{ "vkCreateSamplerYcbcrConversion", reinterpret_cast<PFN_vkVoidFunction>(CreateSamplerYcbcrConversion) },
{ "vkCreateSemaphore", reinterpret_cast<PFN_vkVoidFunction>(CreateSemaphore) },
@@ -749,13 +784,16 @@
{ "vkFreeDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(FreeDescriptorSets) },
{ "vkFreeMemory", reinterpret_cast<PFN_vkVoidFunction>(FreeMemory) },
{ "vkGetAndroidHardwareBufferPropertiesANDROID", reinterpret_cast<PFN_vkVoidFunction>(GetAndroidHardwareBufferPropertiesANDROID) },
+ { "vkGetBufferDeviceAddress", reinterpret_cast<PFN_vkVoidFunction>(GetBufferDeviceAddress) },
{ "vkGetBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetBufferMemoryRequirements) },
{ "vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(GetBufferMemoryRequirements2) },
+ { "vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetBufferOpaqueCaptureAddress) },
{ "vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(GetDescriptorSetLayoutSupport) },
{ "vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPeerMemoryFeatures) },
{ "vkGetDeviceGroupPresentCapabilitiesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPresentCapabilitiesKHR) },
{ "vkGetDeviceGroupSurfacePresentModesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupSurfacePresentModesKHR) },
{ "vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryCommitment) },
+ { "vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryOpaqueCaptureAddress) },
{ "vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr) },
{ "vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue) },
{ "vkGetDeviceQueue2", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue2) },
@@ -771,6 +809,7 @@
{ "vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(GetPipelineCacheData) },
{ "vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(GetQueryPoolResults) },
{ "vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(GetRenderAreaGranularity) },
+ { "vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(GetSemaphoreCounterValue) },
{ "vkGetSwapchainImagesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR) },
{ "vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(InvalidateMappedMemoryRanges) },
{ "vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(MapMemory) },
@@ -784,12 +823,15 @@
{ "vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(ResetDescriptorPool) },
{ "vkResetEvent", reinterpret_cast<PFN_vkVoidFunction>(ResetEvent) },
{ "vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(ResetFences) },
+ { "vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(ResetQueryPool) },
{ "vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(SetEvent) },
+ { "vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(SignalSemaphore) },
{ "vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(TrimCommandPool) },
{ "vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(UnmapMemory) },
{ "vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSetWithTemplate) },
{ "vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSets) },
{ "vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(WaitForFences) },
+ { "vkWaitSemaphores", reinterpret_cast<PFN_vkVoidFunction>(WaitSemaphores) },
};
// clang-format on
constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
@@ -965,6 +1007,10 @@
return GetData(device).dispatch.GetQueryPoolResults(device, queryPool, firstQuery, queryCount, dataSize, pData, stride, flags);
}
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+ GetData(device).dispatch.ResetQueryPool(device, queryPool, firstQuery, queryCount);
+}
+
VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
return GetData(device).dispatch.CreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
}
@@ -1481,6 +1527,34 @@
GetData(device).dispatch.GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport);
}
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
+ return GetData(device).dispatch.CreateRenderPass2(device, pCreateInfo, pAllocator, pRenderPass);
+}
+
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+ GetData(commandBuffer).dispatch.CmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
+}
+
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+ GetData(commandBuffer).dispatch.CmdNextSubpass2(commandBuffer, pSubpassBeginInfo, pSubpassEndInfo);
+}
+
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+ GetData(commandBuffer).dispatch.CmdEndRenderPass2(commandBuffer, pSubpassEndInfo);
+}
+
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+ return GetData(device).dispatch.GetSemaphoreCounterValue(device, semaphore, pValue);
+}
+
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+ return GetData(device).dispatch.WaitSemaphores(device, pWaitInfo, timeout);
+}
+
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+ return GetData(device).dispatch.SignalSemaphore(device, pSignalInfo);
+}
+
VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
return GetData(device).dispatch.GetAndroidHardwareBufferPropertiesANDROID(device, buffer, pProperties);
}
@@ -1489,6 +1563,26 @@
return GetData(device).dispatch.GetMemoryAndroidHardwareBufferANDROID(device, pInfo, pBuffer);
}
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ GetData(commandBuffer).dispatch.CmdDrawIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ GetData(commandBuffer).dispatch.CmdDrawIndexedIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return GetData(device).dispatch.GetBufferOpaqueCaptureAddress(device, pInfo);
+}
+
+VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return GetData(device).dispatch.GetBufferDeviceAddress(device, pInfo);
+}
+
+VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+ return GetData(device).dispatch.GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
+}
+
} // anonymous namespace
@@ -1755,6 +1849,11 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR void vkResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+ vulkan::api::ResetQueryPool(device, queryPool, firstQuery, queryCount);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR VkResult vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
return vulkan::api::CreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
}
@@ -2400,6 +2499,41 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
+ return vulkan::api::CreateRenderPass2(device, pCreateInfo, pAllocator, pRenderPass);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+ vulkan::api::CmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+ vulkan::api::CmdNextSubpass2(commandBuffer, pSubpassBeginInfo, pSubpassEndInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+ vulkan::api::CmdEndRenderPass2(commandBuffer, pSubpassEndInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+ return vulkan::api::GetSemaphoreCounterValue(device, semaphore, pValue);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkWaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+ return vulkan::api::WaitSemaphores(device, pWaitInfo, timeout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkSignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+ return vulkan::api::SignalSemaphore(device, pSignalInfo);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR VkResult vkGetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
return vulkan::api::GetAndroidHardwareBufferPropertiesANDROID(device, buffer, pProperties);
}
@@ -2409,4 +2543,29 @@
return vulkan::api::GetMemoryAndroidHardwareBufferANDROID(device, pInfo, pBuffer);
}
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ vulkan::api::CmdDrawIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ vulkan::api::CmdDrawIndexedIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR uint64_t vkGetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return vulkan::api::GetBufferOpaqueCaptureAddress(device, pInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkDeviceAddress vkGetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return vulkan::api::GetBufferDeviceAddress(device, pInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR uint64_t vkGetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+ return vulkan::api::GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
+}
+
// clang-format on
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 2195845..ad5cc34 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -99,6 +99,7 @@
PFN_vkCreateQueryPool CreateQueryPool;
PFN_vkDestroyQueryPool DestroyQueryPool;
PFN_vkGetQueryPoolResults GetQueryPoolResults;
+ PFN_vkResetQueryPool ResetQueryPool;
PFN_vkCreateBuffer CreateBuffer;
PFN_vkDestroyBuffer DestroyBuffer;
PFN_vkCreateBufferView CreateBufferView;
@@ -210,8 +211,20 @@
PFN_vkDestroySamplerYcbcrConversion DestroySamplerYcbcrConversion;
PFN_vkGetDeviceQueue2 GetDeviceQueue2;
PFN_vkGetDescriptorSetLayoutSupport GetDescriptorSetLayoutSupport;
+ PFN_vkCreateRenderPass2 CreateRenderPass2;
+ PFN_vkCmdBeginRenderPass2 CmdBeginRenderPass2;
+ PFN_vkCmdNextSubpass2 CmdNextSubpass2;
+ PFN_vkCmdEndRenderPass2 CmdEndRenderPass2;
+ PFN_vkGetSemaphoreCounterValue GetSemaphoreCounterValue;
+ PFN_vkWaitSemaphores WaitSemaphores;
+ PFN_vkSignalSemaphore SignalSemaphore;
PFN_vkGetAndroidHardwareBufferPropertiesANDROID GetAndroidHardwareBufferPropertiesANDROID;
PFN_vkGetMemoryAndroidHardwareBufferANDROID GetMemoryAndroidHardwareBufferANDROID;
+ PFN_vkCmdDrawIndirectCount CmdDrawIndirectCount;
+ PFN_vkCmdDrawIndexedIndirectCount CmdDrawIndexedIndirectCount;
+ PFN_vkGetBufferOpaqueCaptureAddress GetBufferOpaqueCaptureAddress;
+ PFN_vkGetBufferDeviceAddress GetBufferDeviceAddress;
+ PFN_vkGetDeviceMemoryOpaqueCaptureAddress GetDeviceMemoryOpaqueCaptureAddress;
// clang-format on
};
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 01cbf39..55d80fb 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -28,20 +28,17 @@
#include <android/dlext.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
-#include <cutils/properties.h>
#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
-#include <nativeloader/dlext_namespaces.h>
#include <sys/prctl.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
+#include <vndksupport/linker.h>
#include <algorithm>
#include <array>
#include <climits>
#include <new>
-#include <string_view>
-#include <sstream>
#include <vector>
#include "stubhal.h"
@@ -151,19 +148,11 @@
Hal Hal::hal_;
-void* LoadLibrary(const android_dlextinfo& dlextinfo,
- const std::string_view subname) {
- ATRACE_CALL();
-
- std::stringstream ss;
- ss << "vulkan." << subname << ".so";
- return android_dlopen_ext(ss.str().c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
-}
-
const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
"ro.hardware.vulkan",
"ro.board.platform",
}};
+constexpr int LIB_DL_FLAGS = RTLD_LOCAL | RTLD_NOW;
// LoadDriver returns:
// * 0 when succeed, or
@@ -174,20 +163,26 @@
const hwvulkan_module_t** module) {
ATRACE_CALL();
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE,
- .library_namespace = library_namespace,
- };
void* so = nullptr;
- char prop[PROPERTY_VALUE_MAX];
for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
- int prop_len = property_get(key, prop, nullptr);
- if (prop_len > 0 && prop_len <= UINT_MAX) {
- std::string_view lib_name(prop, static_cast<unsigned int>(prop_len));
- so = LoadLibrary(dlextinfo, lib_name);
- if (so)
- break;
+ std::string lib_name = android::base::GetProperty(key, "");
+ if (lib_name.empty())
+ continue;
+
+ lib_name = "vulkan." + lib_name + ".so";
+ if (library_namespace) {
+ // load updated driver
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = library_namespace,
+ };
+ so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo);
+ } else {
+ // load built-in driver
+ so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS);
}
+ if (so)
+ break;
}
if (!so)
return -ENOENT;
@@ -211,12 +206,9 @@
int LoadBuiltinDriver(const hwvulkan_module_t** module) {
ATRACE_CALL();
- auto ns = android_get_exported_namespace("sphal");
- if (!ns)
- return -ENOENT;
android::GraphicsEnv::getInstance().setDriverToLoad(
android::GpuStatsInfo::Driver::VULKAN);
- return LoadDriver(ns, module);
+ return LoadDriver(nullptr, module);
}
int LoadUpdatedDriver(const hwvulkan_module_t** module) {
@@ -549,6 +541,7 @@
case ProcHook::GOOGLE_display_timing:
case ProcHook::EXTENSION_CORE_1_0:
case ProcHook::EXTENSION_CORE_1_1:
+ case ProcHook::EXTENSION_CORE_1_2:
case ProcHook::EXTENSION_COUNT:
// Device and meta extensions. If we ever get here it's a bug in
// our code. But enumerating them lets us avoid having a default
@@ -598,6 +591,7 @@
case ProcHook::ANDROID_native_buffer:
case ProcHook::EXTENSION_CORE_1_0:
case ProcHook::EXTENSION_CORE_1_1:
+ case ProcHook::EXTENSION_CORE_1_2:
case ProcHook::EXTENSION_COUNT:
// Instance and meta extensions. If we ever get here it's a bug
// in our code. But enumerating them lets us avoid having a
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 43c4d14..62538c1 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -51,6 +51,7 @@
EXTENSION_CORE_1_0,
EXTENSION_CORE_1_1,
+ EXTENSION_CORE_1_2,
EXTENSION_COUNT,
EXTENSION_UNKNOWN,
};
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index 0be66c9..df97d7f 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -12,6 +12,7 @@
vkBindImageMemory2; # introduced=28
vkCmdBeginQuery;
vkCmdBeginRenderPass;
+ vkCmdBeginRenderPass2; # introduced=31
vkCmdBindDescriptorSets;
vkCmdBindIndexBuffer;
vkCmdBindPipeline;
@@ -31,12 +32,16 @@
vkCmdDraw;
vkCmdDrawIndexed;
vkCmdDrawIndexedIndirect;
+ vkCmdDrawIndexedIndirectCount; # introduced=31
vkCmdDrawIndirect;
+ vkCmdDrawIndirectCount; # introduced=31
vkCmdEndQuery;
vkCmdEndRenderPass;
+ vkCmdEndRenderPass2; # introduced=31
vkCmdExecuteCommands;
vkCmdFillBuffer;
vkCmdNextSubpass;
+ vkCmdNextSubpass2; # introduced=31
vkCmdPipelineBarrier;
vkCmdPushConstants;
vkCmdResetEvent;
@@ -76,6 +81,7 @@
vkCreatePipelineLayout;
vkCreateQueryPool;
vkCreateRenderPass;
+ vkCreateRenderPass2; # introduced=31
vkCreateSampler;
vkCreateSamplerYcbcrConversion; # introduced=28
vkCreateSemaphore;
@@ -119,13 +125,16 @@
vkFreeDescriptorSets;
vkFreeMemory;
vkGetAndroidHardwareBufferPropertiesANDROID; # introduced=28
+ vkGetBufferDeviceAddress; # introduced=31
vkGetBufferMemoryRequirements;
vkGetBufferMemoryRequirements2; # introduced=28
+ vkGetBufferOpaqueCaptureAddress; # introduced=31
vkGetDescriptorSetLayoutSupport; # introduced=28
vkGetDeviceGroupPeerMemoryFeatures; # introduced=28
vkGetDeviceGroupPresentCapabilitiesKHR; # introduced=28
vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28
vkGetDeviceMemoryCommitment;
+ vkGetDeviceMemoryOpaqueCaptureAddress; # introduced=31
vkGetDeviceProcAddr;
vkGetDeviceQueue;
vkGetDeviceQueue2; # introduced=28
@@ -163,6 +172,7 @@
vkGetPipelineCacheData;
vkGetQueryPoolResults;
vkGetRenderAreaGranularity;
+ vkGetSemaphoreCounterValue; # introduced=31
vkGetSwapchainImagesKHR;
vkInvalidateMappedMemoryRanges;
vkMapMemory;
@@ -176,12 +186,15 @@
vkResetDescriptorPool;
vkResetEvent;
vkResetFences;
+ vkResetQueryPool; # introduced=31
vkSetEvent;
+ vkSignalSemaphore; # introduced=31
vkTrimCommandPool; # introduced=28
vkUnmapMemory;
vkUpdateDescriptorSets;
vkUpdateDescriptorSetWithTemplate; # introduced=28
vkWaitForFences;
+ vkWaitSemaphores; # introduced=31
local:
*;
};
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 4647a80..b94233b 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -1123,6 +1123,14 @@
return VK_SUCCESS;
}
+VkResult CreateRenderPass2(VkDevice device,
+ const VkRenderPassCreateInfo2*,
+ const VkAllocationCallbacks* /*allocator*/,
+ VkRenderPass* pRenderPass) {
+ *pRenderPass = AllocHandle<VkRenderPass>(device, HandleType::kRenderPass);
+ return VK_SUCCESS;
+}
+
// -----------------------------------------------------------------------------
// No-op entrypoints
@@ -1568,6 +1576,55 @@
void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) {
}
+void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+}
+
+void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+}
+
+void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+}
+
+void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+}
+
+VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return VK_SUCCESS;
+}
+
+VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return VK_SUCCESS;
+}
+
+VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return VK_SUCCESS;
+}
+
+void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+}
+
+void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+}
+
+uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return 0;
+}
+
+VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return (VkDeviceAddress)0;
+}
+
+uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return 0;
+}
+
#pragma clang diagnostic pop
// clang-format on
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index b8d7d2b..edda12c 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -67,6 +67,7 @@
{"vkBindImageMemory2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkBindImageMemory2>(BindImageMemory2))},
{"vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginQuery>(CmdBeginQuery))},
{"vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass>(CmdBeginRenderPass))},
+ {"vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass2>(CmdBeginRenderPass2))},
{"vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindDescriptorSets>(CmdBindDescriptorSets))},
{"vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindIndexBuffer>(CmdBindIndexBuffer))},
{"vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindPipeline>(CmdBindPipeline))},
@@ -86,12 +87,16 @@
{"vkCmdDraw", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDraw>(CmdDraw))},
{"vkCmdDrawIndexed", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexed>(CmdDrawIndexed))},
{"vkCmdDrawIndexedIndirect", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexedIndirect>(CmdDrawIndexedIndirect))},
+ {"vkCmdDrawIndexedIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexedIndirectCount>(CmdDrawIndexedIndirectCount))},
{"vkCmdDrawIndirect", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndirect>(CmdDrawIndirect))},
+ {"vkCmdDrawIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndirectCount>(CmdDrawIndirectCount))},
{"vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndQuery>(CmdEndQuery))},
{"vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRenderPass>(CmdEndRenderPass))},
+ {"vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRenderPass2>(CmdEndRenderPass2))},
{"vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdExecuteCommands>(CmdExecuteCommands))},
{"vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdFillBuffer>(CmdFillBuffer))},
{"vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdNextSubpass>(CmdNextSubpass))},
+ {"vkCmdNextSubpass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdNextSubpass2>(CmdNextSubpass2))},
{"vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier>(CmdPipelineBarrier))},
{"vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushConstants>(CmdPushConstants))},
{"vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent>(CmdResetEvent))},
@@ -131,6 +136,7 @@
{"vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreatePipelineLayout>(CreatePipelineLayout))},
{"vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateQueryPool>(CreateQueryPool))},
{"vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateRenderPass>(CreateRenderPass))},
+ {"vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateRenderPass2>(CreateRenderPass2))},
{"vkCreateSampler", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSampler>(CreateSampler))},
{"vkCreateSamplerYcbcrConversion", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSamplerYcbcrConversion>(CreateSamplerYcbcrConversion))},
{"vkCreateSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSemaphore>(CreateSemaphore))},
@@ -172,11 +178,14 @@
{"vkFreeCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeCommandBuffers>(FreeCommandBuffers))},
{"vkFreeDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeDescriptorSets>(FreeDescriptorSets))},
{"vkFreeMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeMemory>(FreeMemory))},
+ {"vkGetBufferDeviceAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferDeviceAddress>(GetBufferDeviceAddress))},
{"vkGetBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferMemoryRequirements>(GetBufferMemoryRequirements))},
{"vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferMemoryRequirements2>(GetBufferMemoryRequirements2))},
+ {"vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferOpaqueCaptureAddress>(GetBufferOpaqueCaptureAddress))},
{"vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDescriptorSetLayoutSupport>(GetDescriptorSetLayoutSupport))},
{"vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceGroupPeerMemoryFeatures>(GetDeviceGroupPeerMemoryFeatures))},
{"vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryCommitment>(GetDeviceMemoryCommitment))},
+ {"vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryOpaqueCaptureAddress>(GetDeviceMemoryOpaqueCaptureAddress))},
{"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr))},
{"vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceQueue>(GetDeviceQueue))},
{"vkGetDeviceQueue2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceQueue2>(GetDeviceQueue2))},
@@ -215,6 +224,7 @@
{"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPipelineCacheData>(GetPipelineCacheData))},
{"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))},
{"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
+ {"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
{"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
{"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
{"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
@@ -229,12 +239,15 @@
{"vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetDescriptorPool>(ResetDescriptorPool))},
{"vkResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetEvent>(ResetEvent))},
{"vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetFences>(ResetFences))},
+ {"vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetQueryPool>(ResetQueryPool))},
{"vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetEvent>(SetEvent))},
+ {"vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSignalSemaphore>(SignalSemaphore))},
{"vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkTrimCommandPool>(TrimCommandPool))},
{"vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUnmapMemory>(UnmapMemory))},
{"vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSetWithTemplate>(UpdateDescriptorSetWithTemplate))},
{"vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSets>(UpdateDescriptorSets))},
{"vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkWaitForFences>(WaitForFences))},
+ {"vkWaitSemaphores", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkWaitSemaphores>(WaitSemaphores))},
// clang-format on
};
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 0d3f688..e59cae9 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -79,6 +79,7 @@
VKAPI_ATTR VkResult CreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool);
VKAPI_ATTR void DestroyQueryPool(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult GetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags);
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
VKAPI_ATTR void DestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult CreateBufferView(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView);
@@ -207,6 +208,18 @@
VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo);
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue);
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout);
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo);
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
// clang-format on
} // namespace null_driver
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index ef0719d..e9a96f3 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -25,6 +25,7 @@
_BLOCKED_EXTENSIONS = [
'VK_EXT_acquire_xlib_display',
'VK_EXT_direct_mode_display',
+ 'VK_EXT_directfb_surface',
'VK_EXT_display_control',
'VK_EXT_display_surface_counter',
'VK_EXT_full_screen_exclusive',
@@ -302,12 +303,12 @@
else:
f.write('INIT_PROC(')
- if name in version_dict and version_dict[name] == 'VK_VERSION_1_1':
+ if name in _OPTIONAL_COMMANDS:
f.write('false, ')
- elif name in _OPTIONAL_COMMANDS:
- f.write('false, ')
- else:
+ elif version_dict[name] == 'VK_VERSION_1_0':
f.write('true, ')
+ else:
+ f.write('false, ')
if is_instance_dispatched(name):
f.write('instance, ')
@@ -376,7 +377,7 @@
for exts in root.iter('extensions'):
for extension in exts:
- apiversion = ''
+ apiversion = 'VK_VERSION_1_0'
if extension.tag == 'extension':
extname = extension.get('name')
for req in extension:
@@ -387,8 +388,7 @@
cmd_name = commands.get('name')
if cmd_name not in extension_dict:
extension_dict[cmd_name] = extname
- if apiversion:
- version_dict[cmd_name] = apiversion
+ version_dict[cmd_name] = apiversion
for feature in root.iter('feature'):
apiversion = feature.get('name')
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index b0b466c..bfc240e 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -57,9 +57,17 @@
template <typename T> struct EnumTraits;
template <> struct EnumTraits<VkPhysicalDeviceType> {
- static uint32_t min() { return VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE; }
- static uint32_t max() { return VK_PHYSICAL_DEVICE_TYPE_END_RANGE; }
- static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+ static bool exist(uint32_t e) {
+ switch (e) {
+ case VK_PHYSICAL_DEVICE_TYPE_OTHER:
+ case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+ case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+ case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+ case VK_PHYSICAL_DEVICE_TYPE_CPU:
+ return true;
+ }
+ return false;
+ }
};
template <> struct EnumTraits<VkFormat> {
@@ -250,6 +258,40 @@
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+ case VK_FORMAT_G8B8G8R8_422_UNORM:
+ case VK_FORMAT_B8G8R8G8_422_UNORM:
+ case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
+ case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
+ case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
+ case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
+ case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
+ case VK_FORMAT_R10X6_UNORM_PACK16:
+ case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
+ case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
+ case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
+ case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
+ case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
+ case VK_FORMAT_R12X4_UNORM_PACK16:
+ case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
+ case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
+ case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
+ case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
+ case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
+ case VK_FORMAT_G16B16G16R16_422_UNORM:
+ case VK_FORMAT_B16G16R16G16_422_UNORM:
+ case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
+ case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
+ case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
+ case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
+ case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
@@ -258,40 +300,22 @@
case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
- case VK_FORMAT_G8B8G8R8_422_UNORM_KHR:
- case VK_FORMAT_B8G8R8G8_422_UNORM_KHR:
- case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR:
- case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR:
- case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR:
- case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR:
- case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR:
- case VK_FORMAT_R10X6_UNORM_PACK16_KHR:
- case VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR:
- case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR:
- case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR:
- case VK_FORMAT_R12X4_UNORM_PACK16_KHR:
- case VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR:
- case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR:
- case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR:
- case VK_FORMAT_G16B16G16R16_422_UNORM_KHR:
- case VK_FORMAT_B16G16R16G16_422_UNORM_KHR:
- case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR:
- case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR:
- case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR:
- case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR:
- case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR:
+ case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT:
+ case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT:
return true;
}
return false;
@@ -300,9 +324,14 @@
template <>
struct EnumTraits<VkPointClippingBehavior> {
- static uint32_t min() { return VK_POINT_CLIPPING_BEHAVIOR_BEGIN_RANGE; }
- static uint32_t max() { return VK_POINT_CLIPPING_BEHAVIOR_END_RANGE; }
- static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+ static bool exist(uint32_t e) {
+ switch (e) {
+ case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES:
+ case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY:
+ return true;
+ }
+ return false;
+ }
};
template <>
@@ -336,9 +365,26 @@
template <>
struct EnumTraits<VkDriverIdKHR> {
- static uint32_t min() { return VK_DRIVER_ID_BEGIN_RANGE_KHR; }
- static uint32_t max() { return VK_DRIVER_ID_END_RANGE_KHR; }
- static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+ static bool exist(uint32_t e) {
+ switch (e) {
+ case VK_DRIVER_ID_AMD_PROPRIETARY:
+ case VK_DRIVER_ID_AMD_OPEN_SOURCE:
+ case VK_DRIVER_ID_MESA_RADV:
+ case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
+ case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
+ case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
+ case VK_DRIVER_ID_IMAGINATION_PROPRIETARY:
+ case VK_DRIVER_ID_QUALCOMM_PROPRIETARY:
+ case VK_DRIVER_ID_ARM_PROPRIETARY:
+ case VK_DRIVER_ID_GOOGLE_SWIFTSHADER:
+ case VK_DRIVER_ID_GGP_PROPRIETARY:
+ case VK_DRIVER_ID_BROADCOM_PROPRIETARY:
+ case VK_DRIVER_ID_MESA_LLVMPIPE:
+ case VK_DRIVER_ID_MOLTENVK:
+ return true;
+ }
+ return false;
+ }
};
// VkSparseImageFormatProperties
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 84cfe5e..73586d4 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -165,7 +165,9 @@
VkFormatProperties format_properties = {};
for (VkFormat format = VK_FORMAT_R4G4_UNORM_PACK8;
- format <= VK_FORMAT_END_RANGE;
+ // TODO(http://b/171403054): avoid hard-coding last value in the
+ // contiguous range
+ format <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK;
format = static_cast<VkFormat>(format + 1)) {
vkGetPhysicalDeviceFormatProperties(physical_device, format,
&format_properties);
@@ -178,6 +180,8 @@
if (device.properties.apiVersion >= VK_API_VERSION_1_1) {
for (VkFormat format = VK_FORMAT_G8B8G8R8_422_UNORM;
+ // TODO(http://b/171403054): avoid hard-coding last value in the
+ // contiguous range
format <= VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM;
format = static_cast<VkFormat>(format + 1)) {
vkGetPhysicalDeviceFormatProperties(physical_device, format,