Merge "Add systrace tag for thermal"
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
index c2c8a72..4d3a67b 100644
--- a/cmds/dumpstate/DumpPool.cpp
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -33,6 +33,20 @@
const std::string DumpPool::PREFIX_TMPFILE_NAME = "dump-tmp.";
+
+void WaitForTask(std::future<std::string> future, const std::string& title, int out_fd) {
+ DurationReporter duration_reporter("Wait for " + title, true);
+
+ 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));
+ }
+}
+
DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false),
log_duration_(true) {
assert(!tmp_root.empty());
@@ -40,31 +54,10 @@
}
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;
}
- futures_map_.clear();
while (!tasks_.empty()) tasks_.pop();
shutdown_ = true;
@@ -76,27 +69,22 @@
}
threads_.clear();
deleteTempFiles(tmp_root_);
- MYLOGI("shutdown thread pool");
+ MYLOGI("shutdown thread pool\n");
}
-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;
+void DumpPool::start(int thread_counts) {
+ assert(thread_counts > 0);
+ assert(threads_.empty());
+ if (thread_counts > MAX_THREAD_COUNT) {
+ thread_counts = MAX_THREAD_COUNT;
}
- 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));
+ MYLOGI("Start thread pool:%d\n", thread_counts);
+ shutdown_ = false;
+ for (int i = 0; i < thread_counts; i++) {
+ threads_.emplace_back(std::thread([=]() {
+ setThreadName(pthread_self(), i + 1);
+ loop();
+ }));
}
}
diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h
index 0c3c2cc..9fb0fcc 100644
--- a/cmds/dumpstate/DumpPool.h
+++ b/cmds/dumpstate/DumpPool.h
@@ -18,7 +18,6 @@
#define FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
#include <future>
-#include <map>
#include <queue>
#include <string>
@@ -32,8 +31,26 @@
class DumpPoolTest;
/*
+ * Waits until the task is finished. Dumps the task results to the specified
+ * out_fd.
+ *
+ * |future| The task future.
+ * |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(std::future<std::string> future, const std::string& title, int out_fd);
+
+/*
+ * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
+ */
+
+inline void WaitForTask(std::future<std::string> future) {
+ WaitForTask(std::move(future), "", STDOUT_FILENO);
+}
+
+/*
* 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
+ * simultaneously for 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.
@@ -44,13 +61,16 @@
* }
* ...
* DumpPool pool(tmp_root);
- * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
+ * auto task = pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
* ...
- * pool.waitForTask("TaskName");
+ * WaitForTask(task);
*
* 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.
+ *
+ * std::futures returned by `enqueueTask*()` must all have their `get` methods
+ * called, or have been destroyed before the DumpPool itself is destroyed.
*/
class DumpPool {
friend class android::os::dumpstate::DumpPoolTest;
@@ -63,6 +83,12 @@
* files.
*/
explicit DumpPool(const std::string& tmp_root);
+
+ /*
+ * Will waits until all threads exit the loop. Destroying DumpPool before destroying the
+ * associated std::futures created by `enqueueTask*` will cause an abort on Android because
+ * Android is built with `-fno-exceptions`.
+ */
~DumpPool();
/*
@@ -73,68 +99,47 @@
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
+ * |duration_title| 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) {
+ template<class F, class... Args>
+ std::future<std::string> enqueueTask(const std::string& duration_title, 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);
+ auto future = post(duration_title, func);
if (threads_.empty()) {
start();
}
+ return future;
}
/*
* 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.
+ * |duration_title| 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) {
+ template<class F, class... Args> std::future<std::string> enqueueTaskWithFd(
+ const std::string& duration_title, 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);
+ auto future = post(duration_title, func);
if (threads_.empty()) {
start();
}
+ return future;
}
/*
- * 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();
@@ -143,22 +148,22 @@
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) {
+ template<class T>
+ std::future<std::string> post(const std::string& duration_title, 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());
+ invokeTask(dump_func, duration_title, 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();
+ auto future = packaged_task.get_future();
tasks_.push(std::move(packaged_task));
condition_variable_.notify_one();
return future;
@@ -194,7 +199,6 @@
std::vector<std::thread> threads_;
std::queue<Task> tasks_;
- std::map<std::string, Future> futures_map_;
DISALLOW_COPY_AND_ASSIGN(DumpPool);
};
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index c833d0e..aa42541 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -48,11 +48,26 @@
sigemptyset(&child_mask);
sigaddset(&child_mask, SIGCHLD);
+ // block SIGCHLD before we check if a process has exited
if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
printf("*** sigprocmask failed: %s\n", strerror(errno));
return false;
}
+ // if the child has exited already, handle and reset signals before leaving
+ pid_t child_pid = waitpid(pid, status, WNOHANG);
+ if (child_pid != pid) {
+ if (child_pid > 0) {
+ printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+ sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+ return false;
+ }
+ } else {
+ sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+ return true;
+ }
+
+ // wait for a SIGCHLD
timespec ts;
ts.tv_sec = MSEC_TO_SEC(timeout_ms);
ts.tv_nsec = (timeout_ms % 1000) * 1000000;
@@ -76,7 +91,7 @@
return false;
}
- pid_t child_pid = waitpid(pid, status, WNOHANG);
+ child_pid = waitpid(pid, status, WNOHANG);
if (child_pid != pid) {
if (child_pid != -1) {
printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
@@ -232,7 +247,6 @@
dprintf(out_fd, "*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(),
strerror(err));
}
- fsync(out_fd);
return -1;
}
return DumpFileFromFdToFd(title, path, fd.get(), out_fd, PropertiesHelper::IsDryRun());
@@ -280,7 +294,6 @@
if (!title.empty()) {
dprintf(fd, "------ %s (%s) ------\n", title.c_str(), command);
- fsync(fd);
}
const std::string& logging_message = options.LoggingMessage();
@@ -299,14 +312,13 @@
// There is no title, but we should still print a dry-run message
dprintf(fd, "%s: skipped on dry run\n", command_string.c_str());
}
- fsync(fd);
return 0;
}
const char* path = args[0];
uint64_t start = Nanotime();
- pid_t pid = fork();
+ pid_t pid = vfork();
/* handle error case */
if (pid < 0) {
@@ -323,7 +335,7 @@
strerror(errno));
}
MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
- return -1;
+ _exit(EXIT_FAILURE);
}
if (options.ShouldCloseAllFileDescriptorsOnExec()) {
@@ -376,7 +388,6 @@
/* handle parent case */
int status;
bool ret = waitpid_with_timeout(pid, options.TimeoutInMs(), &status);
- fsync(fd);
uint64_t elapsed = Nanotime() - start;
if (!ret) {
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8ccd940..0e9ce89 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -120,6 +120,7 @@
using android::os::dumpstate::DumpPool;
using android::os::dumpstate::PropertiesHelper;
using android::os::dumpstate::TaskQueue;
+using android::os::dumpstate::WaitForTask;
// Keep in sync with
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -218,9 +219,9 @@
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) \
+#define WAIT_TASK_WITH_CONSENT_CHECK(future) \
RETURN_IF_USER_DENIED_CONSENT(); \
- pool_ptr->waitForTask(task_name); \
+ WaitForTask(future); \
RETURN_IF_USER_DENIED_CONSENT();
static const char* WAKE_LOCK_NAME = "dumpstate_wakelock";
@@ -1549,15 +1550,18 @@
DurationReporter duration_reporter("DUMPSTATE");
// Enqueue slow functions into the thread pool, if the parallel run is enabled.
+ std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins;
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_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+ dump_incident_report = ds.dump_pool_->enqueueTask(
+ DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
+ dump_board = ds.dump_pool_->enqueueTaskWithFd(
+ DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+ dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
}
// Dump various things. Note that anything that takes "long" (i.e. several seconds) should
@@ -1582,7 +1586,6 @@
DumpFile("BUDDYINFO", "/proc/buddyinfo");
DumpExternalFragmentationInfo();
- DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources");
DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
RunCommand("PROCESSES AND THREADS",
@@ -1592,7 +1595,7 @@
CommandOptions::AS_ROOT);
if (ds.dump_pool_) {
- WAIT_TASK_WITH_CONSENT_CHECK(DUMP_HALS_TASK, ds.dump_pool_);
+ WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_hals));
} else {
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_HALS_TASK, DumpHals);
}
@@ -1689,7 +1692,7 @@
ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
if (ds.dump_pool_) {
- WAIT_TASK_WITH_CONSENT_CHECK(DUMP_BOARD_TASK, ds.dump_pool_);
+ WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_board));
} else {
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
}
@@ -1718,7 +1721,7 @@
ds.AddDir("/data/misc/bluetooth/logs", true);
if (ds.dump_pool_) {
- WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
+ WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_checkins));
} else {
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins);
}
@@ -1752,7 +1755,7 @@
dump_frozen_cgroupfs();
if (ds.dump_pool_) {
- WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
+ WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_incident_report));
} else {
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_INCIDENT_REPORT_TASK,
DumpIncidentReport);
@@ -1777,6 +1780,7 @@
time_t logcat_ts = time(nullptr);
/* collect stack traces from Dalvik and native processes (needs root) */
+ std::future<std::string> dump_traces;
if (dump_pool_) {
RETURN_IF_USER_DENIED_CONSENT();
// One thread is enough since we only need to enqueue DumpTraces here.
@@ -1784,7 +1788,8 @@
// 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);
+ dump_traces = 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);
@@ -1833,12 +1838,11 @@
if (dump_pool_) {
RETURN_IF_USER_DENIED_CONSENT();
- dump_pool_->waitForTask(DUMP_TRACES_TASK);
+ WaitForTask(std::move(dump_traces));
- // 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();
+ // Current running thread in the pool is the root user also. Delete
+ // the pool and make a new one later to ensure none of threads in the pool are root.
+ dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
}
if (!DropRootUser()) {
return Dumpstate::RunStatus::ERROR;
@@ -1869,8 +1873,9 @@
} else {
// DumpHals takes long time, post it to the another thread in the pool,
// if pool is available.
+ std::future<std::string> dump_hals;
if (ds.dump_pool_) {
- ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+ dump_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
}
// Contains various system properties and process startup info.
do_dmesg();
@@ -1880,7 +1885,7 @@
DoKmsg();
// DumpHals contains unrelated hardware info (camera, NFC, biometrics, ...).
if (ds.dump_pool_) {
- ds.dump_pool_->waitForTask(DUMP_HALS_TASK);
+ WaitForTask(std::move(dump_hals));
} else {
RUN_SLOW_FUNCTION_AND_LOG(DUMP_HALS_TASK, DumpHals);
}
@@ -1914,12 +1919,14 @@
// Starts thread pool after the root user is dropped, and two additional threads
// are created for DumpHals in the DumpstateRadioCommon and DumpstateBoard.
+ std::future<std::string> dump_board;
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);
+ dump_board = ds.dump_pool_->enqueueTaskWithFd(
+ DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
}
DumpstateRadioCommon(include_sensitive_info);
@@ -2002,7 +2009,7 @@
printf("========================================================\n");
if (ds.dump_pool_) {
- ds.dump_pool_->waitForTask(DUMP_BOARD_TASK);
+ WaitForTask(std::move(dump_board));
} else {
RUN_SLOW_FUNCTION_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
}
@@ -3226,8 +3233,7 @@
void Dumpstate::ShutdownDumpPool() {
if (dump_pool_) {
- dump_pool_->shutdown();
- dump_pool_ = nullptr;
+ dump_pool_.reset();
}
if (zip_entry_tasks_) {
zip_entry_tasks_->run(/* do_cancel = */true);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 4a99cd8..ee6b1ae 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -478,7 +478,7 @@
// This is useful for debugging.
std::string log_path_;
- // Full path of the bugreport file, be it zip or text, inside bugreport_internal_dir_.
+ // Full path of the bugreport zip file inside bugreport_internal_dir_.
std::string path_;
// Full path of the file containing the screenshot (when requested).
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 42beb2b..70b4e5c 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -1720,14 +1720,13 @@
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);
+ auto t1 = dump_pool_->enqueueTaskWithFd("", dump_func_1, std::placeholders::_1);
+ auto t2 = dump_pool_->enqueueTaskWithFd("", dump_func_2, std::placeholders::_1);
+ auto t3 = dump_pool_->enqueueTaskWithFd("", 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();
+ WaitForTask(std::move(t1), "", out_fd_.get());
+ WaitForTask(std::move(t2), "", out_fd_.get());
+ WaitForTask(std::move(t3), "", out_fd_.get());
std::string result;
ReadFileToString(out_path_, &result);
@@ -1741,9 +1740,8 @@
run_1 = true;
};
- dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
- dump_pool_->waitForTask("1", "", out_fd_.get());
- dump_pool_->shutdown();
+ auto t1 = dump_pool_->enqueueTask(/* duration_title = */"1", dump_func_1);
+ WaitForTask(std::move(t1), "", out_fd_.get());
std::string result;
ReadFileToString(out_path_, &result);
@@ -1752,27 +1750,6 @@
EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
}
-TEST_F(DumpPoolTest, Shutdown_withoutCrash) {
- bool run_1 = false;
- auto dump_func_1 = [&]() {
- run_1 = true;
- };
- auto dump_func = []() {
- sleep(1);
- };
-
- dump_pool_->start(/* thread_counts = */1);
- dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
- dump_pool_->enqueueTask(/* task_name = */"2", dump_func);
- dump_pool_->enqueueTask(/* task_name = */"3", dump_func);
- dump_pool_->enqueueTask(/* task_name = */"4", dump_func);
- dump_pool_->waitForTask("1", "", out_fd_.get());
- dump_pool_->shutdown();
-
- EXPECT_TRUE(run_1);
- EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
-}
-
class TaskQueueTest : public DumpstateBaseTest {
public:
void SetUp() {
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 00babc3..c9f680b 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -51,6 +51,7 @@
],
static_libs: [
"libasync_safe",
+ "libext2_uuid",
],
export_shared_lib_headers: [
"libbinder",
@@ -262,6 +263,7 @@
"libasync_safe",
"libdiskusage",
"libotapreoptparameters",
+ "libext2_uuid",
],
shared_libs: [
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b20c908..91f7d3b 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -957,13 +957,13 @@
binder::Status res = ok();
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
- if (delete_dir_contents_and_dir(path) != 0) {
+ if (rename_delete_dir_contents_and_dir(path) != 0) {
res = error("Failed to delete " + path);
}
}
if (flags & FLAG_STORAGE_DE) {
auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
- if (delete_dir_contents_and_dir(path) != 0) {
+ if (rename_delete_dir_contents_and_dir(path) != 0) {
res = error("Failed to delete " + path);
}
if ((flags & FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) {
@@ -997,7 +997,6 @@
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete contents of " + path);
}
-
path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete contents of " + path);
@@ -3177,5 +3176,22 @@
return ok();
}
+binder::Status InstalldNativeService::cleanupInvalidPackageDirs(
+ const std::optional<std::string>& uuid, int32_t userId, int32_t flags) {
+ const char* uuid_cstr = uuid ? uuid->c_str() : nullptr;
+
+ if (flags & FLAG_STORAGE_CE) {
+ auto ce_path = create_data_user_ce_path(uuid_cstr, userId);
+ cleanup_invalid_package_dirs_under_path(ce_path);
+ }
+
+ if (flags & FLAG_STORAGE_DE) {
+ auto de_path = create_data_user_de_path(uuid_cstr, userId);
+ cleanup_invalid_package_dirs_under_path(de_path);
+ }
+
+ return ok();
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index aa5674d..96783c3 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -184,6 +184,9 @@
binder::Status migrateLegacyObbData();
+ binder::Status cleanupInvalidPackageDirs(const std::optional<std::string>& uuid, int32_t userId,
+ int32_t flags);
+
private:
std::recursive_mutex mLock;
std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock;
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 807eb13..f4fd9a9 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -126,6 +126,8 @@
void migrateLegacyObbData();
+ void cleanupInvalidPackageDirs(@nullable @utf8InCpp String uuid, int userId, int flags);
+
const int FLAG_STORAGE_DE = 0x1;
const int FLAG_STORAGE_CE = 0x2;
const int FLAG_STORAGE_EXTERNAL = 0x4;
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 51f7716..e390bab 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -25,6 +25,7 @@
static_libs: [
"libasync_safe",
"libdiskusage",
+ "libext2_uuid",
"libinstalld",
"liblog",
],
@@ -53,6 +54,7 @@
static_libs: [
"libasync_safe",
"libdiskusage",
+ "libext2_uuid",
"libinstalld",
"libziparchive",
"liblog",
@@ -99,6 +101,7 @@
static_libs: [
"libasync_safe",
"libdiskusage",
+ "libext2_uuid",
"libinstalld",
"libziparchive",
"liblog",
@@ -144,6 +147,7 @@
static_libs: [
"libasync_safe",
"libdiskusage",
+ "libext2_uuid",
"libinstalld",
"liblog",
"liblogwrap",
@@ -204,6 +208,7 @@
"libutils",
],
static_libs: [
+ "libext2_uuid",
"libinstalld",
"liblog",
],
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index b831515..dadc27b 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -75,6 +75,7 @@
namespace installd {
constexpr const char* kTestUuid = "TEST";
+constexpr const char* kTestPath = "/data/local/tmp/user/0";
#define FLAG_FORCE InstalldNativeService::FLAG_FORCE
@@ -97,7 +98,7 @@
}
static std::string get_full_path(const char* path) {
- return StringPrintf("/data/local/tmp/user/0/%s", path);
+ return StringPrintf("%s/%s", kTestPath, path);
}
static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
@@ -107,12 +108,16 @@
EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0);
}
-static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
+static int create(const char* path, uid_t owner, gid_t group, mode_t mode) {
int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode);
EXPECT_NE(fd, -1);
EXPECT_EQ(::fchown(fd, owner, group), 0);
EXPECT_EQ(::fchmod(fd, mode), 0);
- EXPECT_EQ(::close(fd), 0);
+ return fd;
+}
+
+static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
+ EXPECT_EQ(::close(create(path, owner, group, mode)), 0);
}
static int stat_gid(const char* path) {
@@ -127,6 +132,35 @@
return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
}
+static bool exists(const char* path) {
+ return ::access(get_full_path(path).c_str(), F_OK) == 0;
+}
+
+template <class Pred>
+static bool find_file(const char* path, Pred&& pred) {
+ bool result = false;
+ auto d = opendir(path);
+ if (d == nullptr) {
+ return result;
+ }
+ struct dirent* de;
+ while ((de = readdir(d))) {
+ const char* name = de->d_name;
+ if (pred(name, de->d_type == DT_DIR)) {
+ result = true;
+ break;
+ }
+ }
+ closedir(d);
+ return result;
+}
+
+static bool exists_renamed_deleted_dir() {
+ return find_file(kTestPath, [](const std::string& name, bool is_dir) {
+ return is_dir && is_renamed_deleted_dir(name);
+ });
+}
+
class ServiceTest : public testing::Test {
protected:
InstalldNativeService* service;
@@ -193,6 +227,134 @@
EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
}
+TEST_F(ServiceTest, DestroyUserData) {
+ LOG(INFO) << "DestroyUserData";
+
+ mkdir("com.example", 10000, 10000, 0700);
+ mkdir("com.example/foo", 10000, 10000, 0700);
+ touch("com.example/foo/file", 10000, 20000, 0700);
+ mkdir("com.example/bar", 10000, 20000, 0700);
+ touch("com.example/bar/file", 10000, 20000, 0700);
+
+ EXPECT_TRUE(exists("com.example/foo"));
+ EXPECT_TRUE(exists("com.example/foo/file"));
+ EXPECT_TRUE(exists("com.example/bar"));
+ EXPECT_TRUE(exists("com.example/bar/file"));
+
+ service->destroyUserData(testUuid, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE);
+
+ EXPECT_FALSE(exists("com.example/foo"));
+ EXPECT_FALSE(exists("com.example/foo/file"));
+ EXPECT_FALSE(exists("com.example/bar"));
+ EXPECT_FALSE(exists("com.example/bar/file"));
+
+ EXPECT_FALSE(exists_renamed_deleted_dir());
+}
+
+TEST_F(ServiceTest, DestroyAppData) {
+ LOG(INFO) << "DestroyAppData";
+
+ mkdir("com.example", 10000, 10000, 0700);
+ mkdir("com.example/foo", 10000, 10000, 0700);
+ touch("com.example/foo/file", 10000, 20000, 0700);
+ mkdir("com.example/bar", 10000, 20000, 0700);
+ touch("com.example/bar/file", 10000, 20000, 0700);
+
+ EXPECT_TRUE(exists("com.example/foo"));
+ EXPECT_TRUE(exists("com.example/foo/file"));
+ EXPECT_TRUE(exists("com.example/bar"));
+ EXPECT_TRUE(exists("com.example/bar/file"));
+
+ service->destroyAppData(testUuid, "com.example", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, 0);
+
+ EXPECT_FALSE(exists("com.example/foo"));
+ EXPECT_FALSE(exists("com.example/foo/file"));
+ EXPECT_FALSE(exists("com.example/bar"));
+ EXPECT_FALSE(exists("com.example/bar/file"));
+
+ EXPECT_FALSE(exists_renamed_deleted_dir());
+}
+
+TEST_F(ServiceTest, CleanupInvalidPackageDirs) {
+ LOG(INFO) << "CleanupInvalidPackageDirs";
+
+ mkdir("5b14b6458a44==deleted==", 10000, 10000, 0700);
+ mkdir("5b14b6458a44==deleted==/foo", 10000, 10000, 0700);
+ touch("5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700);
+ mkdir("5b14b6458a44==deleted==/bar", 10000, 20000, 0700);
+ touch("5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700);
+
+ auto fd = create("5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700);
+
+ mkdir("b14b6458a44NOTdeleted", 10000, 10000, 0700);
+ mkdir("b14b6458a44NOTdeleted/foo", 10000, 10000, 0700);
+ touch("b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700);
+ mkdir("b14b6458a44NOTdeleted/bar", 10000, 20000, 0700);
+ touch("b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700);
+
+ mkdir("com.example", 10000, 10000, 0700);
+ mkdir("com.example/foo", 10000, 10000, 0700);
+ touch("com.example/foo/file", 10000, 20000, 0700);
+ mkdir("com.example/bar", 10000, 20000, 0700);
+ touch("com.example/bar/file", 10000, 20000, 0700);
+
+ mkdir("==deleted==", 10000, 10000, 0700);
+ mkdir("==deleted==/foo", 10000, 10000, 0700);
+ touch("==deleted==/foo/file", 10000, 20000, 0700);
+ mkdir("==deleted==/bar", 10000, 20000, 0700);
+ touch("==deleted==/bar/file", 10000, 20000, 0700);
+
+ EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo"));
+ EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo/file"));
+ EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar"));
+ EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/file"));
+ EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/opened_file"));
+
+ EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo"));
+ EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file"));
+ EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar"));
+ EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file"));
+
+ EXPECT_TRUE(exists("com.example/foo"));
+ EXPECT_TRUE(exists("com.example/foo/file"));
+ EXPECT_TRUE(exists("com.example/bar"));
+ EXPECT_TRUE(exists("com.example/bar/file"));
+
+ EXPECT_TRUE(exists("==deleted==/foo"));
+ EXPECT_TRUE(exists("==deleted==/foo/file"));
+ EXPECT_TRUE(exists("==deleted==/bar"));
+ EXPECT_TRUE(exists("==deleted==/bar/file"));
+
+ EXPECT_TRUE(exists_renamed_deleted_dir());
+
+ service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE);
+
+ EXPECT_EQ(::close(fd), 0);
+
+ EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo"));
+ EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo/file"));
+ EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar"));
+ EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/file"));
+ EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/opened_file"));
+
+ EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo"));
+ EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file"));
+ EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar"));
+ EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file"));
+
+ EXPECT_TRUE(exists("com.example/foo"));
+ EXPECT_TRUE(exists("com.example/foo/file"));
+ EXPECT_TRUE(exists("com.example/bar"));
+ EXPECT_TRUE(exists("com.example/bar/file"));
+
+ EXPECT_FALSE(exists("==deleted==/foo"));
+ EXPECT_FALSE(exists("==deleted==/foo/file"));
+ EXPECT_FALSE(exists("==deleted==/bar"));
+ EXPECT_FALSE(exists("==deleted==/bar/file"));
+
+ EXPECT_FALSE(exists_renamed_deleted_dir());
+}
+
TEST_F(ServiceTest, HashSecondaryDex) {
LOG(INFO) << "HashSecondaryDex";
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index ed87b67..5e8c79e 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -555,6 +555,24 @@
EXPECT_EQ(0, MatchExtension("docx"));
}
+TEST_F(UtilsTest, TestIsRenamedDeletedDir) {
+ EXPECT_FALSE(is_renamed_deleted_dir(""));
+ EXPECT_FALSE(is_renamed_deleted_dir("1"));
+ EXPECT_FALSE(is_renamed_deleted_dir("="));
+ EXPECT_FALSE(is_renamed_deleted_dir("=="));
+ EXPECT_FALSE(is_renamed_deleted_dir("d=="));
+ EXPECT_FALSE(is_renamed_deleted_dir("ed=="));
+ EXPECT_FALSE(is_renamed_deleted_dir("ted=="));
+ EXPECT_FALSE(is_renamed_deleted_dir("eted=="));
+ EXPECT_FALSE(is_renamed_deleted_dir("leted=="));
+ EXPECT_FALSE(is_renamed_deleted_dir("eleted=="));
+ EXPECT_FALSE(is_renamed_deleted_dir("deleted=="));
+ EXPECT_FALSE(is_renamed_deleted_dir("=deleted=="));
+ EXPECT_TRUE(is_renamed_deleted_dir("==deleted=="));
+ EXPECT_TRUE(is_renamed_deleted_dir("123==deleted=="));
+ EXPECT_TRUE(is_renamed_deleted_dir("5b14b6458a44==deleted=="));
+}
+
TEST_F(UtilsTest, TestRollbackPaths) {
EXPECT_EQ("/data/misc_ce/0/rollback/239/com.foo",
create_data_misc_ce_rollback_package_path(nullptr, 0, 239, "com.foo"));
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 0f8a732..faccded 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -22,9 +22,10 @@
#include <stdlib.h>
#include <sys/capability.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <sys/wait.h>
#include <sys/xattr.h>
-#include <sys/statvfs.h>
+#include <uuid/uuid.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -47,6 +48,7 @@
#define DEBUG_XATTRS 0
+using android::base::Dirname;
using android::base::EndsWith;
using android::base::Fdopendir;
using android::base::StringPrintf;
@@ -55,6 +57,10 @@
namespace android {
namespace installd {
+using namespace std::literals;
+
+static constexpr auto deletedSuffix = "==deleted=="sv;
+
/**
* Check that given string is valid filename, and that it attempts no
* parent or child directory traversal.
@@ -595,6 +601,97 @@
return res;
}
+static std::string make_unique_name(std::string_view suffix) {
+ static constexpr auto uuidStringSize = 36;
+
+ uuid_t guid;
+ uuid_generate(guid);
+
+ std::string name;
+ const auto suffixSize = suffix.size();
+ name.reserve(uuidStringSize + suffixSize);
+
+ name.resize(uuidStringSize);
+ uuid_unparse(guid, name.data());
+ name.append(suffix);
+
+ return name;
+}
+
+static int rename_delete_dir_contents(const std::string& pathname,
+ int (*exclusion_predicate)(const char*, const int),
+ bool ignore_if_missing) {
+ auto temp_dir_name = make_unique_name(deletedSuffix);
+ auto temp_dir_path =
+ base::StringPrintf("%s/%s", Dirname(pathname).c_str(), temp_dir_name.c_str());
+
+ if (::rename(pathname.c_str(), temp_dir_path.c_str())) {
+ if (ignore_if_missing && (errno == ENOENT)) {
+ return 0;
+ }
+ ALOGE("Couldn't rename %s -> %s: %s \n", pathname.c_str(), temp_dir_path.c_str(),
+ strerror(errno));
+ return -errno;
+ }
+
+ return delete_dir_contents(temp_dir_path.c_str(), 1, exclusion_predicate, ignore_if_missing);
+}
+
+bool is_renamed_deleted_dir(const std::string& path) {
+ if (path.size() < deletedSuffix.size()) {
+ return false;
+ }
+ std::string_view pathSuffix{path.c_str() + path.size() - deletedSuffix.size()};
+ return pathSuffix == deletedSuffix;
+}
+
+int rename_delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing) {
+ return rename_delete_dir_contents(pathname, nullptr, ignore_if_missing);
+}
+
+static auto open_dir(const char* dir) {
+ struct DirCloser {
+ void operator()(DIR* d) const noexcept { ::closedir(d); }
+ };
+ return std::unique_ptr<DIR, DirCloser>(::opendir(dir));
+}
+
+void cleanup_invalid_package_dirs_under_path(const std::string& pathname) {
+ auto dir = open_dir(pathname.c_str());
+ if (!dir) {
+ return;
+ }
+ int dfd = dirfd(dir.get());
+ if (dfd < 0) {
+ ALOGE("Couldn't dirfd %s: %s\n", pathname.c_str(), strerror(errno));
+ return;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(dir.get()))) {
+ if (de->d_type != DT_DIR) {
+ continue;
+ }
+
+ std::string name{de->d_name};
+ // always skip "." and ".."
+ if (name == "." || name == "..") {
+ continue;
+ }
+
+ if (is_renamed_deleted_dir(name) || !is_valid_filename(name) ||
+ !is_valid_package_name(name)) {
+ ALOGI("Deleting renamed or invalid data directory: %s\n", name.c_str());
+ // Deleting the content.
+ delete_dir_contents_fd(dfd, name.c_str());
+ // Deleting the directory
+ if (unlinkat(dfd, name.c_str(), AT_REMOVEDIR) < 0) {
+ ALOGE("Couldn't unlinkat %s: %s\n", name.c_str(), strerror(errno));
+ }
+ }
+ }
+}
+
int delete_dir_contents_fd(int dfd, const char *name)
{
int fd, res;
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 549fc6c..0b1c324 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -120,6 +120,11 @@
int delete_dir_contents(const std::string& pathname, bool ignore_if_missing = false);
int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false);
+bool is_renamed_deleted_dir(const std::string& path);
+int rename_delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = true);
+
+void cleanup_invalid_package_dirs_under_path(const std::string& pathname);
+
int delete_dir_contents(const char *pathname,
int also_delete_dir,
int (*exclusion_predicate)(const char *name, const int is_dir),
diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp
index 406fee0..1504715 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -27,13 +27,6 @@
#include "ParcelValTypes.h"
-using android::BAD_TYPE;
-using android::BAD_VALUE;
-using android::NO_ERROR;
-using android::Parcel;
-using android::status_t;
-using android::UNEXPECTED_NULL;
-
using android::binder::VAL_BOOLEAN;
using android::binder::VAL_INTEGER;
using android::binder::VAL_LONG;
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 1446802..38bde3a 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -26,13 +26,9 @@
static_libs: [
"libbase",
"libbinder_random_parcel",
- "libcgrouprc",
- "libcgrouprc_format",
"libcutils",
"libhidlbase",
"liblog",
- "libprocessgroup",
- "libjsoncpp",
"libutils",
],
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index e2f32e3..ef7602f 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -588,6 +588,8 @@
"HAL and AHardwareBuffer pixel format don't match");
static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
"HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_YCBCR_P010 == AHARDWAREBUFFER_FORMAT_YCbCr_P010,
+ "HAL and AHardwareBuffer pixel format don't match");
switch (format) {
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
@@ -617,6 +619,7 @@
case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
+ case AHARDWAREBUFFER_FORMAT_YCbCr_P010:
return true;
default:
@@ -633,6 +636,7 @@
case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
+ case AHARDWAREBUFFER_FORMAT_YCbCr_P010:
return true;
default:
return false;
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index d93a84c..78c56d9 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -158,6 +158,14 @@
* cube-maps or multi-layered textures.
*/
AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23,
+
+ /**
+ * YUV P010 format.
+ * Must have an even width and height. Can be accessed in OpenGL
+ * shaders through an external sampler. Does not support mip-maps
+ * cube-maps or multi-layered textures.
+ */
+ AHARDWAREBUFFER_FORMAT_YCbCr_P010 = 0x36,
};
/**