diff options
Diffstat (limited to 'cmds')
68 files changed, 4948 insertions, 1475 deletions
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 783a475829..6fb9a4d112 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -239,13 +239,14 @@ static const TracingCategory k_categories[] = { } }, { "memory", "Memory", 0, { { OPT, "events/mm_event/mm_event_record/enable" }, - { OPT, "events/kmem/rss_stat/enable" }, + { OPT, "events/synthetic/rss_stat_throttled/enable" }, { OPT, "events/kmem/ion_heap_grow/enable" }, { OPT, "events/kmem/ion_heap_shrink/enable" }, { OPT, "events/ion/ion_stat/enable" }, { OPT, "events/gpu_mem/gpu_mem_total/enable" }, + { OPT, "events/fastrpc/fastrpc_dma_stat/enable" }, } }, - { "thermal", "Thermal event", 0, { + { "thermal", "Thermal event", ATRACE_TAG_THERMAL, { { REQ, "events/thermal/thermal_temperature/enable" }, { OPT, "events/thermal/cdev_update/enable" }, } }, @@ -1224,10 +1225,7 @@ int main(int argc, char **argv) if (ret < 0) { for (int i = optind; i < argc; i++) { - if (!setCategoryEnable(argv[i])) { - fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]); - exit(1); - } + setCategoryEnable(argv[i]); } break; } @@ -1343,10 +1341,10 @@ int main(int argc, char **argv) // contain entries from only one CPU can cause "begin" entries without a // matching "end" entry to show up if a task gets migrated from one CPU to // another. - if (!onlyUserspace) + if (!onlyUserspace) { ok = clearTrace(); - - writeClockSyncMarker(); + writeClockSyncMarker(); + } if (ok && !async && !traceStream) { // Sleep to allow the trace to be captured. struct timespec timeLeft; diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 01c4723f29..5267b0294c 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -35,6 +35,8 @@ on late-init chmod 0666 /sys/kernel/tracing/events/sched/sched_pi_setprio/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_process_exit/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_process_exit/enable + chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_process_free/enable + chmod 0666 /sys/kernel/tracing/events/sched/sched_process_free/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_waking/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_waking/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_wakeup_new/enable @@ -61,6 +63,8 @@ on late-init chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_pause/enable chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable + chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_work_period/enable + chmod 0666 /sys/kernel/tracing/events/power/gpu_work_period/enable chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/enable chmod 0666 /sys/kernel/tracing/events/power/suspend_resume/enable chmod 0666 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable @@ -282,6 +286,18 @@ on late-init chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu23/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu23/trace +# Setup synthetic events + chmod 0666 /sys/kernel/tracing/synthetic_events + chmod 0666 /sys/kernel/debug/tracing/synthetic_events + + # rss_stat_throttled + write /sys/kernel/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" + write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" + + # allow creating event triggers + chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger + chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger + # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created # will likely fail with EBUSY as it would be in use by traced_probes. diff --git a/cmds/cmd/Android.bp b/cmds/cmd/Android.bp index c900a24e15..c3d2601444 100644 --- a/cmds/cmd/Android.bp +++ b/cmds/cmd/Android.bp @@ -49,6 +49,7 @@ cc_binary { "liblog", "libselinux", "libbinder", + "packagemanager_aidl-cpp", ], cflags: [ diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp index be2c702034..8f1c01ab2c 100644 --- a/cmds/cmd/cmd.cpp +++ b/cmds/cmd/cmd.cpp @@ -52,7 +52,7 @@ static int sort_func(const String16* lhs, const String16* rhs) } struct SecurityContext_Delete { - void operator()(security_context_t p) const { + void operator()(char* p) const { freecon(p); } }; @@ -108,7 +108,7 @@ public: } if (is_selinux_enabled() && seLinuxContext.size() > 0) { String8 seLinuxContext8(seLinuxContext); - security_context_t tmp = nullptr; + char* tmp = nullptr; getfilecon(fullPath.string(), &tmp); Unique_SecurityContext context(tmp); if (checkWrite) { diff --git a/cmds/cmd/fuzzer/Android.bp b/cmds/cmd/fuzzer/Android.bp new file mode 100644 index 0000000000..a65f6deb8a --- /dev/null +++ b/cmds/cmd/fuzzer/Android.bp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_cmds_cmd_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_cmds_cmd_license"], +} + +cc_fuzz { + name: "cmd_fuzzer", + srcs: [ + "cmd_fuzzer.cpp", + ], + static_libs: [ + "libcmd", + "liblog", + "libselinux", + ], + shared_libs: [ + "libbinder", + "libutils", + ], + fuzz_config: { + cc: [ + "android-media-fuzzing-reports@google.com", + ], + componentid: 155276, + }, +} diff --git a/cmds/cmd/fuzzer/README.md b/cmds/cmd/fuzzer/README.md new file mode 100644 index 0000000000..db37ecea32 --- /dev/null +++ b/cmds/cmd/fuzzer/README.md @@ -0,0 +1,51 @@ +# Fuzzer for libcmd_fuzzer + +## Plugin Design Considerations +The fuzzer plugin for libcmd 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. + +libcmd supports the following parameters: +1. In (parameter name: `in`) +2. Out (parameter name: `out`) +3. Err (parameter name: `err`) +4. Run Mode (parameter name: `runMode`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +| `in` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider| +| `out` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider| +| `err` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider| +| `runMode` | 1.`RunMode::kStandalone` 2. `RunMode::kLibrary` | Value chosen from valid values using FuzzedDataProvider| + +This also ensures that the plugin is always deterministic for any given input. + +##### Maximize utilization of input data +The plugin feeds the entire input data to the cmd module. +This ensures that the plugin tolerates any kind of input (empty, huge, +malformed, etc) and doesnt `exit()` on any input and thereby increasing the +chance of identifying vulnerabilities. + +## Build + +This describes steps to build cmd_fuzzer binary. + +### Android + +#### Steps to build +Build the fuzzer +``` + $ mm -j$(nproc) cmd_fuzzer +``` +#### Steps to run +To run on device +``` + $ adb sync data + $ adb shell /data/fuzz/${TARGET_ARCH}/cmd_fuzzer/cmd_fuzzer +``` + +## References: + * http://llvm.org/docs/LibFuzzer.html + * https://github.com/google/oss-fuzz diff --git a/cmds/cmd/fuzzer/cmd_fuzzer.cpp b/cmds/cmd/fuzzer/cmd_fuzzer.cpp new file mode 100644 index 0000000000..ab514a1d04 --- /dev/null +++ b/cmds/cmd/fuzzer/cmd_fuzzer.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021 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/TextOutput.h> +#include <cmd.h> +#include <fcntl.h> +#include <unistd.h> +#include <string> +#include <vector> + +#include <fuzzer/FuzzedDataProvider.h> + +using namespace std; +using namespace android; + +class TestTextOutput : public TextOutput { +public: + TestTextOutput() {} + virtual ~TestTextOutput() {} + + virtual status_t print(const char* /*txt*/, size_t /*len*/) { return NO_ERROR; } + virtual void moveIndent(int /*delta*/) { return; } + virtual void pushBundle() { return; } + virtual void popBundle() { return; } +}; + +class CmdFuzzer { +public: + void process(const uint8_t* data, size_t size); + +private: + FuzzedDataProvider* mFDP = nullptr; +}; + +void CmdFuzzer::process(const uint8_t* data, size_t size) { + mFDP = new FuzzedDataProvider(data, size); + vector<string> arguments; + if (mFDP->ConsumeBool()) { + if (mFDP->ConsumeBool()) { + arguments = {"-w", "media.aaudio"}; + } else { + arguments = {"-l"}; + } + } else { + while (mFDP->remaining_bytes() > 0) { + size_t sizestr = mFDP->ConsumeIntegralInRange<size_t>(1, mFDP->remaining_bytes()); + string argument = mFDP->ConsumeBytesAsString(sizestr); + arguments.emplace_back(argument); + } + } + vector<string_view> argSV; + for (auto& argument : arguments) { + argSV.emplace_back(argument.c_str()); + } + int32_t in = open("/dev/null", O_RDWR | O_CREAT); + int32_t out = open("/dev/null", O_RDWR | O_CREAT); + int32_t err = open("/dev/null", O_RDWR | O_CREAT); + TestTextOutput output; + TestTextOutput error; + RunMode runMode = mFDP->ConsumeBool() ? RunMode::kStandalone : RunMode::kLibrary; + cmdMain(argSV, output, error, in, out, err, runMode); + delete mFDP; + close(in); + close(out); + close(err); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + CmdFuzzer cmdFuzzer; + cmdFuzzer.process(data, size); + return 0; +} diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index aff32c38c2..a2491e503f 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -86,9 +86,11 @@ cc_defaults { shared_libs: [ "android.hardware.dumpstate@1.0", "android.hardware.dumpstate@1.1", + "android.hardware.dumpstate-V1-ndk", "libziparchive", "libbase", "libbinder", + "libbinder_ndk", "libcrypto", "libcutils", "libdebuggerd_client", @@ -100,6 +102,7 @@ cc_defaults { "liblog", "libutils", "libbinderdebug", + "packagemanager_aidl-cpp", ], srcs: [ "DumpstateService.cpp", @@ -173,7 +176,6 @@ cc_test { test_suites: ["device-tests"], } - // =======================# // dumpstate_test_fixture # // =======================# diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp index c2c8a72f52..4d3a67bcd3 100644 --- a/cmds/dumpstate/DumpPool.cpp +++ b/cmds/dumpstate/DumpPool.cpp @@ -33,6 +33,20 @@ namespace dumpstate { 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(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_ } 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 @@ void DumpPool::shutdown() { } 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; - } - Future future = iterator->second; - futures_map_.erase(iterator); - - std::string result = future.get(); - if (result.empty()) { - 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; } - 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 0c3c2cc0d7..9fb0fcc83c 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 @@ namespace dumpstate { 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 @@ class DumpPoolTest; * } * ... * 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 @@ class DumpPool { * 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 @@ class DumpPool { 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 @@ class DumpPool { 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 @@ class DumpPool { 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/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index ba25a5a603..e42ee0540b 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -192,7 +192,7 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "progress:\n"); ds_->progress_->Dump(fd, " "); dprintf(fd, "args: %s\n", ds_->options_->args.c_str()); - dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode.c_str()); + dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode_string.c_str()); dprintf(fd, "version: %s\n", ds_->version_.c_str()); dprintf(fd, "bugreport_dir: %s\n", destination.c_str()); dprintf(fd, "screenshot_path: %s\n", ds_->screenshot_path_.c_str()); @@ -202,7 +202,6 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "base_name: %s\n", ds_->base_name_.c_str()); dprintf(fd, "name: %s\n", ds_->name_.c_str()); dprintf(fd, "now: %ld\n", ds_->now_); - dprintf(fd, "is_zipping: %s\n", ds_->IsZipping() ? "true" : "false"); dprintf(fd, "notification title: %s\n", ds_->options_->notification_title.c_str()); dprintf(fd, "notification description: %s\n", ds_->options_->notification_description.c_str()); diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index c833d0e6bd..aa42541a66 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp @@ -48,11 +48,26 @@ static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) { 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 @@ static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) { 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 @@ int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) 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 @@ int RunCommandToFd(int fd, const std::string& title, const std::vector<std::stri 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 @@ int RunCommandToFd(int fd, const std::string& title, const std::vector<std::stri // 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 @@ int RunCommandToFd(int fd, const std::string& title, const std::vector<std::stri 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 @@ int RunCommandToFd(int fd, const std::string& title, const std::vector<std::stri /* 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 30ef4b9f55..2b94b71a7f 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -57,12 +57,15 @@ #include <utility> #include <vector> +#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> #include <android/content/pm/IPackageManagerNative.h> #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h> #include <android/hardware/dumpstate/1.1/IDumpstateDevice.h> @@ -89,11 +92,10 @@ #include "DumpstateService.h" #include "dumpstate.h" -using IDumpstateDevice_1_0 = ::android::hardware::dumpstate::V1_0::IDumpstateDevice; -using IDumpstateDevice_1_1 = ::android::hardware::dumpstate::V1_1::IDumpstateDevice; -using ::android::hardware::dumpstate::V1_1::DumpstateMode; -using ::android::hardware::dumpstate::V1_1::DumpstateStatus; -using ::android::hardware::dumpstate::V1_1::toString; +namespace dumpstate_hal_hidl_1_0 = android::hardware::dumpstate::V1_0; +namespace dumpstate_hal_hidl = android::hardware::dumpstate::V1_1; +namespace dumpstate_hal_aidl = aidl::android::hardware::dumpstate; + using ::std::literals::chrono_literals::operator""ms; using ::std::literals::chrono_literals::operator""s; using ::std::placeholders::_1; @@ -118,6 +120,7 @@ using android::os::dumpstate::DumpFileToFd; 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 @@ -216,9 +219,9 @@ static const std::string ANR_FILE_PREFIX = "anr_"; 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"; @@ -369,14 +372,10 @@ static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot( /* * Returns a vector of dump fds under |dir_path| with a given |file_prefix|. * The returned vector is sorted by the mtimes of the dumps with descending - * order. If |limit_by_mtime| is set, the vector only contains files that - * were written in the last 30 minutes. + * order. */ static std::vector<DumpData> GetDumpFds(const std::string& dir_path, - const std::string& file_prefix, - bool limit_by_mtime) { - const time_t thirty_minutes_ago = ds.now_ - 60 * 30; - + const std::string& file_prefix) { std::unique_ptr<DIR, decltype(&closedir)> dump_dir(opendir(dir_path.c_str()), closedir); if (dump_dir == nullptr) { @@ -410,11 +409,6 @@ static std::vector<DumpData> GetDumpFds(const std::string& dir_path, continue; } - if (limit_by_mtime && st.st_mtime < thirty_minutes_ago) { - MYLOGI("Excluding stale dump file: %s\n", abs_path.c_str()); - continue; - } - dump_data.emplace_back(DumpData{abs_path, std::move(fd), st.st_mtime}); } if (!dump_data.empty()) { @@ -445,7 +439,7 @@ static bool AddDumps(const std::vector<DumpData>::const_iterator start, strerror(errno)); } - if (ds.IsZipping() && add_to_zip) { + if (add_to_zip) { if (ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd, /* timeout = */ 0ms) != OK) { MYLOGE("Unable to add %s to zip file, addZipEntryFromFd failed\n", name.c_str()); } @@ -484,7 +478,6 @@ void do_mountinfo(int pid, const char* name __attribute__((unused))) { } void add_mountinfo() { - if (!ds.IsZipping()) return; std::string title = "MOUNT INFO"; mount_points.clear(); DurationReporter duration_reporter(title, true); @@ -807,7 +800,7 @@ void Dumpstate::PrintHeader() const { printf("Bugreport format version: %s\n", version_.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()); + options_->args.c_str(), options_->bugreport_mode_string.c_str()); printf("\n"); } @@ -821,11 +814,6 @@ static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = { status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd, std::chrono::milliseconds timeout = 0ms) { - if (!IsZipping()) { - MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n", - entry_name.c_str()); - return INVALID_OPERATION; - } std::string valid_name = entry_name; // Rename extension if necessary. @@ -926,21 +914,12 @@ static int _add_file_from_fd(const char* title __attribute__((unused)), const ch } void Dumpstate::AddDir(const std::string& dir, bool recursive) { - if (!IsZipping()) { - MYLOGD("Not adding dir %s because it's not a zipped bugreport\n", dir.c_str()); - return; - } MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive); DurationReporter duration_reporter(dir, true); dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd); } bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) { - if (!IsZipping()) { - MYLOGD("Not adding text zip entry %s because it's not a zipped bugreport\n", - entry_name.c_str()); - return false; - } MYLOGD("Adding zip text entry %s\n", entry_name.c_str()); int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_); if (err != 0) { @@ -1033,10 +1012,6 @@ static void DoLogcat() { } static void DumpIncidentReport() { - if (!ds.IsZipping()) { - MYLOGD("Not dumping incident report because it's not a zipped bugreport\n"); - return; - } 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, @@ -1062,10 +1037,6 @@ static void MaybeAddSystemTraceToZip() { // This function copies into the .zip the system trace that was snapshotted // by the early call to MaybeSnapshotSystemTrace(), if any background // tracing was happening. - if (!ds.IsZipping()) { - MYLOGD("Not dumping system trace because it's not a zipped bugreport\n"); - return; - } if (!ds.has_system_trace_) { // No background trace was happening at the time dumpstate was invoked. return; @@ -1077,10 +1048,6 @@ static void MaybeAddSystemTraceToZip() { } static void DumpVisibleWindowViews() { - if (!ds.IsZipping()) { - MYLOGD("Not dumping visible views because it's not a zipped bugreport\n"); - return; - } DurationReporter duration_reporter("VISIBLE WINDOW VIEWS"); const std::string path = ds.bugreport_internal_dir_ + "/tmp_visible_window_views"; auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), @@ -1121,7 +1088,7 @@ static void DumpDynamicPartitionInfo() { RunCommand("DEVICE-MAPPER", {"gsid", "dump-device-mapper"}); } -static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_dir) { +static void AddAnrTraceDir(const std::string& anr_traces_dir) { MYLOGD("AddAnrTraceDir(): dump_traces_file=%s, anr_traces_dir=%s\n", dump_traces_path, anr_traces_dir.c_str()); @@ -1129,13 +1096,9 @@ static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_ // (created with mkostemp or similar) that contains dumps taken earlier // on in the process. if (dump_traces_path != nullptr) { - if (add_to_zip) { - ds.AddZipEntry(ZIP_ROOT_DIR + anr_traces_dir + "/traces-just-now.txt", dump_traces_path); - } else { - MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n", - dump_traces_path); - ds.DumpFile("VM TRACES JUST NOW", dump_traces_path); - } + MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n", + dump_traces_path); + ds.DumpFile("VM TRACES JUST NOW", dump_traces_path); const int ret = unlink(dump_traces_path); if (ret == -1) { @@ -1146,14 +1109,12 @@ static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_ // Add a specific message for the first ANR Dump. if (ds.anr_data_.size() > 0) { + // The "last" ANR will always be present in the body of the main entry. AddDumps(ds.anr_data_.begin(), ds.anr_data_.begin() + 1, - "VM TRACES AT LAST ANR", add_to_zip); + "VM TRACES AT LAST ANR", false /* add_to_zip */); - // The "last" ANR will always be included as separate entry in the zip file. In addition, - // it will be present in the body of the main entry if |add_to_zip| == false. - // // Historical ANRs are always included as separate entries in the bugreport zip file. - AddDumps(ds.anr_data_.begin() + ((add_to_zip) ? 1 : 0), ds.anr_data_.end(), + AddDumps(ds.anr_data_.begin(), ds.anr_data_.end(), "HISTORICAL ANR", true /* add_to_zip */); } else { printf("*** NO ANRs to dump in %s\n\n", ANR_DIR.c_str()); @@ -1161,11 +1122,9 @@ static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_ } static void AddAnrTraceFiles() { - const bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR; - std::string anr_traces_dir = "/data/anr"; - AddAnrTraceDir(add_to_zip, anr_traces_dir); + AddAnrTraceDir(anr_traces_dir); RunCommand("ANR FILES", {"ls", "-lt", ANR_DIR}); @@ -1237,22 +1196,29 @@ static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, i std::string path(title); path.append(" - ").append(String8(service).c_str()); size_t bytes_written = 0; - status_t status = dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args); - if (status == OK) { - dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority); - std::chrono::duration<double> elapsed_seconds; - if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH && - service == String16("meminfo")) { - // Use a longer timeout for meminfo, since 30s is not always enough. - status = dumpsys.writeDump(STDOUT_FILENO, service, 60s, - /* as_proto = */ false, elapsed_seconds, bytes_written); - } else { - status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout, - /* as_proto = */ false, elapsed_seconds, bytes_written); + if (PropertiesHelper::IsDryRun()) { + dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority); + dumpsys.writeDumpFooter(STDOUT_FILENO, service, std::chrono::milliseconds(1)); + } else { + status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args); + if (status == OK) { + dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority); + std::chrono::duration<double> elapsed_seconds; + if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH && + service == String16("meminfo")) { + // Use a longer timeout for meminfo, since 30s is not always enough. + status = dumpsys.writeDump(STDOUT_FILENO, service, 60s, + /* as_proto = */ false, elapsed_seconds, + bytes_written); + } else { + status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout, + /* as_proto = */ false, elapsed_seconds, + bytes_written); + } + dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds); + bool dump_complete = (status == OK); + dumpsys.stopDumpThread(dump_complete); } - dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds); - bool dump_complete = (status == OK); - dumpsys.stopDumpThread(dump_complete); } auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>( @@ -1294,10 +1260,6 @@ static Dumpstate::RunStatus RunDumpsysTextNormalPriority(const std::string& titl static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priority, std::chrono::milliseconds timeout, std::chrono::milliseconds service_timeout) { - if (!ds.IsZipping()) { - MYLOGD("Not dumping %s because it's not a zipped bugreport\n", title.c_str()); - return Dumpstate::RunStatus::OK; - } sp<android::IServiceManager> sm = defaultServiceManager(); Dumpsys dumpsys(sm.get()); Vector<String16> args; @@ -1316,7 +1278,7 @@ static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priori path.append("_HIGH"); } path.append(kProtoExt); - status_t status = dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args); + status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args); if (status == OK) { status = ds.AddZipEntryFromFd(path, dumpsys.getDumpFd(), service_timeout); bool dumpTerminated = (status == OK); @@ -1377,12 +1339,6 @@ static Dumpstate::RunStatus RunDumpsysNormal() { * 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(), - false, out_fd); - return; - } RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(), false, out_fd); @@ -1594,15 +1550,18 @@ static Dumpstate::RunStatus dumpstate() { 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 @@ -1627,7 +1586,6 @@ static Dumpstate::RunStatus dumpstate() { 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", @@ -1637,7 +1595,7 @@ static Dumpstate::RunStatus dumpstate() { 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); } @@ -1690,7 +1648,7 @@ static Dumpstate::RunStatus dumpstate() { DumpPacketStats(); - RunDumpsys("EBPF MAP STATS", {"netd", "trafficcontroller"}); + RunDumpsys("EBPF MAP STATS", {"connectivity", "trafficcontroller"}); DoKmsg(); @@ -1734,7 +1692,7 @@ static Dumpstate::RunStatus dumpstate() { 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); } @@ -1763,7 +1721,7 @@ static Dumpstate::RunStatus dumpstate() { 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); } @@ -1797,7 +1755,7 @@ static Dumpstate::RunStatus dumpstate() { 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); @@ -1822,6 +1780,7 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { 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. @@ -1829,15 +1788,18 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { // 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); } /* Run some operations that require root. */ - ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()); - ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping()); + if (!PropertiesHelper::IsDryRun()) { + ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX); + ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX); + } ds.AddDir(RECOVERY_DIR, true); ds.AddDir(RECOVERY_DATA_DIR, true); @@ -1876,12 +1838,11 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { 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; @@ -1912,8 +1873,9 @@ static void DumpstateRadioCommon(bool include_sensitive_info = true) { } 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(); @@ -1923,7 +1885,7 @@ static void DumpstateRadioCommon(bool include_sensitive_info = true) { 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); } @@ -1957,12 +1919,14 @@ static void DumpstateTelephonyOnly(const std::string& calling_package) { // 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); @@ -2045,7 +2009,7 @@ static void DumpstateTelephonyOnly(const std::string& calling_package) { 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); } @@ -2120,7 +2084,7 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { int timeout_failures = 0; bool dalvik_found = false; - const std::set<int> hal_pids = get_interesting_hal_pids(); + const std::set<int> hal_pids = get_interesting_pids(); struct dirent* d; while ((d = readdir(proc.get()))) { @@ -2190,16 +2154,199 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { return RunStatus::OK; } +static dumpstate_hal_hidl::DumpstateMode GetDumpstateHalModeHidl( + const Dumpstate::BugreportMode bugreport_mode) { + switch (bugreport_mode) { + case Dumpstate::BugreportMode::BUGREPORT_FULL: + return dumpstate_hal_hidl::DumpstateMode::FULL; + case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: + return dumpstate_hal_hidl::DumpstateMode::INTERACTIVE; + case Dumpstate::BugreportMode::BUGREPORT_REMOTE: + return dumpstate_hal_hidl::DumpstateMode::REMOTE; + case Dumpstate::BugreportMode::BUGREPORT_WEAR: + return dumpstate_hal_hidl::DumpstateMode::WEAR; + case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: + return dumpstate_hal_hidl::DumpstateMode::CONNECTIVITY; + case Dumpstate::BugreportMode::BUGREPORT_WIFI: + return dumpstate_hal_hidl::DumpstateMode::WIFI; + case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: + return dumpstate_hal_hidl::DumpstateMode::DEFAULT; + } + return dumpstate_hal_hidl::DumpstateMode::DEFAULT; +} + +static dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode GetDumpstateHalModeAidl( + const Dumpstate::BugreportMode bugreport_mode) { + switch (bugreport_mode) { + case Dumpstate::BugreportMode::BUGREPORT_FULL: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::FULL; + case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::INTERACTIVE; + case Dumpstate::BugreportMode::BUGREPORT_REMOTE: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::REMOTE; + case Dumpstate::BugreportMode::BUGREPORT_WEAR: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WEAR; + case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::CONNECTIVITY; + case Dumpstate::BugreportMode::BUGREPORT_WIFI: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WIFI; + case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT; + } + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT; +} + +static void DoDumpstateBoardHidl( + const sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_1_0, + const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds, + const Dumpstate::BugreportMode bugreport_mode, + const size_t timeout_sec) { + + using ScopedNativeHandle = + std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>; + ScopedNativeHandle handle(native_handle_create(static_cast<int>(dumpstate_fds.size()), 0), + [](native_handle_t* handle) { + // we don't close file handle's here + // via native_handle_close(handle) + // instead we let dumpstate_fds close the file handles when + // dumpstate_fds gets destroyed + native_handle_delete(handle); + }); + if (handle == nullptr) { + MYLOGE("Could not create native_handle for dumpstate HAL\n"); + return; + } + + for (size_t i = 0; i < dumpstate_fds.size(); i++) { + handle.get()->data[i] = dumpstate_fds[i].get(); + } + + // Prefer version 1.1 if available. New devices launching with R are no longer allowed to + // implement just 1.0. + const char* descriptor_to_kill; + using DumpstateBoardTask = std::packaged_task<bool()>; + DumpstateBoardTask dumpstate_board_task; + sp<dumpstate_hal_hidl::IDumpstateDevice> dumpstate_hal( + dumpstate_hal_hidl::IDumpstateDevice::castFrom(dumpstate_hal_1_0)); + if (dumpstate_hal != nullptr) { + MYLOGI("Using IDumpstateDevice v1.1 HIDL HAL"); + + dumpstate_hal_hidl::DumpstateMode dumpstate_hal_mode = + GetDumpstateHalModeHidl(bugreport_mode); + + descriptor_to_kill = dumpstate_hal_hidl::IDumpstateDevice::descriptor; + dumpstate_board_task = + DumpstateBoardTask([timeout_sec, dumpstate_hal_mode, dumpstate_hal, &handle]() -> bool { + ::android::hardware::Return<dumpstate_hal_hidl::DumpstateStatus> status = + dumpstate_hal->dumpstateBoard_1_1(handle.get(), dumpstate_hal_mode, + SEC_TO_MSEC(timeout_sec)); + if (!status.isOk()) { + MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); + return false; + } else if (status != dumpstate_hal_hidl::DumpstateStatus::OK) { + MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n", + dumpstate_hal_hidl::toString(status).c_str()); + return false; + } + return true; + }); + } else { + MYLOGI("Using IDumpstateDevice v1.0 HIDL HAL"); + + descriptor_to_kill = dumpstate_hal_hidl_1_0::IDumpstateDevice::descriptor; + dumpstate_board_task = DumpstateBoardTask([dumpstate_hal_1_0, &handle]() -> bool { + ::android::hardware::Return<void> status = + dumpstate_hal_1_0->dumpstateBoard(handle.get()); + if (!status.isOk()) { + MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); + return false; + } + return true; + }); + } + auto result = dumpstate_board_task.get_future(); + std::thread(std::move(dumpstate_board_task)).detach(); + + if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { + MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate HAL\n", timeout_sec); + if (!android::base::SetProperty( + "ctl.interface_restart", + android::base::StringPrintf("%s/default", descriptor_to_kill))) { + MYLOGE("Couldn't restart dumpstate HAL\n"); + } + } + // Wait some time for init to kill dumpstate vendor HAL + constexpr size_t killing_timeout_sec = 10; + if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { + MYLOGE( + "killing dumpstateBoard timed out after %zus, continue and " + "there might be racing in content\n", + killing_timeout_sec); + } +} + +static void DoDumpstateBoardAidl( + const std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> dumpstate_hal, + const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds, + const Dumpstate::BugreportMode bugreport_mode, const size_t timeout_sec) { + MYLOGI("Using IDumpstateDevice AIDL HAL"); + + const char* descriptor_to_kill; + using DumpstateBoardTask = std::packaged_task<bool()>; + DumpstateBoardTask dumpstate_board_task; + dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode dumpstate_hal_mode = + GetDumpstateHalModeAidl(bugreport_mode); + + descriptor_to_kill = dumpstate_hal_aidl::IDumpstateDevice::descriptor; + dumpstate_board_task = DumpstateBoardTask([dumpstate_hal, &dumpstate_fds, dumpstate_hal_mode, + timeout_sec]() -> bool { + auto status = dumpstate_hal->dumpstateBoard(dumpstate_fds, dumpstate_hal_mode, timeout_sec); + + if (!status.isOk()) { + MYLOGE("dumpstateBoard failed: %s\n", status.getDescription().c_str()); + return false; + } + return true; + }); + auto result = dumpstate_board_task.get_future(); + std::thread(std::move(dumpstate_board_task)).detach(); + + if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { + MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate HAL\n", timeout_sec); + if (!android::base::SetProperty( + "ctl.interface_restart", + android::base::StringPrintf("%s/default", descriptor_to_kill))) { + MYLOGE("Couldn't restart dumpstate HAL\n"); + } + } + // Wait some time for init to kill dumpstate vendor HAL + constexpr size_t killing_timeout_sec = 10; + if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { + MYLOGE( + "killing dumpstateBoard timed out after %zus, continue and " + "there might be racing in content\n", + killing_timeout_sec); + } +} + +static std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> GetDumpstateBoardAidlService() { + const std::string aidl_instance_name = + std::string(dumpstate_hal_aidl::IDumpstateDevice::descriptor) + "/default"; + + if (!AServiceManager_isDeclared(aidl_instance_name.c_str())) { + return nullptr; + } + + ndk::SpAIBinder dumpstateBinder(AServiceManager_waitForService(aidl_instance_name.c_str())); + + return dumpstate_hal_aidl::IDumpstateDevice::fromBinder(dumpstateBinder); +} + 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 with ro.product.debugfs_restrictions.enabled * set to true and unmount it after invoking dumpstateBoard_* methods. @@ -2211,8 +2358,7 @@ void Dumpstate::DumpstateBoard(int out_fd) { 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); + RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"}, AS_ROOT_20); } std::vector<std::string> paths; @@ -2224,24 +2370,32 @@ void Dumpstate::DumpstateBoard(int out_fd) { std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i]))); } - sp<IDumpstateDevice_1_0> dumpstate_device_1_0(IDumpstateDevice_1_0::getService()); - if (dumpstate_device_1_0 == nullptr) { - MYLOGE("No IDumpstateDevice implementation\n"); - return; + // get dumpstate HAL AIDL implementation + std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> dumpstate_hal_handle_aidl( + GetDumpstateBoardAidlService()); + if (dumpstate_hal_handle_aidl == nullptr) { + MYLOGI("No IDumpstateDevice AIDL implementation\n"); } - using ScopedNativeHandle = - std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>; - ScopedNativeHandle handle(native_handle_create(static_cast<int>(paths.size()), 0), - [](native_handle_t* handle) { - native_handle_close(handle); - native_handle_delete(handle); - }); - if (handle == nullptr) { - MYLOGE("Could not create native_handle\n"); + // get dumpstate HAL HIDL implementation, only if AIDL HAL implementation not found + sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_handle_hidl_1_0 = nullptr; + if (dumpstate_hal_handle_aidl == nullptr) { + dumpstate_hal_handle_hidl_1_0 = dumpstate_hal_hidl_1_0::IDumpstateDevice::getService(); + if (dumpstate_hal_handle_hidl_1_0 == nullptr) { + MYLOGI("No IDumpstateDevice HIDL implementation\n"); + } + } + + // if neither HIDL nor AIDL implementation found, then return + if (dumpstate_hal_handle_hidl_1_0 == nullptr && dumpstate_hal_handle_aidl == nullptr) { + MYLOGE("Could not find IDumpstateDevice implementation\n"); return; } + // this is used to hold the file descriptors and when this variable goes out of scope + // the file descriptors are closed + std::vector<::ndk::ScopedFileDescriptor> dumpstate_fds; + // TODO(128270426): Check for consent in between? for (size_t i = 0; i < paths.size(); i++) { MYLOGI("Calling IDumpstateDevice implementation using path %s\n", paths[i].c_str()); @@ -2253,65 +2407,26 @@ void Dumpstate::DumpstateBoard(int out_fd) { MYLOGE("Could not open file %s: %s\n", paths[i].c_str(), strerror(errno)); return; } - handle.get()->data[i] = fd.release(); + + dumpstate_fds.emplace_back(fd.release()); + // we call fd.release() here to make sure "fd" does not get closed + // after "fd" goes out of scope after this block. + // "fd" will be closed when "dumpstate_fds" goes out of scope + // i.e. when we exit this function } // Given that bugreport is required to diagnose failures, it's better to set an arbitrary amount // of timeout for IDumpstateDevice than to block the rest of bugreport. In the timeout case, we // will kill the HAL and grab whatever it dumped in time. - constexpr size_t timeout_sec = 30; - // Prefer version 1.1 if available. New devices launching with R are no longer allowed to - // implement just 1.0. - const char* descriptor_to_kill; - using DumpstateBoardTask = std::packaged_task<bool()>; - DumpstateBoardTask dumpstate_board_task; - sp<IDumpstateDevice_1_1> dumpstate_device_1_1( - IDumpstateDevice_1_1::castFrom(dumpstate_device_1_0)); - if (dumpstate_device_1_1 != nullptr) { - MYLOGI("Using IDumpstateDevice v1.1"); - descriptor_to_kill = IDumpstateDevice_1_1::descriptor; - dumpstate_board_task = DumpstateBoardTask([this, dumpstate_device_1_1, &handle]() -> bool { - ::android::hardware::Return<DumpstateStatus> status = - dumpstate_device_1_1->dumpstateBoard_1_1(handle.get(), options_->dumpstate_hal_mode, - SEC_TO_MSEC(timeout_sec)); - if (!status.isOk()) { - MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); - return false; - } else if (status != DumpstateStatus::OK) { - MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n", toString(status).c_str()); - return false; - } - return true; - }); - } else { - MYLOGI("Using IDumpstateDevice v1.0"); - descriptor_to_kill = IDumpstateDevice_1_0::descriptor; - dumpstate_board_task = DumpstateBoardTask([dumpstate_device_1_0, &handle]() -> bool { - ::android::hardware::Return<void> status = - dumpstate_device_1_0->dumpstateBoard(handle.get()); - if (!status.isOk()) { - MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); - return false; - } - return true; - }); - } - auto result = dumpstate_board_task.get_future(); - std::thread(std::move(dumpstate_board_task)).detach(); + constexpr size_t timeout_sec = 45; - if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { - MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate vendor HAL\n", timeout_sec); - if (!android::base::SetProperty( - "ctl.interface_restart", - android::base::StringPrintf("%s/default", descriptor_to_kill))) { - MYLOGE("Couldn't restart dumpstate HAL\n"); - } - } - // Wait some time for init to kill dumpstate vendor HAL - constexpr size_t killing_timeout_sec = 10; - if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { - MYLOGE("killing dumpstateBoard timed out after %zus, continue and " - "there might be racing in content\n", killing_timeout_sec); + if (dumpstate_hal_handle_aidl != nullptr) { + DoDumpstateBoardAidl(dumpstate_hal_handle_aidl, dumpstate_fds, options_->bugreport_mode, + timeout_sec); + } else if (dumpstate_hal_handle_hidl_1_0 != nullptr) { + // run HIDL HAL only if AIDL HAL not found + DoDumpstateBoardHidl(dumpstate_hal_handle_hidl_1_0, dumpstate_fds, options_->bugreport_mode, + timeout_sec); } if (mount_debugfs) { @@ -2324,9 +2439,8 @@ void Dumpstate::DumpstateBoard(int out_fd) { auto file_sizes = std::make_unique<ssize_t[]>(paths.size()); for (size_t i = 0; i < paths.size(); i++) { struct stat s; - if (fstat(handle.get()->data[i], &s) == -1) { - MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(), - strerror(errno)); + if (fstat(dumpstate_fds[i].get(), &s) == -1) { + MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(), strerror(errno)); file_sizes[i] = -1; continue; } @@ -2565,40 +2679,35 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt bool is_screenshot_requested) { // Modify com.android.shell.BugreportProgressService#isDefaultScreenshotRequired as well for // default system screenshots. - options->bugreport_mode = ModeToString(mode); + options->bugreport_mode = mode; + options->bugreport_mode_string = ModeToString(mode); switch (mode) { case Dumpstate::BugreportMode::BUGREPORT_FULL: options->do_screenshot = is_screenshot_requested; - options->dumpstate_hal_mode = DumpstateMode::FULL; break; case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: // Currently, the dumpstate binder is only used by Shell to update progress. options->do_progress_updates = true; options->do_screenshot = is_screenshot_requested; - options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE; break; case Dumpstate::BugreportMode::BUGREPORT_REMOTE: options->do_vibrate = false; options->is_remote_mode = true; options->do_screenshot = false; - options->dumpstate_hal_mode = DumpstateMode::REMOTE; break; case Dumpstate::BugreportMode::BUGREPORT_WEAR: options->do_progress_updates = true; options->do_screenshot = is_screenshot_requested; - options->dumpstate_hal_mode = DumpstateMode::WEAR; break; // TODO(b/148168577) rename TELEPHONY everywhere to CONNECTIVITY. case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: options->telephony_only = true; options->do_progress_updates = true; options->do_screenshot = false; - options->dumpstate_hal_mode = DumpstateMode::CONNECTIVITY; break; case Dumpstate::BugreportMode::BUGREPORT_WIFI: options->wifi_only = true; options->do_screenshot = false; - options->dumpstate_hal_mode = DumpstateMode::WIFI; break; case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: break; @@ -2609,13 +2718,14 @@ static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI( "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 " + "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s " "limited_only: %d args: %s\n", options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket, options.do_screenshot, options.is_remote_mode, options.show_header_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()); + options.do_progress_updates, options.bugreport_fd.get(), + options.bugreport_mode_string.c_str(), + options.limited_only, options.args.c_str()); } void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, @@ -2623,8 +2733,8 @@ void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& screenshot_fd_in, bool is_screenshot_requested) { // 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())); + bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0)); + screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0)); SetOptionsFromMode(bugreport_mode, this, is_screenshot_requested); } @@ -2795,10 +2905,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, version_ = VERSION_CURRENT; } - if (version_ != VERSION_CURRENT && version_ != VERSION_SPLIT_ANR) { - MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n", - version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(), - VERSION_SPLIT_ANR.c_str()); + if (version_ != VERSION_CURRENT) { + MYLOGE("invalid version requested ('%s'); supported values are: ('%s', '%s')\n", + version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str()); return RunStatus::INVALID_INPUT; } @@ -2829,7 +2938,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n", - id_, options_->args.c_str(), options_->bugreport_mode.c_str(), version_.c_str()); + id_, options_->args.c_str(), options_->bugreport_mode_string.c_str(), version_.c_str()); do_early_screenshot_ = options_->do_progress_updates; @@ -3124,8 +3233,7 @@ void Dumpstate::EnableParallelRunIfNeeded() { 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); @@ -3405,10 +3513,6 @@ void Progress::Dump(int fd, const std::string& prefix) const { dprintf(fd, "%saverage_max: %d\n", pr, average_max_); } -bool Dumpstate::IsZipping() const { - return zip_writer_ != nullptr; -} - std::string Dumpstate::GetPath(const std::string& suffix) const { return GetPath(bugreport_internal_dir_, suffix); } @@ -4054,10 +4158,6 @@ void dump_frozen_cgroupfs(const char *dir, int level, } void dump_frozen_cgroupfs() { - if (!ds.IsZipping()) { - MYLOGD("Not adding cgroupfs because it's not a zipped bugreport\n"); - return; - } MYLOGD("Adding frozen processes from %s\n", CGROUPFS_DIR); DurationReporter duration_reporter("FROZEN CGROUPFS"); if (PropertiesHelper::IsDryRun()) return; diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 34280d01e8..ee6b1aee18 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -25,6 +25,7 @@ #include <string> #include <vector> +#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> #include <android-base/macros.h> #include <android-base/unique_fd.h> #include <android/hardware/dumpstate/1.1/types.h> @@ -38,11 +39,6 @@ #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> -// TODO: remove once not used -#define MAX_ARGS_ARRAY_SIZE 1000 - // TODO: move everything under this namespace // TODO: and then remove explicitly android::os::dumpstate:: prefixes namespace android { @@ -161,12 +157,6 @@ class Progress { static std::string VERSION_CURRENT = "2.0"; /* - * Temporary version that adds a anr-traces.txt entry. Once tools support it, the current version - * will be bumped to 3.0. - */ -static std::string VERSION_SPLIT_ANR = "3.0-dev-split-anr"; - -/* * "Alias" for the current version. */ static std::string VERSION_DEFAULT = "default"; @@ -218,9 +208,6 @@ class Dumpstate { static Dumpstate& GetInstance(); - /* Checkes whether dumpstate is generating a zipped bugreport. */ - bool IsZipping() const; - /* Initialize dumpstate fields before starting bugreport generation */ void Initialize(); @@ -405,19 +392,18 @@ class Dumpstate { bool limited_only = false; // Whether progress updates should be published. bool do_progress_updates = false; - // The mode we'll use when calling IDumpstateDevice::dumpstateBoard. + // this is used to derive dumpstate HAL bug report mode // TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead. // The HAL is actually an API surface that can be validated, while the AIDL is not (@hide). - ::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode = - ::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT; + BugreportMode bugreport_mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT; // File descriptor to output zip file. Takes precedence over out_dir. android::base::unique_fd bugreport_fd; // File descriptor to screenshot file. android::base::unique_fd screenshot_fd; // Custom output directory. std::string out_dir; - // Bugreport mode of the bugreport. - std::string bugreport_mode; + // Bugreport mode of the bugreport as a string + std::string bugreport_mode_string; // Command-line arguments as string std::string args; // Notification title and description @@ -492,7 +478,7 @@ class Dumpstate { // 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 db508b52bd..70b4e5c0d8 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -33,6 +33,7 @@ #include <unistd.h> #include <thread> +#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> @@ -47,6 +48,7 @@ namespace android { namespace os { namespace dumpstate { +using DumpstateDeviceAidl = ::aidl::android::hardware::dumpstate::IDumpstateDevice; using ::android::hardware::dumpstate::V1_1::DumpstateMode; using ::testing::EndsWith; using ::testing::Eq; @@ -186,7 +188,6 @@ TEST_F(DumpOptionsTest, InitializeNone) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeAdbBugreport) { @@ -210,7 +211,6 @@ TEST_F(DumpOptionsTest, InitializeAdbBugreport) { EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.stream_to_socket); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { @@ -234,13 +234,11 @@ TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeFullBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true); EXPECT_TRUE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -256,7 +254,6 @@ TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true); EXPECT_TRUE(options_.do_progress_updates); EXPECT_TRUE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -272,7 +269,6 @@ TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { 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_.progress_updates_to_socket); @@ -286,7 +282,7 @@ TEST_F(DumpOptionsTest, InitializeWearBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true); EXPECT_TRUE(options_.do_screenshot); EXPECT_TRUE(options_.do_progress_updates); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR); + // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -302,7 +298,6 @@ TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { EXPECT_FALSE(options_.do_screenshot); 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); @@ -317,7 +312,6 @@ TEST_F(DumpOptionsTest, InitializeWifiBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false); EXPECT_FALSE(options_.do_screenshot); EXPECT_TRUE(options_.wifi_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -354,7 +348,6 @@ TEST_F(DumpOptionsTest, InitializeLimitedOnlyBugreport) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.stream_to_socket); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { @@ -371,7 +364,6 @@ TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -408,7 +400,6 @@ TEST_F(DumpOptionsTest, InitializePartial1) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializePartial2) { @@ -436,7 +427,6 @@ TEST_F(DumpOptionsTest, InitializePartial2) { 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); } TEST_F(DumpOptionsTest, InitializeHelp) { @@ -1730,14 +1720,13 @@ TEST_F(DumpPoolTest, EnqueueTaskWithFd) { 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); @@ -1751,9 +1740,8 @@ TEST_F(DumpPoolTest, EnqueueTask_withDurationLog) { 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); @@ -1762,27 +1750,6 @@ TEST_F(DumpPoolTest, EnqueueTask_withDurationLog) { 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/dumpsys/Android.bp b/cmds/dumpsys/Android.bp index 6ab6b7f951..ceb16cb989 100644 --- a/cmds/dumpsys/Android.bp +++ b/cmds/dumpsys/Android.bp @@ -64,6 +64,10 @@ cc_binary { srcs: [ "main.cpp", ], + + shared_libs: [ + "packagemanager_aidl-cpp", + ], } cc_binary { diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index ba1c449dbf..3d2bdf1d6f 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -21,9 +21,12 @@ #include <android-base/file.h> #include <android-base/stringprintf.h> +#include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <binder/BpBinder.h> #include <binder/Parcel.h> #include <binder/ProcessState.h> +#include <binder/Stability.h> #include <binder/TextOutput.h> #include <binderdebug/BinderDebug.h> #include <serviceutils/PriorityDumper.h> @@ -57,25 +60,30 @@ static int sort_func(const String16* lhs, const String16* rhs) } static void usage() { - fprintf(stderr, - "usage: dumpsys\n" - " To dump all services.\n" - "or:\n" - " dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--thread] [--help | -l | " - "--skip SERVICES " - "| SERVICE [ARGS]]\n" - " --help: shows this help\n" - " -l: only list services, do not dump them\n" - " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n" - " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n" - " --pid: dump PID instead of usual dump\n" - " --thread: dump thread usage instead of usual dump\n" - " --proto: filter services that support dumping data in proto format. Dumps\n" - " will be in proto format.\n" - " --priority LEVEL: filter services based on specified priority\n" - " LEVEL must be one of CRITICAL | HIGH | NORMAL\n" - " --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n" - " SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n"); + fprintf( + stderr, + "usage: dumpsys\n" + " To dump all services.\n" + "or:\n" + " dumpsys [-t TIMEOUT] [--priority LEVEL] [--clients] [--dump] [--pid] [--thread] " + "[--help | " + "-l | --skip SERVICES " + "| SERVICE [ARGS]]\n" + " --help: shows this help\n" + " -l: only list services, do not dump them\n" + " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n" + " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n" + " --clients: dump client PIDs instead of usual dump\n" + " --dump: ask the service to dump itself (this is the default)\n" + " --pid: dump PID instead of usual dump\n" + " --proto: filter services that support dumping data in proto format. Dumps\n" + " will be in proto format.\n" + " --priority LEVEL: filter services based on specified priority\n" + " LEVEL must be one of CRITICAL | HIGH | NORMAL\n" + " --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n" + " --stability: dump binder stability information instead of usual dump\n" + " --thread: dump thread usage instead of usual dump\n" + " SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n"); } static bool IsSkipped(const Vector<String16>& skipped, const String16& service) { @@ -125,16 +133,15 @@ int Dumpsys::main(int argc, char* const argv[]) { bool showListOnly = false; bool skipServices = false; bool asProto = false; - Type type = Type::DUMP; + int dumpTypeFlags = 0; int timeoutArgMs = 10000; int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL; - static struct option longOptions[] = {{"thread", no_argument, 0, 0}, - {"pid", no_argument, 0, 0}, - {"priority", required_argument, 0, 0}, - {"proto", no_argument, 0, 0}, - {"skip", no_argument, 0, 0}, - {"help", no_argument, 0, 0}, - {0, 0, 0, 0}}; + static struct option longOptions[] = { + {"help", no_argument, 0, 0}, {"clients", no_argument, 0, 0}, + {"dump", no_argument, 0, 0}, {"pid", no_argument, 0, 0}, + {"priority", required_argument, 0, 0}, {"proto", no_argument, 0, 0}, + {"skip", no_argument, 0, 0}, {"stability", no_argument, 0, 0}, + {"thread", no_argument, 0, 0}, {0, 0, 0, 0}}; // Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but // happens on test cases). @@ -165,10 +172,16 @@ int Dumpsys::main(int argc, char* const argv[]) { usage(); return -1; } + } else if (!strcmp(longOptions[optionIndex].name, "dump")) { + dumpTypeFlags |= TYPE_DUMP; } else if (!strcmp(longOptions[optionIndex].name, "pid")) { - type = Type::PID; + dumpTypeFlags |= TYPE_PID; + } else if (!strcmp(longOptions[optionIndex].name, "stability")) { + dumpTypeFlags |= TYPE_STABILITY; } else if (!strcmp(longOptions[optionIndex].name, "thread")) { - type = Type::THREAD; + dumpTypeFlags |= TYPE_THREAD; + } else if (!strcmp(longOptions[optionIndex].name, "clients")) { + dumpTypeFlags |= TYPE_CLIENTS; } break; @@ -206,6 +219,10 @@ int Dumpsys::main(int argc, char* const argv[]) { } } + if (dumpTypeFlags == 0) { + dumpTypeFlags = TYPE_DUMP; + } + for (int i = optind; i < argc; i++) { if (skipServices) { skippedServices.add(String16(argv[i])); @@ -258,7 +275,7 @@ int Dumpsys::main(int argc, char* const argv[]) { const String16& serviceName = services[i]; if (IsSkipped(skippedServices, serviceName)) continue; - if (startDumpThread(type, serviceName, args) == OK) { + if (startDumpThread(dumpTypeFlags, serviceName, args) == OK) { bool addSeparator = (N > 1); if (addSeparator) { writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags); @@ -325,16 +342,24 @@ void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityF } } -static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd) { +static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd, bool exclusive) { pid_t pid; status_t status = service->getDebugPid(&pid); if (status != OK) { return status; } + if (!exclusive) { + WriteStringToFd("Service host process PID: ", fd.get()); + } WriteStringToFd(std::to_string(pid) + "\n", fd.get()); return OK; } +static status_t dumpStabilityToFd(const sp<IBinder>& service, const unique_fd& fd) { + WriteStringToFd("Stability: " + internal::Stability::debugToString(service) + "\n", fd); + return OK; +} + static status_t dumpThreadsToFd(const sp<IBinder>& service, const unique_fd& fd) { pid_t pid; status_t status = service->getDebugPid(&pid); @@ -352,7 +377,43 @@ static status_t dumpThreadsToFd(const sp<IBinder>& service, const unique_fd& fd) return OK; } -status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, +static status_t dumpClientsToFd(const sp<IBinder>& service, const unique_fd& fd) { + std::string clientPids; + const auto remoteBinder = service->remoteBinder(); + if (remoteBinder == nullptr) { + WriteStringToFd("Client PIDs are not available for local binders.\n", fd.get()); + return OK; + } + const auto handle = remoteBinder->getDebugBinderHandle(); + if (handle == std::nullopt) { + return OK; + } + std::vector<pid_t> pids; + pid_t myPid = getpid(); + pid_t servicePid; + status_t status = service->getDebugPid(&servicePid); + if (status != OK) { + return status; + } + status = + getBinderClientPids(BinderDebugContext::BINDER, myPid, servicePid, handle.value(), &pids); + if (status != OK) { + return status; + } + pids.erase(std::remove_if(pids.begin(), pids.end(), [&](pid_t pid) { return pid == myPid; }), + pids.end()); + WriteStringToFd("Client PIDs: " + ::android::base::Join(pids, ", ") + "\n", fd.get()); + return OK; +} + +static void reportDumpError(const String16& serviceName, status_t error, const char* context) { + if (error == OK) return; + + std::cerr << "Error with service '" << serviceName << "' while " << context << ": " + << statusToString(error) << std::endl; +} + +status_t Dumpsys::startDumpThread(int dumpTypeFlags, const String16& serviceName, const Vector<String16>& args) { sp<IBinder> service = sm_->checkService(serviceName); if (service == nullptr) { @@ -373,26 +434,27 @@ status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, // dump blocks until completion, so spawn a thread.. activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable { - status_t err = 0; - - switch (type) { - case Type::DUMP: - err = service->dump(remote_end.get(), args); - break; - case Type::PID: - err = dumpPidToFd(service, remote_end); - break; - case Type::THREAD: - err = dumpThreadsToFd(service, remote_end); - break; - default: - std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl; - return; + if (dumpTypeFlags & TYPE_PID) { + status_t err = dumpPidToFd(service, remote_end, dumpTypeFlags == TYPE_PID); + reportDumpError(serviceName, err, "dumping PID"); + } + if (dumpTypeFlags & TYPE_STABILITY) { + status_t err = dumpStabilityToFd(service, remote_end); + reportDumpError(serviceName, err, "dumping stability"); + } + if (dumpTypeFlags & TYPE_THREAD) { + status_t err = dumpThreadsToFd(service, remote_end); + reportDumpError(serviceName, err, "dumping thread info"); + } + if (dumpTypeFlags & TYPE_CLIENTS) { + status_t err = dumpClientsToFd(service, remote_end); + reportDumpError(serviceName, err, "dumping clients info"); } - if (err != OK) { - std::cerr << "Error dumping service info status_t: " << statusToString(err) << " " - << serviceName << std::endl; + // other types always act as a header, this is usually longer + if (dumpTypeFlags & TYPE_DUMP) { + status_t err = service->dump(remote_end.get(), args); + reportDumpError(serviceName, err, "dumping"); } }); return OK; diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h index 349947ce12..6ab1a7dcb5 100644 --- a/cmds/dumpsys/dumpsys.h +++ b/cmds/dumpsys/dumpsys.h @@ -51,23 +51,26 @@ class Dumpsys { */ static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags); - enum class Type { - DUMP, // dump using `dump` function - PID, // dump pid of server only - THREAD, // dump thread usage of server only + enum Type { + TYPE_DUMP = 0x1, // dump using `dump` function + TYPE_PID = 0x2, // dump pid of server only + TYPE_STABILITY = 0x4, // dump stability information of server + TYPE_THREAD = 0x8, // dump thread usage of server only + TYPE_CLIENTS = 0x10, // dump pid of clients }; /** * Starts a thread to connect to a service and get its dump output. The thread redirects * the output to a pipe. Thread must be stopped by a subsequent call to {@code * stopDumpThread}. + * @param dumpTypeFlags operations to perform * @param serviceName * @param args list of arguments to pass to service dump method. * @return {@code OK} thread is started successfully. * {@code NAME_NOT_FOUND} service could not be found. * {@code != OK} error */ - status_t startDumpThread(Type type, const String16& serviceName, + status_t startDumpThread(int dumpTypeFlags, const String16& serviceName, const Vector<String16>& args); /** diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index c9d2dbb883..f0c19b93ec 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -60,6 +60,12 @@ class ServiceManagerMock : public IServiceManager { MOCK_METHOD1(isDeclared, bool(const String16&)); MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&)); MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&)); + MOCK_METHOD1(getConnectionInfo, std::optional<ConnectionInfo>(const String16&)); + MOCK_METHOD2(registerForNotifications, status_t(const String16&, + const sp<LocalRegistrationCallback>&)); + MOCK_METHOD2(unregisterForNotifications, status_t(const String16&, + const sp<LocalRegistrationCallback>&)); + MOCK_METHOD0(getServiceDebugInfo, std::vector<ServiceDebugInfo>()); protected: MOCK_METHOD0(onAsBinder, IBinder*()); }; @@ -200,7 +206,7 @@ class DumpsysTest : public Test { CaptureStdout(); CaptureStderr(); dump_.setServiceArgs(args, supportsProto, priorityFlags); - status_t status = dump_.startDumpThread(Dumpsys::Type::DUMP, serviceName, args); + status_t status = dump_.startDumpThread(Dumpsys::TYPE_DUMP, serviceName, args); EXPECT_THAT(status, Eq(0)); status = dump_.writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(500), false, elapsedDuration, bytesWritten); @@ -582,6 +588,27 @@ TEST_F(DumpsysTest, ListServiceWithPid) { AssertOutput(std::to_string(getpid()) + "\n"); } +// Tests 'dumpsys --stability' +TEST_F(DumpsysTest, ListAllServicesWithStability) { + ExpectListServices({"Locksmith", "Valet"}); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + + CallMain({"--stability"}); + + AssertRunningServices({"Locksmith", "Valet"}); + AssertOutputContains("stability"); +} + +// Tests 'dumpsys --stability service_name' +TEST_F(DumpsysTest, ListServiceWithStability) { + ExpectCheckService("Locksmith"); + + CallMain({"--stability", "Locksmith"}); + + AssertOutputContains("stability"); +} + // Tests 'dumpsys --thread' TEST_F(DumpsysTest, ListAllServicesWithThread) { ExpectListServices({"Locksmith", "Valet"}); @@ -606,6 +633,51 @@ TEST_F(DumpsysTest, ListServiceWithThread) { AssertOutputFormat(format); } +// Tests 'dumpsys --clients' +TEST_F(DumpsysTest, ListAllServicesWithClients) { + ExpectListServices({"Locksmith", "Valet"}); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + + CallMain({"--clients"}); + + AssertRunningServices({"Locksmith", "Valet"}); + + const std::string format("(.|\n)*((Client PIDs are not available for local binders.)(.|\n)*){2}"); + AssertOutputFormat(format); +} + +// Tests 'dumpsys --clients service_name' +TEST_F(DumpsysTest, ListServiceWithClients) { + ExpectCheckService("Locksmith"); + + CallMain({"--clients", "Locksmith"}); + + const std::string format("Client PIDs are not available for local binders.\n"); + AssertOutputFormat(format); +} +// Tests 'dumpsys --thread --stability' +TEST_F(DumpsysTest, ListAllServicesWithMultipleOptions) { + ExpectListServices({"Locksmith", "Valet"}); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + + CallMain({"--pid", "--stability"}); + AssertRunningServices({"Locksmith", "Valet"}); + + AssertOutputContains(std::to_string(getpid())); + AssertOutputContains("stability"); +} + +// Tests 'dumpsys --pid --stability service_name' +TEST_F(DumpsysTest, ListServiceWithMultipleOptions) { + ExpectCheckService("Locksmith"); + CallMain({"--pid", "--stability", "Locksmith"}); + + AssertOutputContains(std::to_string(getpid())); + AssertOutputContains("stability"); +} + TEST_F(DumpsysTest, GetBytesWritten) { const char* serviceName = "service2"; const char* dumpContents = "dump1"; diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp index ec3bc616ad..c18d3f5021 100644 --- a/cmds/idlcli/Android.bp +++ b/cmds/idlcli/Android.bp @@ -24,7 +24,7 @@ package { cc_defaults { name: "idlcli-defaults", shared_libs: [ - "android.hardware.vibrator-V2-ndk_platform", + "android.hardware.vibrator-V2-ndk", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 3f180d94a7..c9f680b266 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -28,6 +28,7 @@ cc_defaults { "dexopt.cpp", "execv_helper.cpp", "globals.cpp", + "restorable_file.cpp", "run_dex2oat.cpp", "unique_file.cpp", "utils.cpp", @@ -45,10 +46,12 @@ cc_defaults { "libprocessgroup", "libselinux", "libutils", + "libziparchive", "server_configurable_flags", ], static_libs: [ "libasync_safe", + "libext2_uuid", ], export_shared_lib_headers: [ "libbinder", @@ -79,7 +82,7 @@ cc_defaults { "-cert-err58-cpp", ], tidy_flags: [ - "-warnings-as-errors=clang-analyzer-security*,cert-*" + "-warnings-as-errors=clang-analyzer-security*,cert-*", ], } @@ -131,7 +134,10 @@ cc_test_host { "unique_file.cpp", "execv_helper.cpp", ], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "server_configurable_flags", @@ -169,7 +175,7 @@ cc_binary { // Needs to be wherever installd is as it's execed by // installd. - required: [ "migrate_legacy_obb_data.sh" ], + required: ["migrate_legacy_obb_data.sh"], } // OTA chroot tool @@ -193,7 +199,7 @@ cc_binary { "libutils", ], required: [ - "apexd" + "apexd", ], } @@ -212,7 +218,7 @@ cc_library_static { name: "libotapreoptparameters", cflags: [ "-Wall", - "-Werror" + "-Werror", ], srcs: ["otapreopt_parameters.cpp"], @@ -236,7 +242,7 @@ cc_binary { name: "otapreopt", cflags: [ "-Wall", - "-Werror" + "-Werror", ], srcs: [ @@ -245,6 +251,7 @@ cc_binary { "globals.cpp", "otapreopt.cpp", "otapreopt_utils.cpp", + "restorable_file.cpp", "run_dex2oat.cpp", "unique_file.cpp", "utils.cpp", @@ -256,6 +263,7 @@ cc_binary { "libasync_safe", "libdiskusage", "libotapreoptparameters", + "libext2_uuid", ], shared_libs: [ @@ -267,6 +275,7 @@ cc_binary { "libprocessgroup", "libselinux", "libutils", + "libziparchive", "server_configurable_flags", ], } @@ -294,5 +303,5 @@ sh_binary { // Script to migrate legacy obb data. sh_binary { name: "migrate_legacy_obb_data.sh", - src: "migrate_legacy_obb_data.sh" + src: "migrate_legacy_obb_data.sh", } diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp index e29ff4c248..27690a3039 100644 --- a/cmds/installd/CacheItem.cpp +++ b/cmds/installd/CacheItem.cpp @@ -116,6 +116,7 @@ int CacheItem::purge() { break; } } + fts_close(fts); } else { if (tombstone) { if (truncate(path.c_str(), 0) != 0) { diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index b84be9b1e6..a49f563060 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -18,13 +18,10 @@ #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER -#include <algorithm> #include <errno.h> -#include <fstream> #include <fts.h> -#include <functional> #include <inttypes.h> -#include <regex> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/capability.h> @@ -39,6 +36,12 @@ #include <sys/wait.h> #include <sys/xattr.h> #include <unistd.h> +#include <algorithm> +#include <filesystem> +#include <fstream> +#include <functional> +#include <regex> +#include <unordered_set> #include <android-base/file.h> #include <android-base/logging.h> @@ -52,6 +55,7 @@ #include <cutils/fs.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> +#include <linux/quota.h> #include <log/log.h> // TODO: Move everything to base/logging. #include <logwrap/logwrap.h> #include <private/android_filesystem_config.h> @@ -76,7 +80,10 @@ #define LOG_TAG "installd" #endif +#define GRANULAR_LOCKS + using android::base::ParseUint; +using android::base::Split; using android::base::StringPrintf; using std::endl; @@ -100,11 +107,6 @@ static constexpr const char* PKG_LIB_POSTFIX = "/lib"; static constexpr const char* CACHE_DIR_POSTFIX = "/cache"; static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache"; -// fsverity assumes the page size is always 4096. If not, the feature can not be -// enabled. -static constexpr int kVerityPageSize = 4096; -static constexpr size_t kSha256Size = 32; -static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode"; static constexpr const char* kFuseProp = "persist.sys.fuse"; /** @@ -116,6 +118,12 @@ static constexpr const char* kMntFuse = "/mnt/pass_through/0/"; static std::atomic<bool> sAppDataIsolationEnabled(false); +/** + * Flag to control if project ids are supported for internal storage + */ +static std::atomic<bool> sUsingProjectIdsFlag(false); +static std::once_flag flag; + namespace { constexpr const char* kDump = "android.permission.DUMP"; @@ -258,11 +266,103 @@ binder::Status checkArgumentPath(const std::optional<std::string>& path) { } \ } -#define ASSERT_PAGE_SIZE_4K() { \ - if (getpagesize() != kVerityPageSize) { \ - return error("FSVerity only supports 4K pages"); \ - } \ -} +#ifdef GRANULAR_LOCKS + +/** + * This class obtains in constructor and keeps the local strong pointer to the RefLock. + * On destruction, it checks if there are any other strong pointers, and remove the map entry if + * this was the last one. + */ +template <class Key, class Mutex> +struct LocalLockHolder { + using WeakPointer = std::weak_ptr<Mutex>; + using StrongPointer = std::shared_ptr<Mutex>; + using Map = std::unordered_map<Key, WeakPointer>; + using MapLock = std::recursive_mutex; + + LocalLockHolder(Key key, Map& map, MapLock& mapLock) + : mKey(std::move(key)), mMap(map), mMapLock(mapLock) { + std::lock_guard lock(mMapLock); + auto& weakPtr = mMap[mKey]; + + // Check if the RefLock is still alive. + mRefLock = weakPtr.lock(); + if (!mRefLock) { + // Create a new lock. + mRefLock = std::make_shared<Mutex>(); + weakPtr = mRefLock; + } + } + LocalLockHolder(LocalLockHolder&& other) noexcept + : mKey(std::move(other.mKey)), + mMap(other.mMap), + mMapLock(other.mMapLock), + mRefLock(std::move(other.mRefLock)) { + other.mRefLock.reset(); + } + ~LocalLockHolder() { + if (!mRefLock) { + return; + } + + std::lock_guard lock(mMapLock); + // Clear the strong pointer. + mRefLock.reset(); + auto found = mMap.find(mKey); + if (found == mMap.end()) { + return; + } + const auto& weakPtr = found->second; + // If this was the last pointer then it's ok to remove the map entry. + if (weakPtr.expired()) { + mMap.erase(found); + } + } + + void lock() { mRefLock->lock(); } + void unlock() { mRefLock->unlock(); } + void lock_shared() { mRefLock->lock_shared(); } + void unlock_shared() { mRefLock->unlock_shared(); } + +private: + Key mKey; + Map& mMap; + MapLock& mMapLock; + StrongPointer mRefLock; +}; + +using UserLock = LocalLockHolder<userid_t, std::shared_mutex>; +using UserWriteLockGuard = std::unique_lock<UserLock>; +using UserReadLockGuard = std::shared_lock<UserLock>; + +using PackageLock = LocalLockHolder<std::string, std::recursive_mutex>; +using PackageLockGuard = std::lock_guard<PackageLock>; + +#define LOCK_USER() \ + UserLock localUserLock(userId, mUserIdLock, mLock); \ + UserWriteLockGuard userLock(localUserLock) + +#define LOCK_USER_READ() \ + UserLock localUserLock(userId, mUserIdLock, mLock); \ + UserReadLockGuard userLock(localUserLock) + +#define LOCK_PACKAGE() \ + PackageLock localPackageLock(packageName, mPackageNameLock, mLock); \ + PackageLockGuard packageLock(localPackageLock) + +#define LOCK_PACKAGE_USER() \ + LOCK_USER_READ(); \ + LOCK_PACKAGE() + +#else + +#define LOCK_USER() std::lock_guard lock(mLock) +#define LOCK_PACKAGE() std::lock_guard lock(mLock) +#define LOCK_PACKAGE_USER() \ + (void)userId; \ + std::lock_guard lock(mLock) + +#endif // GRANULAR_LOCKS } // namespace @@ -281,34 +381,29 @@ status_t InstalldNativeService::start() { } status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */) { - auto out = std::fstream(StringPrintf("/proc/self/fd/%d", fd)); const binder::Status dump_permission = checkPermission(kDump); if (!dump_permission.isOk()) { - out << dump_permission.toString8() << endl; + dprintf(fd, "%s\n", dump_permission.toString8().c_str()); return PERMISSION_DENIED; } - std::lock_guard<std::recursive_mutex> lock(mLock); - - out << "installd is happy!" << endl; { std::lock_guard<std::recursive_mutex> lock(mMountsLock); - out << endl << "Storage mounts:" << endl; + dprintf(fd, "Storage mounts:\n"); for (const auto& n : mStorageMounts) { - out << " " << n.first << " = " << n.second << endl; + dprintf(fd, " %s = %s\n", n.first.c_str(), n.second.c_str()); } } { std::lock_guard<std::recursive_mutex> lock(mQuotasLock); - out << endl << "Per-UID cache quotas:" << endl; + dprintf(fd, "Per-UID cache quotas:\n"); for (const auto& n : mCacheQuotas) { - out << " " << n.first << " = " << n.second << endl; + dprintf(fd, " %d = %" PRId64 "\n", n.first, n.second); } } - out << endl; - out.flush(); + dprintf(fd, "is_dexopt_blocked:%d\n", android::installd::is_dexopt_blocked()); return NO_ERROR; } @@ -323,10 +418,17 @@ static int restorecon_app_data_lazy(const std::string& path, const std::string& int res = 0; char* before = nullptr; char* after = nullptr; + if (!existing) { + if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, + SELINUX_ANDROID_RESTORECON_RECURSE) < 0) { + PLOG(ERROR) << "Failed recursive restorecon for " << path; + goto fail; + } + return res; + } // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by // libselinux. Not needed here. - if (lgetfilecon(path.c_str(), &before) < 0) { PLOG(ERROR) << "Failed before getfilecon for " << path; goto fail; @@ -362,21 +464,41 @@ done: free(after); return res; } - -static int restorecon_app_data_lazy(const std::string& parent, const char* name, - const std::string& seInfo, uid_t uid, bool existing) { - return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seInfo, uid, - existing); +static bool internal_storage_has_project_id() { + // The following path is populated in setFirstBoot, so if this file is present + // then project ids can be used. Using call once to cache the result of this check + // to avoid having to check the file presence again and again. + std::call_once(flag, []() { + auto using_project_ids = + StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); + sUsingProjectIdsFlag = access(using_project_ids.c_str(), F_OK) == 0; + }); + // return sUsingProjectIdsFlag; + return false; } -static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) { - if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) { +static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid, + long project_id) { + if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) { PLOG(ERROR) << "Failed to prepare " << path; return -1; } + if (internal_storage_has_project_id()) { + return set_quota_project_id(path, project_id, true); + } return 0; } +static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, + uid_t uid, gid_t gid, long project_id) { + auto path = StringPrintf("%s/%s", parent.c_str(), name); + int ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid); + if (ret == 0 && internal_storage_has_project_id()) { + return set_quota_project_id(path, project_id, true); + } + return ret; +} + static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) { if (!property_get_bool("dalvik.vm.usejitprofiles", false)) { return true; @@ -424,13 +546,147 @@ static bool prepare_app_profile_dir(const std::string& packageName, int32_t appI return true; } -binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid, - const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, - const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { +static bool chown_app_dir(const std::string& path, uid_t uid, uid_t previousUid, gid_t cacheGid) { + FTS* fts; + char *argv[] = { (char*) path.c_str(), nullptr }; + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { + return false; + } + for (FTSENT* p; (p = fts_read(fts)) != nullptr;) { + if (p->fts_info == FTS_D && p->fts_level == 1 + && (strcmp(p->fts_name, "cache") == 0 + || strcmp(p->fts_name, "code_cache") == 0)) { + // Mark cache dirs + p->fts_number = 1; + } else { + // Inherit parent's number + p->fts_number = p->fts_parent->fts_number; + } + + switch (p->fts_info) { + case FTS_D: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + if (p->fts_statp->st_uid == previousUid) { + if (lchown(p->fts_path, uid, p->fts_number ? cacheGid : uid) != 0) { + PLOG(WARNING) << "Failed to lchown " << p->fts_path; + } + } else { + LOG(WARNING) << "Ignoring " << p->fts_path << " with unexpected UID " + << p->fts_statp->st_uid << " instead of " << previousUid; + } + break; + } + } + fts_close(fts); + return true; +} + +static void chown_app_profile_dir(const std::string &packageName, int32_t appId, int32_t userId) { + uid_t uid = multiuser_get_uid(userId, appId); + gid_t sharedGid = multiuser_get_shared_gid(userId, appId); + + const std::string profile_dir = + create_primary_current_profile_package_dir_path(userId, packageName); + char *argv[] = { (char*) profile_dir.c_str(), nullptr }; + if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) { + for (FTSENT* p; (p = fts_read(fts)) != nullptr;) { + switch (p->fts_info) { + case FTS_D: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + if (lchown(p->fts_path, uid, uid) != 0) { + PLOG(WARNING) << "Failed to lchown " << p->fts_path; + } + break; + } + } + fts_close(fts); + } + + const std::string ref_profile_path = + create_primary_reference_profile_package_dir_path(packageName); + argv[0] = (char *) ref_profile_path.c_str(); + if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) { + for (FTSENT* p; (p = fts_read(fts)) != nullptr;) { + if (p->fts_info == FTS_D && p->fts_level == 0) { + if (chown(p->fts_path, AID_SYSTEM, sharedGid) != 0) { + PLOG(WARNING) << "Failed to chown " << p->fts_path; + } + continue; + } + switch (p->fts_info) { + case FTS_D: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + if (lchown(p->fts_path, sharedGid, sharedGid) != 0) { + PLOG(WARNING) << "Failed to lchown " << p->fts_path; + } + break; + } + } + fts_close(fts); + } +} + +static binder::Status createAppDataDirs(const std::string& path, int32_t uid, int32_t gid, + int32_t previousUid, int32_t cacheGid, + const std::string& seInfo, mode_t targetMode, + long projectIdApp, long projectIdCache) { + struct stat st{}; + bool parent_dir_exists = (stat(path.c_str(), &st) == 0); + + auto cache_path = StringPrintf("%s/%s", path.c_str(), "cache"); + auto code_cache_path = StringPrintf("%s/%s", path.c_str(), "code_cache"); + bool cache_exists = (access(cache_path.c_str(), F_OK) == 0); + bool code_cache_exists = (access(code_cache_path.c_str(), F_OK) == 0); + + if (parent_dir_exists) { + if (previousUid > 0 && previousUid != uid) { + if (!chown_app_dir(path, uid, previousUid, cacheGid)) { + return error("Failed to chown " + path); + } + } + } + + // Prepare only the parent app directory + if (prepare_app_dir(path, targetMode, uid, gid, projectIdApp) || + prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid, projectIdCache) || + prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid, projectIdCache)) { + return error("Failed to prepare " + path); + } + + // Consider restorecon over contents if label changed + if (restorecon_app_data_lazy(path, seInfo, uid, parent_dir_exists)) { + return error("Failed to restorecon " + path); + } + + // If the parent dir exists, the restorecon would already have been done + // as a part of the recursive restorecon above + if (parent_dir_exists && !cache_exists + && restorecon_app_data_lazy(cache_path, seInfo, uid, false)) { + return error("Failed to restorecon " + cache_path); + } + + // If the parent dir exists, the restorecon would already have been done + // as a part of the recursive restorecon above + if (parent_dir_exists && !code_cache_exists + && restorecon_app_data_lazy(code_cache_path, seInfo, uid, false)) { + return error("Failed to restorecon " + code_cache_path); + } + return ok(); +} + +binder::Status InstalldNativeService::createAppDataLocked( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, + int32_t targetSdkVersion, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -439,6 +695,11 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str if (_aidl_return != nullptr) *_aidl_return = -1; int32_t uid = multiuser_get_uid(userId, appId); + + // If previousAppId > 0, an app is changing its app ID + int32_t previousUid = + previousAppId > 0 ? (int32_t)multiuser_get_uid(userId, previousAppId) : -1; + int32_t cacheGid = multiuser_get_cache_gid(userId, appId); mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751; @@ -447,21 +708,16 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str cacheGid = uid; } + long projectIdApp = get_project_id(uid, PROJECT_ID_APP_START); + long projectIdCache = get_project_id(uid, PROJECT_ID_APP_CACHE_START); + if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid_, userId, pkgname); - bool existing = (access(path.c_str(), F_OK) == 0); - if (prepare_app_dir(path, targetMode, uid) || - prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || - prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { - return error("Failed to prepare " + path); - } - - // Consider restorecon over contents if label changed - if (restorecon_app_data_lazy(path, seInfo, uid, existing) || - restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) || - restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) { - return error("Failed to restorecon " + path); + auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode, + projectIdApp, projectIdCache); + if (!status.isOk()) { + return status; } // Remember inode numbers of cache directories so that we can clear @@ -483,38 +739,97 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid_, userId, pkgname); - bool existing = (access(path.c_str(), F_OK) == 0); - if (prepare_app_dir(path, targetMode, uid) || - prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || - prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { - return error("Failed to prepare " + path); + auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode, + projectIdApp, projectIdCache); + if (!status.isOk()) { + return status; } - - // Consider restorecon over contents if label changed - if (restorecon_app_data_lazy(path, seInfo, uid, existing) || - restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) || - restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) { - return error("Failed to restorecon " + path); + if (previousUid > 0 && previousUid != uid) { + chown_app_profile_dir(packageName, appId, userId); } if (!prepare_app_profile_dir(packageName, appId, userId)) { return error("Failed to prepare profiles for " + packageName); } } + + if (flags & FLAG_STORAGE_SDK) { + // Safe to ignore status since we can retry creating this by calling reconcileSdkData + auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, flags); + if (!ignore.isOk()) { + PLOG(WARNING) << "Failed to create sdk data package directory for " << packageName; + } + + } else { + // Package does not need sdk storage. Remove it. + destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags); + } + + return ok(); +} + +/** + * Responsible for creating /data/misc_{ce|de}/user/0/sdksandbox/<package-name> directory and other + * app level sub directories, such as ./shared + */ +binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t appId, int32_t flags) { + int32_t sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId); + if (sdkSandboxUid == -1) { + // There no valid sdk sandbox process for this app. Skip creation of data directory + return ok(); + } + + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int currentFlag : storageFlags) { + if ((flags & currentFlag) == 0) { + continue; + } + bool isCeData = (currentFlag == FLAG_STORAGE_CE); + + // /data/misc_{ce,de}/<user-id>/sdksandbox directory gets created by vold + // during user creation + + // Prepare the package directory + auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, + packageName.c_str()); +#if SDK_DEBUG + LOG(DEBUG) << "Creating app-level sdk data directory: " << packagePath; +#endif + + if (prepare_app_dir(packagePath, 0751, AID_SYSTEM, AID_SYSTEM, 0)) { + return error("Failed to prepare " + packagePath); + } + } + return ok(); } +binder::Status InstalldNativeService::createAppData( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, + int32_t targetSdkVersion, int64_t* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + LOCK_PACKAGE_USER(); + return createAppDataLocked(uuid, packageName, userId, flags, appId, previousAppId, seInfo, + targetSdkVersion, _aidl_return); +} binder::Status InstalldNativeService::createAppData( const android::os::CreateAppDataArgs& args, android::os::CreateAppDataResult* _aidl_return) { ENFORCE_UID(AID_SYSTEM); - std::lock_guard<std::recursive_mutex> lock(mLock); + // Locking is performed depeer in the callstack. int64_t ceDataInode = -1; auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId, - args.seInfo, args.targetSdkVersion, &ceDataInode); + args.previousAppId, args.seInfo, args.targetSdkVersion, &ceDataInode); _aidl_return->ceDataInode = ceDataInode; _aidl_return->exceptionCode = status.exceptionCode(); _aidl_return->exceptionMessage = status.exceptionMessage(); @@ -525,10 +840,10 @@ binder::Status InstalldNativeService::createAppDataBatched( const std::vector<android::os::CreateAppDataArgs>& args, std::vector<android::os::CreateAppDataResult>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); - std::lock_guard<std::recursive_mutex> lock(mLock); + // Locking is performed depeer in the callstack. std::vector<android::os::CreateAppDataResult> results; - for (auto arg : args) { + for (const auto &arg : args) { android::os::CreateAppDataResult result; createAppData(arg, &result); results.push_back(result); @@ -537,12 +852,117 @@ binder::Status InstalldNativeService::createAppDataBatched( return ok(); } +binder::Status InstalldNativeService::reconcileSdkData( + const android::os::ReconcileSdkDataArgs& args) { + // Locking is performed depeer in the callstack. + + return reconcileSdkData(args.uuid, args.packageName, args.subDirNames, args.userId, args.appId, + args.previousAppId, args.seInfo, args.flags); +} + +/** + * Reconciles per-sdk directory under app-level sdk data directory. + + * E.g. `/data/misc_ce/0/sdksandbox/<package-name>/<sdkPackageName>-<randomSuffix> + * + * - If the sdk data package directory is missing, we create it first. + * - If sdkPackageNames is empty, we delete sdk package directory since it's not needed anymore. + * - If a sdk level directory we need to prepare already exist, we skip creating it again. This + * is to avoid having same per-sdk directory with different suffix. + * - If a sdk level directory exist which is absent from sdkPackageNames, we remove it. + */ +binder::Status InstalldNativeService::reconcileSdkData(const std::optional<std::string>& uuid, + const std::string& packageName, + const std::vector<std::string>& subDirNames, + int userId, int appId, int previousAppId, + const std::string& seInfo, int flags) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + LOCK_PACKAGE_USER(); + +#if SDK_DEBUG + LOG(DEBUG) << "Creating per sdk data directory for: " << packageName; +#endif + + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + + // Prepare the sdk package directory in case it's missing + const auto status = + createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, flags); + if (!status.isOk()) { + return status; + } + + auto res = ok(); + // We have to create sdk data for CE and DE storage + const int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int currentFlag : storageFlags) { + if ((flags & currentFlag) == 0) { + continue; + } + const bool isCeData = (currentFlag == FLAG_STORAGE_CE); + + const auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, + packageName.c_str()); + + // Remove existing sub-directories not referred in subDirNames + const std::unordered_set<std::string> expectedSubDirNames(subDirNames.begin(), + subDirNames.end()); + const auto subDirHandler = [&packagePath, &expectedSubDirNames, + &res](const std::string& subDirName) { + // Remove the per-sdk directory if it is not referred in + // expectedSubDirNames + if (expectedSubDirNames.find(subDirName) == expectedSubDirNames.end()) { + auto path = packagePath + "/" + subDirName; + if (delete_dir_contents_and_dir(path) != 0) { + res = error("Failed to delete " + path); + return; + } + } + }; + const int ec = foreach_subdir(packagePath, subDirHandler); + if (ec != 0) { + res = error("Failed to process subdirs for " + packagePath); + continue; + } + + // Now create the subDirNames + for (const auto& subDirName : subDirNames) { + const std::string path = + create_data_misc_sdk_sandbox_sdk_path(uuid_, isCeData, userId, + packageName.c_str(), subDirName.c_str()); + + // Create the directory along with cache and code_cache + const int32_t cacheGid = multiuser_get_cache_gid(userId, appId); + if (cacheGid == -1) { + return exception(binder::Status::EX_ILLEGAL_STATE, + StringPrintf("cacheGid cannot be -1 for sdk data")); + } + const int32_t sandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId); + int32_t previousSandboxUid = multiuser_get_sdk_sandbox_uid(userId, previousAppId); + int32_t appUid = multiuser_get_uid(userId, appId); + long projectIdApp = get_project_id(appUid, PROJECT_ID_APP_START); + long projectIdCache = get_project_id(appUid, PROJECT_ID_APP_CACHE_START); + auto status = + createAppDataDirs(path, sandboxUid, AID_NOBODY, previousSandboxUid, cacheGid, + seInfo, 0700 | S_ISGID, projectIdApp, projectIdCache); + if (!status.isOk()) { + res = status; + continue; + } + } + } + + return res; +} + binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -586,7 +1006,7 @@ binder::Status InstalldNativeService::clearAppProfiles(const std::string& packag const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); binder::Status res = ok(); if (!clear_primary_reference_profile(packageName, profileName)) { @@ -603,7 +1023,7 @@ binder::Status InstalldNativeService::clearAppData(const std::optional<std::stri ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -626,14 +1046,11 @@ binder::Status InstalldNativeService::clearAppData(const std::optional<std::stri } } if (flags & FLAG_STORAGE_DE) { - std::string suffix = ""; - bool only_cache = false; + std::string suffix; if (flags & FLAG_CLEAR_CACHE_ONLY) { suffix = CACHE_DIR_POSTFIX; - only_cache = true; } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { suffix = CODE_CACHE_DIR_POSTFIX; - only_cache = true; } auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix; @@ -685,6 +1102,47 @@ binder::Status InstalldNativeService::clearAppData(const std::optional<std::stri } } } + auto status = clearSdkSandboxDataPackageDirectory(uuid, packageName, userId, flags); + if (!status.isOk()) { + res = status; + } + return res; +} + +binder::Status InstalldNativeService::clearSdkSandboxDataPackageDirectory( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags) { + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + const char* pkgname = packageName.c_str(); + + binder::Status res = ok(); + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int i = 0; i < 2; i++) { + int currentFlag = storageFlags[i]; + if ((flags & currentFlag) == 0) { + continue; + } + bool isCeData = (currentFlag == FLAG_STORAGE_CE); + std::string suffix; + if (flags & FLAG_CLEAR_CACHE_ONLY) { + suffix = CACHE_DIR_POSTFIX; + } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { + suffix = CODE_CACHE_DIR_POSTFIX; + } + + auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgname); + if (access(appPath.c_str(), F_OK) != 0) continue; + const auto subDirHandler = [&appPath, &res, &suffix](const std::string& filename) { + auto filepath = appPath + "/" + filename + suffix; + if (delete_dir_contents(filepath, true) != 0) { + res = error("Failed to clear contents of " + filepath); + } + }; + const int ec = foreach_subdir(appPath, subDirHandler); + if (ec != 0) { + res = error("Failed to process subdirs for " + appPath); + } + } return res; } @@ -703,7 +1161,7 @@ static int destroy_app_current_profiles(const std::string& pkgname, userid_t use binder::Status InstalldNativeService::destroyAppProfiles(const std::string& packageName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); binder::Status res = ok(); std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr); @@ -718,12 +1176,31 @@ binder::Status InstalldNativeService::destroyAppProfiles(const std::string& pack return res; } +binder::Status InstalldNativeService::deleteReferenceProfile(const std::string& packageName, + const std::string& profileName) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + LOCK_PACKAGE(); + + // This function only supports primary dex'es. + std::string path = + create_reference_profile_path(packageName, profileName, /*is_secondary_dex=*/false); + if (unlink(path.c_str()) != 0) { + if (errno == ENOENT) { + return ok(); + } else { + return error("Failed to delete profile " + profileName + " for " + packageName); + } + } + return ok(); +} + binder::Status InstalldNativeService::destroyAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -731,13 +1208,13 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional<std::st 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) { @@ -771,7 +1248,6 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional<std::st 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); @@ -782,6 +1258,32 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional<std::st } } } + auto status = destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags); + if (!status.isOk()) { + res = status; + } + return res; +} + +binder::Status InstalldNativeService::destroySdkSandboxDataPackageDirectory( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags) { + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + const char* pkgname = packageName.c_str(); + + binder::Status res = ok(); + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int i = 0; i < 2; i++) { + int currentFlag = storageFlags[i]; + if ((flags & currentFlag) == 0) { + continue; + } + bool isCeData = (currentFlag == FLAG_STORAGE_CE); + auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgname); + if (rename_delete_dir_contents_and_dir(appPath) != 0) { + res = error("Failed to delete " + appPath); + } + } return res; } @@ -794,15 +1296,15 @@ binder::Status InstalldNativeService::fixupAppData(const std::optional<std::stri int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; - for (auto user : get_known_users(uuid_)) { + for (auto userId : get_known_users(uuid_)) { + LOCK_USER(); ATRACE_BEGIN("fixup user"); FTS* fts; FTSENT* p; - auto ce_path = create_data_user_ce_path(uuid_, user); - auto de_path = create_data_user_de_path(uuid_, user); + auto ce_path = create_data_user_ce_path(uuid_, userId); + auto de_path = create_data_user_de_path(uuid_, userId); char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { return error("Failed to fts_open"); @@ -894,29 +1396,28 @@ binder::Status InstalldNativeService::fixupAppData(const std::optional<std::stri } static int32_t copy_directory_recursive(const char* from, const char* to) { - char *argv[] = { - (char*) kCpPath, - (char*) "-F", /* delete any existing destination file first (--remove-destination) */ - (char*) "-p", /* preserve timestamps, ownership, and permissions */ - (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ - (char*) "-P", /* Do not follow symlinks [default] */ - (char*) "-d", /* don't dereference symlinks */ - (char*) from, - (char*) to - }; + char* argv[] = + {(char*)kCpPath, + (char*)"-F", /* delete any existing destination file first (--remove-destination) */ + (char*)"--preserve=mode,ownership,timestamps,xattr", /* preserve properties */ + (char*)"-R", /* recurse into subdirectories (DEST must be a directory) */ + (char*)"-P", /* Do not follow symlinks [default] */ + (char*)"-d", /* don't dereference symlinks */ + (char*)from, + (char*)to}; LOG(DEBUG) << "Copying " << from << " to " << to; return logwrap_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, LOG_ALOG, false, nullptr); } -binder::Status InstalldNativeService::snapshotAppData( - const std::optional<std::string>& volumeUuid, - const std::string& packageName, int32_t user, int32_t snapshotId, - int32_t storageFlags, int64_t* _aidl_return) { +binder::Status InstalldNativeService::snapshotAppData(const std::optional<std::string>& volumeUuid, + const std::string& packageName, + int32_t userId, int32_t snapshotId, + int32_t storageFlags, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); @@ -929,19 +1430,19 @@ binder::Status InstalldNativeService::snapshotAppData( bool clear_ce_on_exit = false; bool clear_de_on_exit = false; - auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name, - &snapshotId] { + auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &userId, &package_name, + &snapshotId] { if (clear_de_on_exit) { - auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId, - package_name); + auto to = create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } } if (clear_ce_on_exit) { - auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId, - package_name); + auto to = create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } @@ -951,10 +1452,11 @@ binder::Status InstalldNativeService::snapshotAppData( auto scope_guard = android::base::make_scope_guard(deleter); if (storageFlags & FLAG_STORAGE_DE) { - auto from = create_data_user_de_package_path(volume_uuid, user, package_name); - auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId); - auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user, - snapshotId, package_name); + auto from = create_data_user_de_package_path(volume_uuid, userId, package_name); + auto to = create_data_misc_de_rollback_path(volume_uuid, userId, snapshotId); + auto rollback_package_path = + create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); if (rc != 0) { @@ -978,15 +1480,15 @@ binder::Status InstalldNativeService::snapshotAppData( } // The app may not have any data at all, in which case it's OK to skip here. - auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name); + auto from_ce = create_data_user_ce_package_path(volume_uuid, userId, package_name); if (access(from_ce.c_str(), F_OK) != 0) { LOG(INFO) << "Missing source " << from_ce; return ok(); } // ce_data_inode is not needed when FLAG_CLEAR_CACHE_ONLY is set. - binder::Status clear_cache_result = clearAppData(volumeUuid, packageName, user, - storageFlags | FLAG_CLEAR_CACHE_ONLY, 0); + binder::Status clear_cache_result = + clearAppData(volumeUuid, packageName, userId, storageFlags | FLAG_CLEAR_CACHE_ONLY, 0); if (!clear_cache_result.isOk()) { // It should be fine to continue snapshot if we for some reason failed // to clear cache. @@ -994,8 +1496,9 @@ binder::Status InstalldNativeService::snapshotAppData( } // ce_data_inode is not needed when FLAG_CLEAR_CODE_CACHE_ONLY is set. - binder::Status clear_code_cache_result = clearAppData(volumeUuid, packageName, user, - storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, 0); + binder::Status clear_code_cache_result = + clearAppData(volumeUuid, packageName, userId, storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, + 0); if (!clear_code_cache_result.isOk()) { // It should be fine to continue snapshot if we for some reason failed // to clear code_cache. @@ -1003,10 +1506,11 @@ binder::Status InstalldNativeService::snapshotAppData( } if (storageFlags & FLAG_STORAGE_CE) { - auto from = create_data_user_ce_package_path(volume_uuid, user, package_name); - auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId); - auto rollback_package_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, - snapshotId, package_name); + auto from = create_data_user_ce_package_path(volume_uuid, userId, package_name); + auto to = create_data_misc_ce_rollback_path(volume_uuid, userId, snapshotId); + auto rollback_package_path = + create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); if (rc != 0) { @@ -1025,8 +1529,9 @@ binder::Status InstalldNativeService::snapshotAppData( return res; } if (_aidl_return != nullptr) { - auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, - snapshotId, package_name); + auto ce_snapshot_path = + create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return)); if (rc != 0) { res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path); @@ -1041,20 +1546,20 @@ binder::Status InstalldNativeService::snapshotAppData( binder::Status InstalldNativeService::restoreAppDataSnapshot( const std::optional<std::string>& volumeUuid, const std::string& packageName, - const int32_t appId, const std::string& seInfo, const int32_t user, + const int32_t appId, const std::string& seInfo, const int32_t userId, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); - auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, - user, snapshotId, package_name); - auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, - user, snapshotId, package_name); + auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); + auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) && (access(from_ce.c_str(), F_OK) == 0); @@ -1074,14 +1579,14 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( // It's fine to pass 0 as ceDataInode here, because restoreAppDataSnapshot // can only be called when user unlocks the phone, meaning that CE user data // is decrypted. - binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, - 0 /* ceDataInode */); + binder::Status res = + clearAppData(volumeUuid, packageName, userId, storageFlags, 0 /* ceDataInode */); if (!res.isOk()) { return res; } if (needs_ce_rollback) { - auto to_ce = create_data_user_ce_path(volume_uuid, user); + auto to_ce = create_data_user_ce_path(volume_uuid, userId); int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from_ce + " to " + to_ce); @@ -1091,11 +1596,11 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( } if (needs_de_rollback) { - auto to_de = create_data_user_de_path(volume_uuid, user); + auto to_de = create_data_user_de_path(volume_uuid, userId); int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str()); if (rc != 0) { if (needs_ce_rollback) { - auto ce_data = create_data_user_ce_package_path(volume_uuid, user, package_name); + auto ce_data = create_data_user_ce_package_path(volume_uuid, userId, package_name); LOG(WARNING) << "de_data rollback failed. Erasing rolled back ce_data " << ce_data; if (delete_dir_contents(ce_data.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete rolled back ce_data " << ce_data; @@ -1108,24 +1613,24 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( } // Finally, restore the SELinux label on the app data. - return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo); + return restoreconAppData(volumeUuid, packageName, userId, storageFlags, appId, seInfo); } binder::Status InstalldNativeService::destroyAppDataSnapshot( - const std::optional<std::string> &volumeUuid, const std::string& packageName, - const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId, + const std::optional<std::string>& volumeUuid, const std::string& packageName, + const int32_t userId, const int64_t ceSnapshotInode, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); if (storageFlags & FLAG_STORAGE_DE) { - auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, - user, snapshotId, package_name); + auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, userId, + snapshotId, package_name); int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */); if (res != 0) { @@ -1134,8 +1639,9 @@ binder::Status InstalldNativeService::destroyAppDataSnapshot( } if (storageFlags & FLAG_STORAGE_CE) { - auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, - user, snapshotId, package_name, ceSnapshotInode); + auto ce_snapshot_path = + create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name, ceSnapshotInode); int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + ce_snapshot_path); @@ -1145,15 +1651,15 @@ binder::Status InstalldNativeService::destroyAppDataSnapshot( } binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( - const std::optional<std::string> &volumeUuid, const int32_t user, + const std::optional<std::string>& volumeUuid, const int32_t userId, const std::vector<int32_t>& retainSnapshotIds) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; - auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, user); + auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, userId); std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(base_path.c_str()), closedir); if (!dir) { @@ -1171,8 +1677,8 @@ binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( if (parse_ok && std::find(retainSnapshotIds.begin(), retainSnapshotIds.end(), snapshot_id) == retainSnapshotIds.end()) { - auto rollback_path = create_data_misc_ce_rollback_path( - volume_uuid, user, snapshot_id); + auto rollback_path = + create_data_misc_ce_rollback_path(volume_uuid, userId, snapshot_id); int res = delete_dir_contents_and_dir(rollback_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + rollback_path); @@ -1190,7 +1696,7 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s CHECK_ARGUMENT_UUID(fromUuid); CHECK_ARGUMENT_UUID(toUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr; const char* to_uuid = toUuid ? toUuid->c_str() : nullptr; @@ -1218,24 +1724,25 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } // Copy private data for all known users - for (auto user : users) { + for (auto userId : users) { + LOCK_USER(); // Data source may not exist for all users; that's okay - auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name); + auto from_ce = create_data_user_ce_package_path(from_uuid, userId, package_name); if (access(from_ce.c_str(), F_OK) != 0) { LOG(INFO) << "Missing source " << from_ce; continue; } - if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId, - seInfo, targetSdkVersion, nullptr).isOk()) { + if (!createAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE, + appId, /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr) + .isOk()) { res = error("Failed to create package target"); goto fail; } - { - auto from = create_data_user_de_package_path(from_uuid, user, package_name); - auto to = create_data_user_de_path(to_uuid, user); + auto from = create_data_user_de_package_path(from_uuid, userId, package_name); + auto to = create_data_user_de_path(to_uuid, userId); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { @@ -1244,8 +1751,8 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } } { - auto from = create_data_user_ce_package_path(from_uuid, user, package_name); - auto to = create_data_user_ce_path(to_uuid, user); + auto from = create_data_user_ce_package_path(from_uuid, userId, package_name); + auto to = create_data_user_ce_path(to_uuid, userId); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { @@ -1254,13 +1761,44 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } } - if (!restoreconAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, - appId, seInfo).isOk()) { + if (!restoreconAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE, + appId, seInfo) + .isOk()) { res = error("Failed to restorecon"); goto fail; } } + // Copy sdk data for all known users + for (auto userId : users) { + LOCK_USER(); + + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int currentFlag : storageFlags) { + const bool isCeData = currentFlag == FLAG_STORAGE_CE; + + const auto from = create_data_misc_sdk_sandbox_package_path(from_uuid, isCeData, userId, + package_name); + if (access(from.c_str(), F_OK) != 0) { + LOG(INFO) << "Missing source " << from; + continue; + } + const auto to = create_data_misc_sdk_sandbox_path(to_uuid, isCeData, userId); + + const int rc = copy_directory_recursive(from.c_str(), to.c_str()); + if (rc != 0) { + res = error(rc, "Failed copying " + from + " to " + to); + goto fail; + } + } + + if (!restoreconSdkDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE, + appId, seInfo) + .isOk()) { + res = error("Failed to restorecon"); + goto fail; + } + } // We let the framework scan the new location and persist that before // deleting the data in the old location; this ordering ensures that // we can recover from things like battery pulls. @@ -1273,15 +1811,28 @@ fail: LOG(WARNING) << "Failed to rollback " << to_app_package_path; } } - for (auto user : users) { + for (auto userId : users) { + LOCK_USER(); { - auto to = create_data_user_de_package_path(to_uuid, user, package_name); + auto to = create_data_user_de_package_path(to_uuid, userId, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } } { - auto to = create_data_user_ce_package_path(to_uuid, user, package_name); + auto to = create_data_user_ce_package_path(to_uuid, userId, package_name); + if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { + LOG(WARNING) << "Failed to rollback " << to; + } + } + } + for (auto userId : users) { + LOCK_USER(); + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int currentFlag : storageFlags) { + const bool isCeData = currentFlag == FLAG_STORAGE_CE; + const auto to = create_data_misc_sdk_sandbox_package_path(to_uuid, isCeData, userId, + package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } @@ -1294,7 +1845,7 @@ binder::Status InstalldNativeService::createUserData(const std::optional<std::st int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; if (flags & FLAG_STORAGE_DE) { @@ -1312,7 +1863,7 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; binder::Status res = ok(); @@ -1321,6 +1872,11 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } + auto sdk_sandbox_de_path = + create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/false, userId); + if (delete_dir_contents_and_dir(sdk_sandbox_de_path, true) != 0) { + res = error("Failed to delete " + sdk_sandbox_de_path); + } if (uuid_ == nullptr) { path = create_data_misc_legacy_path(userId); if (delete_dir_contents_and_dir(path, true) != 0) { @@ -1337,6 +1893,11 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } + auto sdk_sandbox_ce_path = + create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/true, userId); + if (delete_dir_contents_and_dir(sdk_sandbox_ce_path, true) != 0) { + res = error("Failed to delete " + sdk_sandbox_ce_path); + } path = findDataMediaPath(uuid, userId); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); @@ -1346,15 +1907,18 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s } binder::Status InstalldNativeService::freeCache(const std::optional<std::string>& uuid, - int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) { + int64_t targetFreeBytes, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); +#ifndef GRANULAR_LOCKS + std::lock_guard lock(mLock); +#endif // !GRANULAR_LOCKS auto uuidString = uuid.value_or(""); const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto data_path = create_data_path(uuid_); auto noop = (flags & FLAG_FREE_CACHE_NOOP); + auto defy_target = (flags & FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES); int64_t free = data_disk_free(data_path); if (free < 0) { @@ -1363,11 +1927,13 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> int64_t cleared = 0; int64_t needed = targetFreeBytes - free; - LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested " - << targetFreeBytes << "; needed " << needed; + if (!defy_target) { + LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested " + << targetFreeBytes << "; needed " << needed; - if (free >= targetFreeBytes) { - return ok(); + if (free >= targetFreeBytes) { + return ok(); + } } if (flags & FLAG_FREE_CACHE_V2) { @@ -1376,21 +1942,58 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> // 1. Create trackers for every known UID ATRACE_BEGIN("create"); + const auto users = get_known_users(uuid_); +#ifdef GRANULAR_LOCKS + std::vector<UserLock> userLocks; + userLocks.reserve(users.size()); + std::vector<UserWriteLockGuard> lockGuards; + lockGuards.reserve(users.size()); +#endif // GRANULAR_LOCKS std::unordered_map<uid_t, std::shared_ptr<CacheTracker>> trackers; - for (auto user : get_known_users(uuid_)) { + for (auto userId : users) { +#ifdef GRANULAR_LOCKS + userLocks.emplace_back(userId, mUserIdLock, mLock); + lockGuards.emplace_back(userLocks.back()); +#endif // GRANULAR_LOCKS FTS *fts; FTSENT *p; - auto ce_path = create_data_user_ce_path(uuid_, user); - auto de_path = create_data_user_de_path(uuid_, user); - auto media_path = findDataMediaPath(uuid, user) + "/Android/data/"; - char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), - (char*) media_path.c_str(), nullptr }; + + // Create a list of data paths whose children have cache directories + auto ce_path = create_data_user_ce_path(uuid_, userId); + auto de_path = create_data_user_de_path(uuid_, userId); + auto media_path = findDataMediaPath(uuid, userId) + "/Android/data/"; + auto ce_sdk_path = create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/true, userId); + auto de_sdk_path = create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/false, userId); + + std::vector<std::string> dataPaths = {ce_path, de_path, media_path}; + foreach_subdir(ce_sdk_path, [&ce_sdk_path, &dataPaths](const std::string subDir) { + const auto fullpath = ce_sdk_path + "/" + subDir; + dataPaths.push_back(fullpath); + }); + foreach_subdir(de_sdk_path, [&de_sdk_path, &dataPaths](const std::string subDir) { + const auto fullpath = de_sdk_path + "/" + subDir; + dataPaths.push_back((char*)fullpath.c_str()); + }); + + char* argv[dataPaths.size() + 1]; + for (unsigned int i = 0; i < dataPaths.size(); i++) { + argv[i] = (char*)dataPaths[i].c_str(); + } + argv[dataPaths.size()] = nullptr; + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { return error("Failed to fts_open"); } while ((p = fts_read(fts)) != nullptr) { if (p->fts_info == FTS_D && p->fts_level == 1) { uid_t uid = p->fts_statp->st_uid; + + // If uid belongs to sdk sandbox, then the cache should be attributed to the + // original client app. + const auto client_uid = multiuser_convert_sdk_sandbox_to_app_uid(uid); + const bool isSandboxUid = (client_uid != (uid_t)-1); + if (isSandboxUid) uid = client_uid; + if (multiuser_get_app_id(uid) == AID_MEDIA_RW) { uid = (multiuser_get_app_id(p->fts_statp->st_gid) - AID_EXT_GID_START) + AID_APP_START; @@ -1449,12 +2052,6 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> break; } - // Only keep clearing when we haven't pushed into reserved area - if (cacheReservedBytes > 0 && cleared >= (cacheTotal - cacheReservedBytes)) { - LOG(DEBUG) << "Refusing to clear cached data in reserved space"; - break; - } - // Find the best tracker to work with; this might involve swapping // if the active tracker is no longer the most over quota bool nextBetter = active && !queue.empty() @@ -1487,15 +2084,17 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> cleared += item->size; } - // Verify that we're actually done before bailing, since sneaky - // apps might be using hardlinks - if (needed <= 0) { - free = data_disk_free(data_path); - needed = targetFreeBytes - free; + if (!defy_target) { + // Verify that we're actually done before bailing, since sneaky + // apps might be using hardlinks if (needed <= 0) { - break; - } else { - LOG(WARNING) << "Expected to be done but still need " << needed; + free = data_disk_free(data_path); + needed = targetFreeBytes - free; + if (needed <= 0) { + break; + } else { + LOG(WARNING) << "Expected to be done but still need " << needed; + } } } } @@ -1505,12 +2104,16 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> return error("Legacy cache logic no longer supported"); } - free = data_disk_free(data_path); - if (free >= targetFreeBytes) { - return ok(); + if (!defy_target) { + free = data_disk_free(data_path); + if (free >= targetFreeBytes) { + return ok(); + } else { + return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64, + targetFreeBytes, data_path.c_str(), free)); + } } else { - return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64, - targetFreeBytes, data_path.c_str(), free)); + return ok(); } } @@ -1518,7 +2121,6 @@ binder::Status InstalldNativeService::rmdex(const std::string& codePath, const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(codePath); - std::lock_guard<std::recursive_mutex> lock(mLock); char dex_path[PKG_PATH_MAX]; @@ -1565,68 +2167,78 @@ static std::string toString(std::vector<int64_t> values) { } #endif +// On devices without sdcardfs, if internal and external are on +// the same volume, a uid such as u0_a123 is used for both +// internal and external storage; therefore, subtract that +// amount from internal to make sure we don't count it double. +// This needs to happen for data, cache and OBB +static void deductDoubleSpaceIfNeeded(stats* stats, int64_t doubleSpaceToBeDeleted, uid_t uid, + const std::string& uuid) { + if (!supports_sdcardfs()) { + stats->dataSize -= doubleSpaceToBeDeleted; + long obbProjectId = get_project_id(uid, PROJECT_ID_EXT_OBB_START); + int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId); + stats->dataSize -= appObbSize; + } +} + static void collectQuotaStats(const std::string& uuid, int32_t userId, int32_t appId, struct stats* stats, struct stats* extStats) { - int64_t space; + int64_t space, doubleSpaceToBeDeleted = 0; uid_t uid = multiuser_get_uid(userId, appId); - if (stats != nullptr) { - if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { - stats->dataSize += space; - } - - int cacheGid = multiuser_get_cache_gid(userId, appId); - if (cacheGid != -1) { - if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) { - stats->cacheSize += space; - } - } - - int sharedGid = multiuser_get_shared_gid(0, appId); - if (sharedGid != -1) { - if ((space = GetOccupiedSpaceForGid(uuid, sharedGid)) != -1) { - stats->codeSize += space; - } - } - } + static const bool supportsProjectId = internal_storage_has_project_id(); if (extStats != nullptr) { - static const bool supportsSdCardFs = supports_sdcardfs(); space = get_occupied_app_space_external(uuid, userId, appId); if (space != -1) { extStats->dataSize += space; - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, if internal and external are on - // the same volume, a uid such as u0_a123 is used for - // application dirs on both internal and external storage; - // therefore, substract that amount from internal to make sure - // we don't count it double. - stats->dataSize -= space; - } + doubleSpaceToBeDeleted += space; } space = get_occupied_app_cache_space_external(uuid, userId, appId); if (space != -1) { extStats->dataSize += space; // cache counts for "data" extStats->cacheSize += space; - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, if internal and external are on - // the same volume, a uid such as u0_a123 is used for both - // internal and external storage; therefore, substract that - // amount from internal to make sure we don't count it double. - stats->dataSize -= space; + doubleSpaceToBeDeleted += space; + } + } + + if (stats != nullptr) { + if (!supportsProjectId) { + if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { + stats->dataSize += space; + } + deductDoubleSpaceIfNeeded(stats, doubleSpaceToBeDeleted, uid, uuid); + int sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId); + if (sdkSandboxUid != -1) { + if ((space = GetOccupiedSpaceForUid(uuid, sdkSandboxUid)) != -1) { + stats->dataSize += space; + } + } + int cacheGid = multiuser_get_cache_gid(userId, appId); + if (cacheGid != -1) { + if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) { + stats->cacheSize += space; + } + } + } else { + long projectId = get_project_id(uid, PROJECT_ID_APP_START); + if ((space = GetOccupiedSpaceForProjectId(uuid, projectId)) != -1) { + stats->dataSize += space; + } + projectId = get_project_id(uid, PROJECT_ID_APP_CACHE_START); + if ((space = GetOccupiedSpaceForProjectId(uuid, projectId)) != -1) { + stats->cacheSize += space; + stats->dataSize += space; } } - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, the UID of OBBs on external storage - // matches the regular app UID (eg u0_a123); therefore, to avoid - // OBBs being include in stats->dataSize, compute the OBB size for - // this app, and substract it from the size reported on internal - // storage - long obbProjectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START; - int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId); - stats->dataSize -= appObbSize; + int sharedGid = multiuser_get_shared_gid(0, appId); + if (sharedGid != -1) { + if ((space = GetOccupiedSpaceForGid(uuid, sharedGid)) != -1) { + stats->codeSize += space; + } } } } @@ -1681,8 +2293,17 @@ static void collectManualStats(const std::string& path, struct stats* stats) { closedir(d); } +void collectManualStatsForSubDirectories(const std::string& path, struct stats* stats) { + const auto subDirHandler = [&path, &stats](const std::string& subDir) { + auto fullpath = path + "/" + subDir; + collectManualStats(fullpath, stats); + }; + foreach_subdir(path, subDirHandler); +} + static void collectManualStatsForUser(const std::string& path, struct stats* stats, - bool exclude_apps = false) { + bool exclude_apps = false, + bool is_sdk_sandbox_storage = false) { DIR *d; int dfd; struct dirent *de; @@ -1707,6 +2328,11 @@ static void collectManualStatsForUser(const std::string& path, struct stats* sta continue; } else if (exclude_apps && (user_uid >= AID_APP_START && user_uid <= AID_APP_END)) { continue; + } else if (is_sdk_sandbox_storage) { + // In case of sdk sandbox storage (e.g. /data/misc_ce/0/sdksandbox/<package-name>), + // collect individual stats of each subdirectory (shared, storage of each sdk etc.) + collectManualStatsForSubDirectories(StringPrintf("%s/%s", path.c_str(), name), + stats); } else { collectManualStats(StringPrintf("%s/%s", path.c_str(), name), stats); } @@ -1749,6 +2375,12 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st fts_close(fts); } static bool ownsExternalStorage(int32_t appId) { + // if project id calculation is supported then, there is no need to + // calculate in a different way and project_id based calculation can work + if (internal_storage_has_project_id()) { + return false; + } + // Fetch external storage owner appid and check if it is the same as the // current appId whose size is calculated struct stat s; @@ -1849,6 +2481,19 @@ binder::Status InstalldNativeService::getAppSize(const std::optional<std::string collectManualStats(dePath, &stats); ATRACE_END(); + // In case of sdk sandbox storage (e.g. /data/misc_ce/0/sdksandbox/<package-name>), + // collect individual stats of each subdirectory (shared, storage of each sdk etc.) + if (appId >= AID_APP_START && appId <= AID_APP_END) { + ATRACE_BEGIN("sdksandbox"); + auto sdkSandboxCePath = + create_data_misc_sdk_sandbox_package_path(uuid_, true, userId, pkgname); + collectManualStatsForSubDirectories(sdkSandboxCePath, &stats); + auto sdkSandboxDePath = + create_data_misc_sdk_sandbox_package_path(uuid_, false, userId, pkgname); + collectManualStatsForSubDirectories(sdkSandboxDePath, &stats); + ATRACE_END(); + } + if (!uuid) { ATRACE_BEGIN("profiles"); calculate_tree_size( @@ -2085,6 +2730,13 @@ binder::Status InstalldNativeService::getUserSize(const std::optional<std::strin collectManualStatsForUser(dePath, &stats); ATRACE_END(); + ATRACE_BEGIN("sdksandbox"); + auto sdkSandboxCePath = create_data_misc_sdk_sandbox_path(uuid_, true, userId); + collectManualStatsForUser(sdkSandboxCePath, &stats, false, true); + auto sdkSandboxDePath = create_data_misc_sdk_sandbox_path(uuid_, false, userId); + collectManualStatsForUser(sdkSandboxDePath, &stats, false, true); + ATRACE_END(); + if (!uuid) { ATRACE_BEGIN("profile"); auto userProfilePath = create_primary_cur_profile_dir_path(userId); @@ -2251,7 +2903,7 @@ binder::Status InstalldNativeService::getAppCrates( CHECK_ARGUMENT_PACKAGE_NAME(packageName); } #ifdef ENABLE_STORAGE_CRATES - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); auto retVector = std::vector<std::optional<CrateMetadata>>(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; @@ -2297,7 +2949,7 @@ binder::Status InstalldNativeService::getUserCrates( ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); #ifdef ENABLE_STORAGE_CRATES - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto retVector = std::vector<std::optional<CrateMetadata>>(); @@ -2350,13 +3002,15 @@ binder::Status InstalldNativeService::setAppQuota(const std::optional<std::strin // Dumps the contents of a profile file, using pkgname's dex files for pretty // printing the result. binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::string& packageName, - const std::string& profileName, const std::string& codePath, bool* _aidl_return) { + const std::string& profileName, + const std::string& codePath, + bool dumpClassesAndMethods, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); - *_aidl_return = dump_profiles(uid, packageName, profileName, codePath); + *_aidl_return = dump_profiles(uid, packageName, profileName, codePath, dumpClassesAndMethods); return ok(); } @@ -2366,7 +3020,7 @@ binder::Status InstalldNativeService::copySystemProfile(const std::string& syste bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName); return ok(); } @@ -2376,7 +3030,7 @@ binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::stri const std::string& profileName, int* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = analyze_primary_profiles(uid, packageName, profileName); return ok(); @@ -2387,7 +3041,7 @@ binder::Status InstalldNativeService::createProfileSnapshot(int32_t appId, const std::string& classpath, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath); return ok(); @@ -2397,7 +3051,7 @@ binder::Status InstalldNativeService::destroyProfileSnapshot(const std::string& const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); std::string snapshot = create_snapshot_profile_path(packageName, profileName); if ((unlink(snapshot.c_str()) != 0) && (errno != ENOENT)) { @@ -2410,34 +3064,34 @@ static const char* getCStr(const std::optional<std::string>& data, const char* default_value = nullptr) { return data ? data->c_str() : default_value; } -binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid, - const std::optional<std::string>& packageName, const std::string& instructionSet, - int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags, +binder::Status InstalldNativeService::dexopt( + const std::string& apkPath, int32_t uid, const std::string& packageName, + const std::string& instructionSet, int32_t dexoptNeeded, + const std::optional<std::string>& outputPath, int32_t dexFlags, const std::string& compilerFilter, const std::optional<std::string>& uuid, const std::optional<std::string>& classLoaderContext, const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion, const std::optional<std::string>& profileName, const std::optional<std::string>& dexMetadataPath, - const std::optional<std::string>& compilationReason) { + const std::optional<std::string>& compilationReason, bool* aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PATH(apkPath); - if (packageName && *packageName != "*") { - CHECK_ARGUMENT_PACKAGE_NAME(*packageName); - } + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(outputPath); CHECK_ARGUMENT_PATH(dexMetadataPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + const auto userId = multiuser_get_user_id(uid); + LOCK_PACKAGE_USER(); const char* oat_dir = getCStr(outputPath); const char* instruction_set = instructionSet.c_str(); - if (oat_dir != nullptr && !createOatDir(oat_dir, instruction_set).isOk()) { + if (oat_dir != nullptr && !createOatDir(packageName, oat_dir, instruction_set).isOk()) { // Can't create oat dir - let dexopt use cache dir. oat_dir = nullptr; } const char* apk_path = apkPath.c_str(); - const char* pkgname = getCStr(packageName, "*"); + const char* pkgname = packageName.c_str(); const char* compiler_filter = compilerFilter.c_str(); const char* volume_uuid = getCStr(uuid); const char* class_loader_context = getCStr(classLoaderContext); @@ -2446,12 +3100,20 @@ binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t const char* dm_path = getCStr(dexMetadataPath); const char* compilation_reason = getCStr(compilationReason); std::string error_msg; + bool completed = false; // not necessary but for compiler int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded, oat_dir, dexFlags, compiler_filter, volume_uuid, class_loader_context, se_info, - downgrade, targetSdkVersion, profile_name, dm_path, compilation_reason, &error_msg); + downgrade, targetSdkVersion, profile_name, dm_path, compilation_reason, &error_msg, + &completed); + *aidl_return = completed; return res ? error(res, error_msg) : ok(); } +binder::Status InstalldNativeService::controlDexOptBlocking(bool block) { + android::installd::control_dexopt_blocking(block); + return ok(); +} + binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath, const std::string& packageName, const std ::string& outDexFile, int uid, @@ -2470,7 +3132,7 @@ binder::Status InstalldNativeService::linkNativeLibraryDirectory( CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(nativeLibPath32); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -2561,7 +3223,16 @@ binder::Status InstalldNativeService::restoreconAppData(const std::optional<std: ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); + return restoreconAppDataLocked(uuid, packageName, userId, flags, appId, seInfo); +} + +binder::Status InstalldNativeService::restoreconAppDataLocked( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); binder::Status res = ok(); @@ -2587,11 +3258,56 @@ binder::Status InstalldNativeService::restoreconAppData(const std::optional<std: return res; } -binder::Status InstalldNativeService::createOatDir(const std::string& oatDir, - const std::string& instructionSet) { +binder::Status InstalldNativeService::restoreconSdkDataLocked( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + + binder::Status res = ok(); + + // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here. + unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE; + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + const char* pkgName = packageName.c_str(); + const char* seinfo = seInfo.c_str(); + + uid_t uid = multiuser_get_sdk_sandbox_uid(userId, appId); + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int currentFlag : storageFlags) { + if ((flags & currentFlag) == 0) { + continue; + } + const bool isCeData = (currentFlag == FLAG_STORAGE_CE); + const auto packagePath = + create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgName); + if (access(packagePath.c_str(), F_OK) != 0) { + LOG(INFO) << "Missing source " << packagePath; + continue; + } + const auto subDirHandler = [&packagePath, &seinfo, &uid, &seflags, + &res](const std::string& subDir) { + const auto& fullpath = packagePath + "/" + subDir; + if (selinux_android_restorecon_pkgdir(fullpath.c_str(), seinfo, uid, seflags) < 0) { + res = error("restorecon failed for " + fullpath); + } + }; + const auto ec = foreach_subdir(packagePath, subDirHandler); + if (ec != 0) { + res = error("Failed to restorecon for subdirs of " + packagePath); + } + } + return res; +} + +binder::Status InstalldNativeService::createOatDir(const std::string& packageName, + const std::string& oatDir, + const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(oatDir); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* oat_dir = oatDir.c_str(); const char* instruction_set = instructionSet.c_str(); @@ -2613,10 +3329,11 @@ binder::Status InstalldNativeService::createOatDir(const std::string& oatDir, return ok(); } -binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) { +binder::Status InstalldNativeService::rmPackageDir(const std::string& packageName, + const std::string& packageDir) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(packageDir); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); if (validate_apk_path(packageDir.c_str())) { return error("Invalid path " + packageDir); @@ -2627,12 +3344,15 @@ binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir return ok(); } -binder::Status InstalldNativeService::linkFile(const std::string& relativePath, - const std::string& fromBase, const std::string& toBase) { +binder::Status InstalldNativeService::linkFile(const std::string& packageName, + const std::string& relativePath, + const std::string& fromBase, + const std::string& toBase) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(fromBase); CHECK_ARGUMENT_PATH(toBase); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* relative_path = relativePath.c_str(); const char* from_base = fromBase.c_str(); @@ -2657,12 +3377,15 @@ binder::Status InstalldNativeService::linkFile(const std::string& relativePath, return ok(); } -binder::Status InstalldNativeService::moveAb(const std::string& apkPath, - const std::string& instructionSet, const std::string& outputPath) { +binder::Status InstalldNativeService::moveAb(const std::string& packageName, + const std::string& apkPath, + const std::string& instructionSet, + const std::string& outputPath) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(apkPath); CHECK_ARGUMENT_PATH(outputPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* apk_path = apkPath.c_str(); const char* instruction_set = instructionSet.c_str(); @@ -2672,13 +3395,16 @@ binder::Status InstalldNativeService::moveAb(const std::string& apkPath, return success ? ok() : error(); } -binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath, - const std::string& instructionSet, const std::optional<std::string>& outputPath, - int64_t* _aidl_return) { +binder::Status InstalldNativeService::deleteOdex(const std::string& packageName, + const std::string& apkPath, + const std::string& instructionSet, + const std::optional<std::string>& outputPath, + int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(apkPath); CHECK_ARGUMENT_PATH(outputPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* apk_path = apkPath.c_str(); const char* instruction_set = instructionSet.c_str(); @@ -2688,142 +3414,6 @@ binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath, return *_aidl_return == -1 ? error() : ok(); } -// This kernel feature is experimental. -// TODO: remove local definition once upstreamed -#ifndef FS_IOC_ENABLE_VERITY - -#define FS_IOC_ENABLE_VERITY _IO('f', 133) -#define FS_IOC_SET_VERITY_MEASUREMENT _IOW('f', 134, struct fsverity_measurement) - -#define FS_VERITY_ALG_SHA256 1 - -struct fsverity_measurement { - __u16 digest_algorithm; - __u16 digest_size; - __u32 reserved1; - __u64 reserved2[3]; - __u8 digest[]; -}; - -#endif - -binder::Status InstalldNativeService::installApkVerity(const std::string& filePath, - android::base::unique_fd verityInputAshmem, int32_t contentSize) { - ENFORCE_UID(AID_SYSTEM); - CHECK_ARGUMENT_PATH(filePath); - std::lock_guard<std::recursive_mutex> lock(mLock); - - if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) { - return ok(); - } -#ifndef NDEBUG - ASSERT_PAGE_SIZE_4K(); -#endif - // TODO: also check fsverity support in the current file system if compiled with DEBUG. - // TODO: change ashmem to some temporary file to support huge apk. - if (!ashmem_valid(verityInputAshmem.get())) { - return error("FD is not an ashmem"); - } - - // 1. Seek to the next page boundary beyond the end of the file. - ::android::base::unique_fd wfd(open(filePath.c_str(), O_WRONLY)); - if (wfd.get() < 0) { - return error("Failed to open " + filePath); - } - struct stat st; - if (fstat(wfd.get(), &st) < 0) { - return error("Failed to stat " + filePath); - } - // fsverity starts from the block boundary. - off_t padding = kVerityPageSize - st.st_size % kVerityPageSize; - if (padding == kVerityPageSize) { - padding = 0; - } - if (lseek(wfd.get(), st.st_size + padding, SEEK_SET) < 0) { - return error("Failed to lseek " + filePath); - } - - // 2. Write everything in the ashmem to the file. Note that allocated - // ashmem size is multiple of page size, which is different from the - // actual content size. - int shmSize = ashmem_get_size_region(verityInputAshmem.get()); - if (shmSize < 0) { - return error("Failed to get ashmem size: " + std::to_string(shmSize)); - } - if (contentSize < 0) { - return error("Invalid content size: " + std::to_string(contentSize)); - } - if (contentSize > shmSize) { - return error("Content size overflow: " + std::to_string(contentSize) + " > " + - std::to_string(shmSize)); - } - auto data = std::unique_ptr<void, std::function<void (void *)>>( - mmap(nullptr, contentSize, PROT_READ, MAP_SHARED, verityInputAshmem.get(), 0), - [contentSize] (void* ptr) { - if (ptr != MAP_FAILED) { - munmap(ptr, contentSize); - } - }); - - if (data.get() == MAP_FAILED) { - return error("Failed to mmap the ashmem"); - } - char* cursor = reinterpret_cast<char*>(data.get()); - int remaining = contentSize; - while (remaining > 0) { - int ret = TEMP_FAILURE_RETRY(write(wfd.get(), cursor, remaining)); - if (ret < 0) { - return error("Failed to write to " + filePath + " (" + std::to_string(remaining) + - + "/" + std::to_string(contentSize) + ")"); - } - cursor += ret; - remaining -= ret; - } - wfd.reset(); - - // 3. Enable fsverity (needs readonly fd. Once it's done, the file becomes immutable. - ::android::base::unique_fd rfd(open(filePath.c_str(), O_RDONLY)); - if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, nullptr) < 0) { - return error("Failed to enable fsverity on " + filePath); - } - return ok(); -} - -binder::Status InstalldNativeService::assertFsverityRootHashMatches(const std::string& filePath, - const std::vector<uint8_t>& expectedHash) { - ENFORCE_UID(AID_SYSTEM); - CHECK_ARGUMENT_PATH(filePath); - std::lock_guard<std::recursive_mutex> lock(mLock); - - if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) { - return ok(); - } - // TODO: also check fsverity support in the current file system if compiled with DEBUG. - if (expectedHash.size() != kSha256Size) { - return error("verity hash size should be " + std::to_string(kSha256Size) + " but is " + - std::to_string(expectedHash.size())); - } - - ::android::base::unique_fd fd(open(filePath.c_str(), O_RDONLY)); - if (fd.get() < 0) { - return error("Failed to open " + filePath + ": " + strerror(errno)); - } - - unsigned int buffer_size = sizeof(fsverity_measurement) + kSha256Size; - std::vector<char> buffer(buffer_size, 0); - - fsverity_measurement* config = reinterpret_cast<fsverity_measurement*>(buffer.data()); - config->digest_algorithm = FS_VERITY_ALG_SHA256; - config->digest_size = kSha256Size; - memcpy(config->digest, expectedHash.data(), kSha256Size); - if (ioctl(fd.get(), FS_IOC_SET_VERITY_MEASUREMENT, config) < 0) { - // This includes an expected failure case with no FSVerity setup. It normally happens when - // the apk does not contains the Merkle tree root hash. - return error("Failed to measure fsverity on " + filePath + ": " + strerror(errno)); - } - return ok(); // hashes match -} - binder::Status InstalldNativeService::reconcileSecondaryDexFile( const std::string& dexPath, const std::string& packageName, int32_t uid, const std::vector<std::string>& isas, const std::optional<std::string>& volumeUuid, @@ -2832,7 +3422,8 @@ binder::Status InstalldNativeService::reconcileSecondaryDexFile( CHECK_ARGUMENT_UUID(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(dexPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + const auto userId = multiuser_get_user_id(uid); + LOCK_PACKAGE_USER(); bool result = android::installd::reconcile_secondary_dex_file( dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return); @@ -2855,6 +3446,38 @@ binder::Status InstalldNativeService::hashSecondaryDexFile( dexPath, packageName, uid, volumeUuid, storageFlag, _aidl_return); return result ? ok() : error(); } +/** + * Returns true if ioctl feature (F2FS_IOC_FS{GET,SET}XATTR) is supported as + * these were introduced in Linux 4.14, so kernel versions before that will fail + * while setting project id attributes. Only when these features are enabled, + * storage calculation using project_id is enabled + */ +bool check_if_ioctl_feature_is_supported() { + bool result = false; + auto temp_path = StringPrintf("%smisc/installd/ioctl_check", android_data_dir.c_str()); + if (access(temp_path.c_str(), F_OK) != 0) { + int fd = open(temp_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644); + result = set_quota_project_id(temp_path, 0, false) == 0; + close(fd); + // delete the temp file + remove(temp_path.c_str()); + } + return result; +} + +binder::Status InstalldNativeService::setFirstBoot() { + ENFORCE_UID(AID_SYSTEM); + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + std::string uuid; + if (GetOccupiedSpaceForProjectId(uuid, 0) != -1 && check_if_ioctl_feature_is_supported()) { + auto first_boot_path = + StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); + if (access(first_boot_path.c_str(), F_OK) != 0) { + close(open(first_boot_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644)); + } + } + return ok(); +} binder::Status InstalldNativeService::invalidateMounts() { ENFORCE_UID(AID_SYSTEM); @@ -2912,8 +3535,9 @@ binder::Status InstalldNativeService::tryMountDataMirror( const char* uuid_ = uuid->c_str(); + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_)); - std::lock_guard<std::recursive_mutex> lock(mLock); if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create CE mirror"); } @@ -2982,8 +3606,9 @@ binder::Status InstalldNativeService::onPrivateVolumeRemoved( std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_)); std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_)); + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + // Unmount CE storage - std::lock_guard<std::recursive_mutex> lock(mLock); if (TEMP_FAILURE_RETRY(umount(mirrorCeVolPath.c_str())) != 0) { if (errno != ENOENT) { res = error(StringPrintf("Failed to umount %s %s", mirrorCeVolPath.c_str(), @@ -3032,7 +3657,7 @@ binder::Status InstalldNativeService::prepareAppProfile(const std::string& packa ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); *_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath, dexMetadata); @@ -3050,5 +3675,46 @@ binder::Status InstalldNativeService::migrateLegacyObbData() { 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); + auto sdksandbox_ce_path = + create_data_misc_sdk_sandbox_path(uuid_cstr, /*isCeData=*/true, userId); + cleanup_invalid_package_dirs_under_path(sdksandbox_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); + auto sdksandbox_de_path = + create_data_misc_sdk_sandbox_path(uuid_cstr, /*isCeData=*/false, userId); + cleanup_invalid_package_dirs_under_path(sdksandbox_de_path); + } + + return ok(); +} + +binder::Status InstalldNativeService::getOdexVisibility( + const std::string& packageName, const std::string& apkPath, + const std::string& instructionSet, const std::optional<std::string>& outputPath, + int32_t* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + CHECK_ARGUMENT_PATH(apkPath); + CHECK_ARGUMENT_PATH(outputPath); + LOCK_PACKAGE(); + + const char* apk_path = apkPath.c_str(); + const char* instruction_set = instructionSet.c_str(); + const char* oat_dir = outputPath ? outputPath->c_str() : nullptr; + + *_aidl_return = get_odex_visibility(apk_path, instruction_set, oat_dir); + return *_aidl_return == -1 ? error() : ok(); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index ea0c9454ae..521afc3f97 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -21,8 +21,9 @@ #include <inttypes.h> #include <unistd.h> -#include <vector> +#include <shared_mutex> #include <unordered_map> +#include <vector> #include <android-base/macros.h> #include <binder/BinderService.h> @@ -46,8 +47,9 @@ public: int32_t flags); binder::Status createAppData(const std::optional<std::string>& uuid, - const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, - const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return); + const std::string& packageName, int32_t userId, int32_t flags, + int32_t appId, int32_t previousAppId, const std::string& seInfo, + int32_t targetSdkVersion, int64_t* _aidl_return); binder::Status createAppData( const android::os::CreateAppDataArgs& args, @@ -56,9 +58,12 @@ public: const std::vector<android::os::CreateAppDataArgs>& args, std::vector<android::os::CreateAppDataResult>* _aidl_return); + binder::Status reconcileSdkData(const android::os::ReconcileSdkDataArgs& args); + binder::Status restoreconAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo); + binder::Status migrateAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags); binder::Status clearAppData(const std::optional<std::string>& uuid, @@ -109,15 +114,17 @@ public: int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, const std::string& fromCodePath); - binder::Status dexopt(const std::string& apkPath, int32_t uid, - const std::optional<std::string>& packageName, const std::string& instructionSet, - int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags, - const std::string& compilerFilter, const std::optional<std::string>& uuid, - const std::optional<std::string>& classLoaderContext, - const std::optional<std::string>& seInfo, bool downgrade, - int32_t targetSdkVersion, const std::optional<std::string>& profileName, - const std::optional<std::string>& dexMetadataPath, - const std::optional<std::string>& compilationReason); + binder::Status dexopt(const std::string& apkPath, int32_t uid, const std::string& packageName, + const std::string& instructionSet, int32_t dexoptNeeded, + const std::optional<std::string>& outputPath, int32_t dexFlags, + const std::string& compilerFilter, const std::optional<std::string>& uuid, + const std::optional<std::string>& classLoaderContext, + const std::optional<std::string>& seInfo, bool downgrade, + int32_t targetSdkVersion, const std::optional<std::string>& profileName, + const std::optional<std::string>& dexMetadataPath, + const std::optional<std::string>& compilationReason, bool* aidl_return); + + binder::Status controlDexOptBlocking(bool block); binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName, const std::string& outDexFile, int uid, bool* _aidl_return); @@ -127,34 +134,35 @@ public: binder::Status mergeProfiles(int32_t uid, const std::string& packageName, const std::string& profileName, int* _aidl_return); binder::Status dumpProfiles(int32_t uid, const std::string& packageName, - const std::string& profileName, const std::string& codePath, bool* _aidl_return); + const std::string& profileName, const std::string& codePath, + bool dumpClassesAndMethods, bool* _aidl_return); binder::Status copySystemProfile(const std::string& systemProfile, int32_t uid, const std::string& packageName, const std::string& profileName, bool* _aidl_return); binder::Status clearAppProfiles(const std::string& packageName, const std::string& profileName); binder::Status destroyAppProfiles(const std::string& packageName); + binder::Status deleteReferenceProfile(const std::string& packageName, + const std::string& profileName); binder::Status createProfileSnapshot(int32_t appId, const std::string& packageName, const std::string& profileName, const std::string& classpath, bool* _aidl_return); binder::Status destroyProfileSnapshot(const std::string& packageName, const std::string& profileName); - binder::Status rmPackageDir(const std::string& packageDir); + binder::Status rmPackageDir(const std::string& packageName, const std::string& packageDir); binder::Status freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes, - int64_t cacheReservedBytes, int32_t flags); + int32_t flags); binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid, const std::string& packageName, const std::string& nativeLibPath32, int32_t userId); - binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet); - binder::Status linkFile(const std::string& relativePath, const std::string& fromBase, - const std::string& toBase); - binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet, - const std::string& outputPath); - binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet, - const std::optional<std::string>& outputPath, int64_t* _aidl_return); - binder::Status installApkVerity(const std::string& filePath, - android::base::unique_fd verityInput, int32_t contentSize); - binder::Status assertFsverityRootHashMatches(const std::string& filePath, - const std::vector<uint8_t>& expectedHash); + binder::Status createOatDir(const std::string& packageName, const std::string& oatDir, + const std::string& instructionSet); + binder::Status linkFile(const std::string& packageName, const std::string& relativePath, + const std::string& fromBase, const std::string& toBase); + binder::Status moveAb(const std::string& packageName, const std::string& apkPath, + const std::string& instructionSet, const std::string& outputPath); + binder::Status deleteOdex(const std::string& packageName, const std::string& apkPath, + const std::string& instructionSet, + const std::optional<std::string>& outputPath, int64_t* _aidl_return); binder::Status reconcileSecondaryDexFile(const std::string& dexPath, const std::string& packageName, int32_t uid, const std::vector<std::string>& isa, const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return); @@ -163,6 +171,7 @@ public: int32_t storageFlag, std::vector<uint8_t>* _aidl_return); binder::Status invalidateMounts(); + binder::Status setFirstBoot(); binder::Status isQuotaSupported(const std::optional<std::string>& volumeUuid, bool* _aidl_return); binder::Status tryMountDataMirror(const std::optional<std::string>& volumeUuid); @@ -175,8 +184,18 @@ public: binder::Status migrateLegacyObbData(); + binder::Status cleanupInvalidPackageDirs(const std::optional<std::string>& uuid, int32_t userId, + int32_t flags); + + binder::Status getOdexVisibility(const std::string& packageName, const std::string& apkPath, + const std::string& instructionSet, + const std::optional<std::string>& outputPath, + int32_t* _aidl_return); + private: std::recursive_mutex mLock; + std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock; + std::unordered_map<std::string, std::weak_ptr<std::recursive_mutex>> mPackageNameLock; std::recursive_mutex mMountsLock; std::recursive_mutex mQuotasLock; @@ -188,6 +207,34 @@ private: std::unordered_map<uid_t, int64_t> mCacheQuotas; std::string findDataMediaPath(const std::optional<std::string>& uuid, userid_t userid); + + binder::Status createAppDataLocked(const std::optional<std::string>& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, int32_t previousAppId, + const std::string& seInfo, int32_t targetSdkVersion, + int64_t* _aidl_return); + binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo); + + binder::Status createSdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid, + const std::string& packageName, + int32_t userId, int32_t appId, + int32_t flags); + binder::Status clearSdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid, + const std::string& packageName, + int32_t userId, int32_t flags); + binder::Status destroySdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid, + const std::string& packageName, + int32_t userId, int32_t flags); + binder::Status reconcileSdkData(const std::optional<std::string>& uuid, + const std::string& packageName, + const std::vector<std::string>& subDirNames, int32_t userId, + int32_t appId, int32_t previousAppId, const std::string& seInfo, + int flags); + binder::Status restoreconSdkDataLocked(const std::optional<std::string>& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo); }; } // namespace installd diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS index d6807ffe26..643b2c2d11 100644 --- a/cmds/installd/OWNERS +++ b/cmds/installd/OWNERS @@ -4,7 +4,6 @@ calin@google.com jsharkey@android.com maco@google.com mast@google.com -mathieuc@google.com narayan@google.com ngeoffray@google.com rpl@google.com diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING index 3f0fb6d2ba..fc4cfc98dc 100644 --- a/cmds/installd/TEST_MAPPING +++ b/cmds/installd/TEST_MAPPING @@ -30,6 +30,14 @@ }, { "name": "CtsCompilationTestCases" + }, + { + "name": "SdkSandboxStorageHostTest", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + } + ] } ] } diff --git a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl index 96d7faaaa2..d5e8ee5daf 100644 --- a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl +++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl @@ -23,6 +23,7 @@ parcelable CreateAppDataArgs { int userId; int flags; int appId; + int previousAppId; @utf8InCpp String seInfo; int targetSdkVersion; } diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 3d32f61997..9ad853b1df 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -20,10 +20,12 @@ package android.os; interface IInstalld { void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags); void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags); - + void setFirstBoot(); android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args); android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args); + void reconcileSdkData(in android.os.ReconcileSdkDataArgs args); + void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, int flags, int appId, @utf8InCpp String seInfo); void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, @@ -55,7 +57,8 @@ interface IInstalld { @utf8InCpp String packageName, int appId, @utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath); - void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName, + // Returns false if it is cancelled. Returns true if it is completed or have other errors. + boolean dexopt(@utf8InCpp String apkPath, int uid, @utf8InCpp String packageName, @utf8InCpp String instructionSet, int dexoptNeeded, @nullable @utf8InCpp String outputPath, int dexFlags, @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid, @@ -64,6 +67,9 @@ interface IInstalld { @nullable @utf8InCpp String profileName, @nullable @utf8InCpp String dexMetadataPath, @nullable @utf8InCpp String compilationReason); + // Blocks (when block is true) or unblock (when block is false) dexopt. + // Blocking also invloves cancelling the currently running dexopt. + void controlDexOptBlocking(boolean block); boolean compileLayouts(@utf8InCpp String apkPath, @utf8InCpp String packageName, @utf8InCpp String outDexFile, int uid); @@ -71,31 +77,29 @@ interface IInstalld { int mergeProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName); boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName, - @utf8InCpp String codePath); + @utf8InCpp String codePath, boolean dumpClassesAndMethods); boolean copySystemProfile(@utf8InCpp String systemProfile, int uid, @utf8InCpp String packageName, @utf8InCpp String profileName); void clearAppProfiles(@utf8InCpp String packageName, @utf8InCpp String profileName); void destroyAppProfiles(@utf8InCpp String packageName); + void deleteReferenceProfile(@utf8InCpp String packageName, @utf8InCpp String profileName); boolean createProfileSnapshot(int appId, @utf8InCpp String packageName, @utf8InCpp String profileName, @utf8InCpp String classpath); void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName); - void rmPackageDir(@utf8InCpp String packageDir); - void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, - long cacheReservedBytes, int flags); + void rmPackageDir(@utf8InCpp String packageName, @utf8InCpp String packageDir); + void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, int flags); void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId); - void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet); - void linkFile(@utf8InCpp String relativePath, @utf8InCpp String fromBase, - @utf8InCpp String toBase); - void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet, - @utf8InCpp String outputPath); - long deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet, - @nullable @utf8InCpp String outputPath); - void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput, - int contentSize); - void assertFsverityRootHashMatches(@utf8InCpp String filePath, in byte[] expectedHash); + void createOatDir(@utf8InCpp String packageName, @utf8InCpp String oatDir, + @utf8InCpp String instructionSet); + void linkFile(@utf8InCpp String packageName, @utf8InCpp String relativePath, + @utf8InCpp String fromBase, @utf8InCpp String toBase); + void moveAb(@utf8InCpp String packageName, @utf8InCpp String apkPath, + @utf8InCpp String instructionSet, @utf8InCpp String outputPath); + long deleteOdex(@utf8InCpp String packageName, @utf8InCpp String apkPath, + @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath); boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName, int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid, @@ -125,9 +129,15 @@ interface IInstalld { void migrateLegacyObbData(); + void cleanupInvalidPackageDirs(@nullable @utf8InCpp String uuid, int userId, int flags); + + int getOdexVisibility(@utf8InCpp String packageName, @utf8InCpp String apkPath, + @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath); + const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; const int FLAG_STORAGE_EXTERNAL = 0x4; + const int FLAG_STORAGE_SDK = 0x8; const int FLAG_CLEAR_CACHE_ONLY = 0x10; const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20; @@ -135,6 +145,8 @@ interface IInstalld { const int FLAG_FREE_CACHE_V2 = 0x100; const int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 0x200; const int FLAG_FREE_CACHE_NOOP = 0x400; + // Set below flag to clear cache irrespective of target free bytes required + const int FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES = 0x800; const int FLAG_USE_QUOTA = 0x1000; const int FLAG_FORCE = 0x2000; diff --git a/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl new file mode 100644 index 0000000000..583a36d580 --- /dev/null +++ b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** {@hide} */ +parcelable ReconcileSdkDataArgs { + @nullable @utf8InCpp String uuid; + @utf8InCpp String packageName; + @utf8InCpp List<String> subDirNames; + int userId; + int appId; + int previousAppId; + @utf8InCpp String seInfo; + int flags; +} diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index d678281b6e..ebb78913b1 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -15,8 +15,8 @@ */ #define LOG_TAG "installd" -#include <array> #include <fcntl.h> +#include <signal.h> #include <stdlib.h> #include <string.h> #include <sys/capability.h> @@ -28,10 +28,14 @@ #include <sys/wait.h> #include <unistd.h> +#include <array> #include <iomanip> +#include <mutex> +#include <unordered_set> #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/no_destructor.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> @@ -47,14 +51,17 @@ #include <selinux/android.h> #include <server_configurable_flags/get_flags.h> #include <system/thread_defs.h> +#include <utils/Mutex.h> +#include <ziparchive/zip_archive.h> #include "dexopt.h" #include "dexopt_return_codes.h" #include "execv_helper.h" #include "globals.h" -#include "installd_deps.h" #include "installd_constants.h" +#include "installd_deps.h" #include "otapreopt_utils.h" +#include "restorable_file.h" #include "run_dex2oat.h" #include "unique_file.h" #include "utils.h" @@ -67,8 +74,87 @@ using android::base::ReadFdToString; using android::base::ReadFully; using android::base::StringPrintf; using android::base::WriteFully; +using android::base::borrowed_fd; using android::base::unique_fd; +namespace { + +// Timeout for short operations, such as merging profiles. +constexpr int kShortTimeoutMs = 60000; // 1 minute. + +// Timeout for long operations, such as compilation. This should be smaller than the Package Manager +// watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that the operation will be +// aborted before that watchdog would take down the system server. +constexpr int kLongTimeoutMs = 570000; // 9.5 minutes. + +class DexOptStatus { + public: + // Check if dexopt is cancelled and fork if it is not cancelled. + // cancelled is set to true if cancelled. Otherwise it will be set to false. + // If it is not cancelled, it will return the return value of fork() call. + // If cancelled, fork will not happen and it will return -1. + pid_t check_cancellation_and_fork(/* out */ bool *cancelled) { + std::lock_guard<std::mutex> lock(dexopt_lock_); + if (dexopt_blocked_) { + *cancelled = true; + return -1; + } + pid_t pid = fork(); + *cancelled = false; + if (pid > 0) { // parent + dexopt_pids_.insert(pid); + } + return pid; + } + + // Returns true if pid was killed (is in killed list). It could have finished if killing + // happened after the process is finished. + bool check_if_killed_and_remove_dexopt_pid(pid_t pid) { + std::lock_guard<std::mutex> lock(dexopt_lock_); + dexopt_pids_.erase(pid); + if (dexopt_killed_pids_.erase(pid) == 1) { + return true; + } + return false; + } + + // Tells whether dexopt is blocked or not. + bool is_dexopt_blocked() { + std::lock_guard<std::mutex> lock(dexopt_lock_); + return dexopt_blocked_; + } + + // Enable or disable dexopt blocking. + void control_dexopt_blocking(bool block) { + std::lock_guard<std::mutex> lock(dexopt_lock_); + dexopt_blocked_ = block; + if (!block) { + return; + } + // Blocked, also kill currently running tasks + for (auto pid : dexopt_pids_) { + LOG(INFO) << "control_dexopt_blocking kill pid:" << pid; + kill(pid, SIGKILL); + dexopt_killed_pids_.insert(pid); + } + dexopt_pids_.clear(); + } + + private: + std::mutex dexopt_lock_; + // when true, dexopt is blocked and will not run. + bool dexopt_blocked_ GUARDED_BY(dexopt_lock_) = false; + // PIDs of child process while runinng dexopt. + // If the child process is finished, it should be removed. + std::unordered_set<pid_t> dexopt_pids_ GUARDED_BY(dexopt_lock_); + // PIDs of child processes killed by cancellation. + std::unordered_set<pid_t> dexopt_killed_pids_ GUARDED_BY(dexopt_lock_); +}; + +android::base::NoDestructor<DexOptStatus> dexopt_status_; + +} // namespace + namespace android { namespace installd { @@ -167,7 +253,7 @@ bool clear_primary_reference_profile(const std::string& package_name, // The location is the profile name for primary apks or the dex path for secondary dex files. bool clear_primary_current_profiles(const std::string& package_name, const std::string& location) { bool success = true; - // For secondary dex files, we don't really need the user but we use it for sanity checks. + // For secondary dex files, we don't really need the user but we use it for validity checks. std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr); for (auto user : users) { success &= clear_current_profile(package_name, location, user, /*is_secondary_dex*/false); @@ -233,12 +319,6 @@ static bool IsBootClassPathProfilingEnable() { return profile_boot_class_path == "true"; } -static void UnlinkIgnoreResult(const std::string& path) { - if (unlink(path.c_str()) < 0) { - PLOG(ERROR) << "Failed to unlink " << path; - } -} - /* * Whether dexopt should use a swap file when compiling an APK. * @@ -384,8 +464,8 @@ static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::st }); } -static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name, - const std::string& location) { +static unique_fd open_snapshot_profile(uid_t uid, const std::string& package_name, + const std::string& location) { std::string profile = create_snapshot_profile_path(package_name, location); return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); } @@ -397,7 +477,7 @@ static void open_profile_files(uid_t uid, const std::string& package_name, *reference_profile_fd = open_reference_profile(uid, package_name, location, /*read_write*/ true, is_secondary_dex); - // For secondary dex files, we don't really need the user but we use it for sanity checks. + // For secondary dex files, we don't really need the user but we use it for validity checks. // Note: the user owning the dex file should be the current user. std::vector<userid_t> users; if (is_secondary_dex){ @@ -415,6 +495,25 @@ static void open_profile_files(uid_t uid, const std::string& package_name, } } +// Cleans up an output file specified by a file descriptor. This function should be called whenever +// a subprocess that modifies a system-managed file crashes. +// If the subprocess crashes while it's writing to the file, the file is likely corrupted, so we +// should remove it. +// If the subprocess times out and is killed while it's acquiring a flock on the file, there is +// probably a deadlock, so it's also good to remove the file so that later operations won't +// encounter the same problem. It's safe to do so because the process that is holding the flock will +// still have access to the file until the file descriptor is closed. +// Note that we can't do `clear_reference_profile` here even if the fd points to a reference profile +// because that also requires a flock and is therefore likely to be stuck in the second case. +static bool cleanup_output_fd(int fd) { + std::string path; + bool ret = remove_file_at_fd(fd, &path); + if (ret) { + LOG(INFO) << "Removed file at path " << path; + } + return ret; +} + static constexpr int PROFMAN_BIN_RETURN_CODE_SUCCESS = 0; static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 1; static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION_NOT_ENOUGH_DELTA = 2; @@ -426,13 +525,14 @@ static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION_EMPTY_PROFILES = 7 class RunProfman : public ExecVHelper { public: - void SetupArgs(const std::vector<unique_fd>& profile_fds, - const unique_fd& reference_profile_fd, - const std::vector<unique_fd>& apk_fds, - const std::vector<std::string>& dex_locations, - bool copy_and_update, - bool for_snapshot, - bool for_boot_image) { + template <typename T, typename U> + void SetupArgs(const std::vector<T>& profile_fds, + const unique_fd& reference_profile_fd, + const std::vector<U>& apk_fds, + const std::vector<std::string>& dex_locations, + bool copy_and_update, + bool for_snapshot, + bool for_boot_image) { // TODO(calin): Assume for now we run in the bg compile job (which is in // most of the invocation). With the current data flow, is not very easy or @@ -448,11 +548,11 @@ class RunProfman : public ExecVHelper { AddArg("--reference-profile-file-fd=" + std::to_string(reference_profile_fd.get())); } - for (const unique_fd& fd : profile_fds) { + for (const T& fd : profile_fds) { AddArg("--profile-file-fd=" + std::to_string(fd.get())); } - for (const unique_fd& fd : apk_fds) { + for (const U& fd : apk_fds) { AddArg("--apk-fd=" + std::to_string(fd.get())); } @@ -511,31 +611,28 @@ class RunProfman : public ExecVHelper { for_boot_image); } - void SetupCopyAndUpdate(unique_fd&& profile_fd, - unique_fd&& reference_profile_fd, - unique_fd&& apk_fd, + void SetupCopyAndUpdate(const unique_fd& profile_fd, + const unique_fd& reference_profile_fd, + const unique_fd& apk_fd, const std::string& dex_location) { - // The fds need to stay open longer than the scope of the function, so put them into a local - // variable vector. - profiles_fd_.push_back(std::move(profile_fd)); - apk_fds_.push_back(std::move(apk_fd)); - reference_profile_fd_ = std::move(reference_profile_fd); - std::vector<std::string> dex_locations = {dex_location}; - SetupArgs(profiles_fd_, - reference_profile_fd_, - apk_fds_, - dex_locations, + SetupArgs(std::vector<borrowed_fd>{profile_fd}, + reference_profile_fd, + std::vector<borrowed_fd>{apk_fd}, + {dex_location}, /*copy_and_update=*/true, /*for_snapshot*/false, /*for_boot_image*/false); } - void SetupDump(const std::vector<unique_fd>& profiles_fd, - const unique_fd& reference_profile_fd, + void SetupDump(const std::vector<unique_fd>& profiles_fd, const unique_fd& reference_profile_fd, const std::vector<std::string>& dex_locations, - const std::vector<unique_fd>& apk_fds, + const std::vector<unique_fd>& apk_fds, bool dump_classes_and_methods, const unique_fd& output_fd) { - AddArg("--dump-only"); + if (dump_classes_and_methods) { + AddArg("--dump-classes-and-methods"); + } else { + AddArg("--dump-only"); + } AddArg(StringPrintf("--dump-output-to-fd=%d", output_fd.get())); SetupArgs(profiles_fd, reference_profile_fd, @@ -550,11 +647,6 @@ class RunProfman : public ExecVHelper { void Exec() { ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec); } - - private: - unique_fd reference_profile_fd_; - std::vector<unique_fd> profiles_fd_; - std::vector<unique_fd> apk_fds_; }; static int analyze_profiles(uid_t uid, const std::string& package_name, @@ -586,13 +678,14 @@ static int analyze_profiles(uid_t uid, const std::string& package_name, profman_merge.Exec(); } /* parent */ - int return_code = wait_child(pid); + int return_code = wait_child_with_timeout(pid, kShortTimeoutMs); bool need_to_compile = false; bool empty_profiles = false; bool should_clear_current_profiles = false; bool should_clear_reference_profile = false; if (!WIFEXITED(return_code)) { LOG(WARNING) << "profman failed for location " << location << ": " << return_code; + cleanup_output_fd(reference_profile_fd.get()); } else { return_code = WEXITSTATUS(return_code); switch (return_code) { @@ -682,7 +775,7 @@ int analyze_primary_profiles(uid_t uid, const std::string& package_name, } bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& profile_name, - const std::string& code_path) { + const std::string& code_path, bool dump_classes_and_methods) { std::vector<unique_fd> profile_fds; unique_fd reference_profile_fd; std::string out_file_name = StringPrintf("/data/misc/profman/%s-%s.txt", @@ -718,7 +811,8 @@ bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& p RunProfman profman_dump; - profman_dump.SetupDump(profile_fds, reference_profile_fd, dex_locations, apk_fds, output_fd); + profman_dump.SetupDump(profile_fds, reference_profile_fd, dex_locations, apk_fds, + dump_classes_and_methods, output_fd); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ @@ -726,10 +820,10 @@ bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& p profman_dump.Exec(); } /* parent */ - int return_code = wait_child(pid); + int return_code = wait_child_with_timeout(pid, kShortTimeoutMs); if (!WIFEXITED(return_code)) { - LOG(WARNING) << "profman failed for package " << pkgname << ": " - << return_code; + LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code; + cleanup_output_fd(output_fd.get()); return false; } return true; @@ -800,7 +894,11 @@ bool copy_system_profile(const std::string& system_profile, _exit(0); } /* parent */ - int return_code = wait_child(pid); + int return_code = wait_child_with_timeout(pid, kShortTimeoutMs); + if (!WIFEXITED(return_code)) { + cleanup_output_fd(out_fd.get()); + return false; + } return return_code == 0; } @@ -912,42 +1010,34 @@ static bool create_oat_out_path(const char* apk_path, const char* instruction_se } // (re)Creates the app image if needed. -UniqueFile maybe_open_app_image(const std::string& out_oat_path, - bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) { - +RestorableFile maybe_open_app_image(const std::string& out_oat_path, bool generate_app_image, + bool is_public, int uid, bool is_secondary_dex) { const std::string image_path = create_image_filename(out_oat_path); if (image_path.empty()) { // Happens when the out_oat_path has an unknown extension. - return UniqueFile(); + return RestorableFile(); } - // In case there is a stale image, remove it now. Ignore any error. - unlink(image_path.c_str()); - // Not enabled, exit. if (!generate_app_image) { - return UniqueFile(); + RestorableFile::RemoveAllFiles(image_path); + return RestorableFile(); } std::string app_image_format = GetProperty("dalvik.vm.appimageformat", ""); if (app_image_format.empty()) { - return UniqueFile(); + RestorableFile::RemoveAllFiles(image_path); + return RestorableFile(); } - // Recreate is true since we do not want to modify a mapped image. If the app is - // already running and we modify the image file, it can cause crashes (b/27493510). - UniqueFile image_file( - open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/), - image_path, - UnlinkIgnoreResult); + // If the app is already running and we modify the image file, it can cause crashes + // (b/27493510). + RestorableFile image_file = RestorableFile::CreateWritableFile(image_path, + /*permissions*/ 0600); if (image_file.fd() < 0) { // Could not create application image file. Go on since we can compile without it. LOG(ERROR) << "installd could not create '" << image_path << "' for image file during dexopt"; - // If we have a valid image file path but no image fd, explicitly erase the image file. - if (unlink(image_path.c_str()) < 0) { - if (errno != ENOENT) { - PLOG(ERROR) << "Couldn't unlink image file " << image_path; - } - } + // If we have a valid image file path but cannot create tmp file, reset it. + image_file.reset(); } else if (!set_permissions_and_ownership( image_file.fd(), is_public, uid, image_path.c_str(), is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str()); @@ -1021,9 +1111,9 @@ UniqueFile maybe_open_reference_profile(const std::string& pkgname, // Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to // out_vdex_wrapper. Returns true for success or false in case of errors. bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed, - const char* instruction_set, bool is_public, int uid, bool is_secondary_dex, - bool profile_guided, UniqueFile* in_vdex_wrapper, - UniqueFile* out_vdex_wrapper) { + const char* instruction_set, bool is_public, int uid, + bool is_secondary_dex, bool profile_guided, + UniqueFile* in_vdex_wrapper, RestorableFile* out_vdex_wrapper) { CHECK(in_vdex_wrapper != nullptr); CHECK(out_vdex_wrapper != nullptr); // Open the existing VDEX. We do this before creating the new output VDEX, which will @@ -1038,6 +1128,14 @@ bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, return false; } + // Create work file first. All files will be deleted when it fails. + *out_vdex_wrapper = RestorableFile::CreateWritableFile(out_vdex_path_str, + /*permissions*/ 0644); + if (out_vdex_wrapper->fd() < 0) { + ALOGE("installd cannot open vdex '%s' during dexopt\n", out_vdex_path_str.c_str()); + return false; + } + bool update_vdex_in_place = false; if (dexopt_action != DEX2OAT_FROM_SCRATCH) { // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd. @@ -1069,41 +1167,19 @@ bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) && !profile_guided; if (update_vdex_in_place) { + // dex2oat marks it invalid anyway. So delete it and set work file fd. + unlink(in_vdex_path_str.c_str()); // Open the file read-write to be able to update it. - in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0), - in_vdex_path_str); - if (in_vdex_wrapper->fd() == -1) { - // If we failed to open the file, we cannot update it in place. - update_vdex_in_place = false; - } + in_vdex_wrapper->reset(out_vdex_wrapper->fd(), in_vdex_path_str); + // Disable auto close for the in wrapper fd (it will be done when destructing the out + // wrapper). + in_vdex_wrapper->DisableAutoClose(); } else { in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0), in_vdex_path_str); } } - // If we are updating the vdex in place, we do not need to recreate a vdex, - // and can use the same existing one. - if (update_vdex_in_place) { - // We unlink the file in case the invocation of dex2oat fails, to ensure we don't - // have bogus stale vdex files. - out_vdex_wrapper->reset( - in_vdex_wrapper->fd(), - out_vdex_path_str, - UnlinkIgnoreResult); - // Disable auto close for the in wrapper fd (it will be done when destructing the out - // wrapper). - in_vdex_wrapper->DisableAutoClose(); - } else { - out_vdex_wrapper->reset( - open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644), - out_vdex_path_str, - UnlinkIgnoreResult); - if (out_vdex_wrapper->fd() < 0) { - ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str()); - return false; - } - } if (!set_permissions_and_ownership(out_vdex_wrapper->fd(), is_public, uid, out_vdex_path_str.c_str(), is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str()); @@ -1115,16 +1191,13 @@ bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, } // Opens the output oat file for the given apk. -UniqueFile open_oat_out_file(const char* apk_path, const char* oat_dir, - bool is_public, int uid, const char* instruction_set, bool is_secondary_dex) { +RestorableFile open_oat_out_file(const char* apk_path, const char* oat_dir, bool is_public, int uid, + const char* instruction_set, bool is_secondary_dex) { char out_oat_path[PKG_PATH_MAX]; if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) { - return UniqueFile(); + return RestorableFile(); } - UniqueFile oat( - open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644), - out_oat_path, - UnlinkIgnoreResult); + RestorableFile oat = RestorableFile::CreateWritableFile(out_oat_path, /*permissions*/ 0644); if (oat.fd() < 0) { PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path; } else if (!set_permissions_and_ownership( @@ -1475,7 +1548,7 @@ static bool get_class_loader_context_dex_paths(const char* class_loader_context, } pipe_read.reset(); - int return_code = wait_child(pid); + int return_code = wait_child_with_timeout(pid, kShortTimeoutMs); if (!WIFEXITED(return_code)) { PLOG(ERROR) << "Error waiting for child dexoptanalyzer process"; return false; @@ -1525,23 +1598,46 @@ static std::string join_fds(const std::vector<unique_fd>& fds) { return ss.str(); } +void control_dexopt_blocking(bool block) { + dexopt_status_->control_dexopt_blocking(block); +} + +bool is_dexopt_blocked() { + return dexopt_status_->is_dexopt_blocked(); +} + +enum SecondaryDexOptProcessResult { + kSecondaryDexOptProcessOk = 0, + kSecondaryDexOptProcessCancelled = 1, + kSecondaryDexOptProcessError = 2 +}; + // Processes the dex_path as a secondary dex files and return true if the path dex file should -// be compiled. Returns false for errors (logged) or true if the secondary dex path was process -// successfully. -// When returning true, the output parameters will be: +// be compiled. +// Returns: kSecondaryDexOptProcessError for errors (logged). +// kSecondaryDexOptProcessOk if the secondary dex path was process successfully. +// kSecondaryDexOptProcessCancelled if the processing was cancelled. +// +// When returning kSecondaryDexOptProcessOk, the output parameters will be: // - is_public_out: whether or not the oat file should not be made public // - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded // - oat_dir_out: the oat dir path where the oat file should be stored -static bool process_secondary_dex_dexopt(const std::string& dex_path, const char* pkgname, - int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set, - const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out, - std::string* oat_dir_out, bool downgrade, const char* class_loader_context, - const std::vector<std::string>& context_dex_paths, /* out */ std::string* error_msg) { +static SecondaryDexOptProcessResult process_secondary_dex_dexopt(const std::string& dex_path, + const char* pkgname, int dexopt_flags, const char* volume_uuid, int uid, + const char* instruction_set, const char* compiler_filter, bool* is_public_out, + int* dexopt_needed_out, std::string* oat_dir_out, bool downgrade, + const char* class_loader_context, const std::vector<std::string>& context_dex_paths, + /* out */ std::string* error_msg) { LOG(DEBUG) << "Processing secondary dex path " << dex_path; + + if (dexopt_status_->is_dexopt_blocked()) { + return kSecondaryDexOptProcessCancelled; + } + int storage_flag; if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag, error_msg)) { LOG(ERROR) << *error_msg; - return false; + return kSecondaryDexOptProcessError; } // Compute the oat dir as it's not easy to extract it from the child computation. char oat_path[PKG_PATH_MAX]; @@ -1550,11 +1646,15 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char if (!create_secondary_dex_oat_layout( dex_path, instruction_set, oat_dir, oat_isa_dir, oat_path, error_msg)) { LOG(ERROR) << "Could not create secondary odex layout: " << *error_msg; - return false; + return kSecondaryDexOptProcessError; } oat_dir_out->assign(oat_dir); - pid_t pid = fork(); + bool cancelled = false; + pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled); + if (cancelled) { + return kSecondaryDexOptProcessCancelled; + } if (pid == 0) { // child -- drop privileges before continuing. drop_capabilities(uid); @@ -1622,13 +1722,18 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char } /* parent */ - int result = wait_child(pid); + int result = wait_child_with_timeout(pid, kShortTimeoutMs); + cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid); if (!WIFEXITED(result)) { + if ((WTERMSIG(result) == SIGKILL) && cancelled) { + LOG(INFO) << "dexoptanalyzer cancelled for path:" << dex_path; + return kSecondaryDexOptProcessCancelled; + } *error_msg = StringPrintf("dexoptanalyzer failed for path %s: 0x%04x", dex_path.c_str(), result); LOG(ERROR) << *error_msg; - return false; + return kSecondaryDexOptProcessError; } result = WEXITSTATUS(result); // Check that we successfully executed dexoptanalyzer. @@ -1656,7 +1761,7 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char // It is ok to check this flag outside in the parent process. *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) && is_file_public(dex_path); - return success; + return success ? kSecondaryDexOptProcessOk : kSecondaryDexOptProcessError; } static std::string format_dexopt_error(int status, const char* dex_path) { @@ -1670,17 +1775,29 @@ static std::string format_dexopt_error(int status, const char* dex_path) { return StringPrintf("Dex2oat invocation for %s failed with 0x%04x", dex_path, status); } + int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set, int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter, const char* volume_uuid, const char* class_loader_context, const char* se_info, bool downgrade, int target_sdk_version, const char* profile_name, - const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg) { + const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg, + /* out */ bool* completed) { CHECK(pkgname != nullptr); CHECK(pkgname[0] != 0); CHECK(error_msg != nullptr); CHECK_EQ(dexopt_flags & ~DEXOPT_MASK, 0) << "dexopt flags contains unknown fields: " << dexopt_flags; + bool local_completed; // local placeholder for nullptr case + if (completed == nullptr) { + completed = &local_completed; + } + *completed = true; + if (dexopt_status_->is_dexopt_blocked()) { + *completed = false; + return 0; + } + if (!validate_dex_path_size(dex_path)) { *error_msg = StringPrintf("Failed to validate %s", dex_path); return -1; @@ -1712,14 +1829,20 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins *error_msg = "Failed acquiring context dex paths"; return -1; // We had an error, logged in the process method. } - - if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, - instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str, - downgrade, class_loader_context, context_dex_paths, error_msg)) { + SecondaryDexOptProcessResult sec_dex_result = process_secondary_dex_dexopt(dex_path, + pkgname, dexopt_flags, volume_uuid, uid,instruction_set, compiler_filter, + &is_public, &dexopt_needed, &oat_dir_str, downgrade, class_loader_context, + context_dex_paths, error_msg); + if (sec_dex_result == kSecondaryDexOptProcessOk) { oat_dir = oat_dir_str.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { + *completed = true; return 0; // Nothing to do, report success. } + } else if (sec_dex_result == kSecondaryDexOptProcessCancelled) { + // cancelled, not an error. + *completed = false; + return 0; } else { if (error_msg->empty()) { // TODO: Make this a CHECK. *error_msg = "Failed processing secondary."; @@ -1749,8 +1872,8 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } // Create the output OAT file. - UniqueFile out_oat = open_oat_out_file(dex_path, oat_dir, is_public, uid, - instruction_set, is_secondary_dex); + RestorableFile out_oat = + open_oat_out_file(dex_path, oat_dir, is_public, uid, instruction_set, is_secondary_dex); if (out_oat.fd() < 0) { *error_msg = "Could not open out oat file."; return -1; @@ -1758,7 +1881,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Open vdex files. UniqueFile in_vdex; - UniqueFile out_vdex; + RestorableFile out_vdex; if (!open_vdex_files_for_dex2oat(dex_path, out_oat.path().c_str(), dexopt_needed, instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex, &out_vdex)) { @@ -1794,8 +1917,8 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } // Create the app image file if needed. - UniqueFile out_image = maybe_open_app_image( - out_oat.path(), generate_app_image, is_public, uid, is_secondary_dex); + RestorableFile out_image = maybe_open_app_image(out_oat.path(), generate_app_image, is_public, + uid, is_secondary_dex); UniqueFile dex_metadata; if (dex_metadata_path != nullptr) { @@ -1828,28 +1951,20 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---"; RunDex2Oat runner(dex2oat_bin, execv_helper.get()); - runner.Initialize(out_oat, - out_vdex, - out_image, - in_dex, - in_vdex, - dex_metadata, - reference_profile, - class_loader_context, - join_fds(context_input_fds), - swap_fd.get(), - instruction_set, - compiler_filter, - debuggable, - boot_complete, - for_restore, - target_sdk_version, - enable_hidden_api_checks, - generate_compact_dex, - use_jitzygote_image, + runner.Initialize(out_oat.GetUniqueFile(), out_vdex.GetUniqueFile(), out_image.GetUniqueFile(), + in_dex, in_vdex, dex_metadata, reference_profile, class_loader_context, + join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter, + debuggable, boot_complete, for_restore, target_sdk_version, + enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image, compilation_reason); - pid_t pid = fork(); + bool cancelled = false; + pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled); + if (cancelled) { + *completed = false; + reference_profile.DisableCleanup(); + return 0; + } if (pid == 0) { // Need to set schedpolicy before dropping privileges // for cgroup migration. See details at b/175178520. @@ -1866,10 +1981,18 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins runner.Exec(DexoptReturnCodes::kDex2oatExec); } else { - int res = wait_child(pid); + int res = wait_child_with_timeout(pid, kLongTimeoutMs); + bool cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid); if (res == 0) { LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' (success) ---"; } else { + if ((WTERMSIG(res) == SIGKILL) && cancelled) { + LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- cancelled"; + // cancelled, not an error + *completed = false; + reference_profile.DisableCleanup(); + return 0; + } LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- status=0x" << std::hex << std::setw(4) << res << ", process failed"; *error_msg = format_dexopt_error(res, dex_path); @@ -1877,12 +2000,43 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } } - // We've been successful, don't delete output. - out_oat.DisableCleanup(); - out_vdex.DisableCleanup(); - out_image.DisableCleanup(); + // dex2oat ran successfully, so profile is safe to keep. reference_profile.DisableCleanup(); + // We've been successful, commit work files. + // If committing (=renaming tmp to regular) fails, try to restore backup files. + // If restoring fails as well, as a last resort, remove all files. + if (!out_oat.CreateBackupFile() || !out_vdex.CreateBackupFile() || + !out_image.CreateBackupFile()) { + // Renaming failure can mean that the original file may not be accessible from installd. + LOG(ERROR) << "Cannot create backup file from existing file, file in wrong state?" + << ", out_oat:" << out_oat.path() << " ,out_vdex:" << out_vdex.path() + << " ,out_image:" << out_image.path(); + out_oat.ResetAndRemoveAllFiles(); + out_vdex.ResetAndRemoveAllFiles(); + out_image.ResetAndRemoveAllFiles(); + return -1; + } + if (!out_oat.CommitWorkFile() || !out_vdex.CommitWorkFile() || !out_image.CommitWorkFile()) { + LOG(ERROR) << "Cannot commit, out_oat:" << out_oat.path() + << " ,out_vdex:" << out_vdex.path() << " ,out_image:" << out_image.path(); + if (!out_oat.RestoreBackupFile() || !out_vdex.RestoreBackupFile() || + !out_image.RestoreBackupFile()) { + LOG(ERROR) << "Cannot cancel commit, out_oat:" << out_oat.path() + << " ,out_vdex:" << out_vdex.path() << " ,out_image:" << out_image.path(); + // Restoring failed. + out_oat.ResetAndRemoveAllFiles(); + out_vdex.ResetAndRemoveAllFiles(); + out_image.ResetAndRemoveAllFiles(); + } + return -1; + } + // Now remove remaining backup files. + out_oat.RemoveBackupFile(); + out_vdex.RemoveBackupFile(); + out_image.RemoveBackupFile(); + + *completed = true; return 0; } @@ -2016,7 +2170,7 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, _exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError); } - int return_code = wait_child(pid); + int return_code = wait_child_with_timeout(pid, kShortTimeoutMs); if (!WIFEXITED(return_code)) { LOG(WARNING) << "reconcile dex failed for location " << dex_path << ": " << return_code; } else { @@ -2134,7 +2288,7 @@ bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkg if (!ReadFully(pipe_read, out_secondary_dex_hash->data(), out_secondary_dex_hash->size())) { out_secondary_dex_hash->clear(); } - return wait_child(pid) == 0; + return wait_child_with_timeout(pid, kShortTimeoutMs) == 0; } // Helper for move_ab, so that we can have common failure-case cleanup. @@ -2425,7 +2579,7 @@ static bool create_app_profile_snapshot(int32_t app_id, const std::string& classpath) { int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id); - unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); + unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name); if (snapshot_fd < 0) { return false; } @@ -2464,9 +2618,10 @@ static bool create_app_profile_snapshot(int32_t app_id, } /* parent */ - int return_code = wait_child(pid); + int return_code = wait_child_with_timeout(pid, kShortTimeoutMs); if (!WIFEXITED(return_code)) { LOG(WARNING) << "profman failed for " << package_name << ":" << profile_name; + cleanup_output_fd(snapshot_fd.get()); return false; } @@ -2499,7 +2654,7 @@ static bool create_boot_image_profile_snapshot(const std::string& package_name, } // Open and create the snapshot profile. - unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); + unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name); // Collect all non empty profiles. // The collection will traverse all applications profiles and find the non empty files. @@ -2573,10 +2728,11 @@ static bool create_boot_image_profile_snapshot(const std::string& package_name, } /* parent */ - int return_code = wait_child(pid); + int return_code = wait_child_with_timeout(pid, kShortTimeoutMs); if (!WIFEXITED(return_code)) { PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name; + cleanup_output_fd(snapshot_fd.get()); return false; } @@ -2601,29 +2757,54 @@ bool create_profile_snapshot(int32_t app_id, const std::string& package_name, } } +static bool check_profile_exists_in_dexmetadata(const std::string& dex_metadata) { + ZipArchiveHandle zip = nullptr; + if (OpenArchive(dex_metadata.c_str(), &zip) != 0) { + PLOG(ERROR) << "Failed to open dm '" << dex_metadata << "'"; + return false; + } + + ZipEntry64 entry; + int result = FindEntry(zip, "primary.prof", &entry); + CloseArchive(zip); + + return result != 0 ? false : true; +} + bool prepare_app_profile(const std::string& package_name, userid_t user_id, appid_t app_id, const std::string& profile_name, const std::string& code_path, const std::optional<std::string>& dex_metadata) { - // Prepare the current profile. - std::string cur_profile = create_current_profile_path(user_id, package_name, profile_name, - /*is_secondary_dex*/ false); - uid_t uid = multiuser_get_uid(user_id, app_id); - if (fs_prepare_file_strict(cur_profile.c_str(), 0600, uid, uid) != 0) { - PLOG(ERROR) << "Failed to prepare " << cur_profile; - return false; + if (user_id != USER_NULL) { + if (user_id < 0) { + LOG(ERROR) << "Unexpected user ID " << user_id; + return false; + } + + // Prepare the current profile. + std::string cur_profile = create_current_profile_path(user_id, package_name, profile_name, + /*is_secondary_dex*/ false); + uid_t uid = multiuser_get_uid(user_id, app_id); + if (fs_prepare_file_strict(cur_profile.c_str(), 0600, uid, uid) != 0) { + PLOG(ERROR) << "Failed to prepare " << cur_profile; + return false; + } + } else { + // Prepare the reference profile as the system user. + user_id = USER_SYSTEM; } // Check if we need to install the profile from the dex metadata. - if (!dex_metadata) { + if (!dex_metadata || !check_profile_exists_in_dexmetadata(dex_metadata->c_str())) { return true; } // We have a dex metdata. Merge the profile into the reference profile. - unique_fd ref_profile_fd = open_reference_profile(uid, package_name, profile_name, - /*read_write*/ true, /*is_secondary_dex*/ false); + unique_fd ref_profile_fd = + open_reference_profile(multiuser_get_uid(user_id, app_id), package_name, profile_name, + /*read_write*/ true, /*is_secondary_dex*/ false); unique_fd dex_metadata_fd(TEMP_FAILURE_RETRY( open(dex_metadata->c_str(), O_RDONLY | O_NOFOLLOW))); unique_fd apk_fd(TEMP_FAILURE_RETRY(open(code_path.c_str(), O_RDONLY | O_NOFOLLOW))); @@ -2633,9 +2814,9 @@ bool prepare_app_profile(const std::string& package_name, } RunProfman args; - args.SetupCopyAndUpdate(std::move(dex_metadata_fd), - std::move(ref_profile_fd), - std::move(apk_fd), + args.SetupCopyAndUpdate(dex_metadata_fd, + ref_profile_fd, + apk_fd, code_path); pid_t pid = fork(); if (pid == 0) { @@ -2648,13 +2829,31 @@ bool prepare_app_profile(const std::string& package_name, } /* parent */ - int return_code = wait_child(pid); + int return_code = wait_child_with_timeout(pid, kShortTimeoutMs); if (!WIFEXITED(return_code)) { PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name; + cleanup_output_fd(ref_profile_fd.get()); return false; } return true; } +int get_odex_visibility(const char* apk_path, const char* instruction_set, const char* oat_dir) { + char oat_path[PKG_PATH_MAX]; + if (!create_oat_out_path(apk_path, instruction_set, oat_dir, /*is_secondary_dex=*/false, + oat_path)) { + return -1; + } + struct stat st; + if (stat(oat_path, &st) == -1) { + if (errno == ENOENT) { + return ODEX_NOT_FOUND; + } + PLOG(ERROR) << "Could not stat " << oat_path; + return -1; + } + return (st.st_mode & S_IROTH) ? ODEX_IS_PUBLIC : ODEX_IS_PRIVATE; +} + } // namespace installd } // namespace android diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index 5a637b1ce7..5cf402c54c 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -88,20 +88,17 @@ bool create_profile_snapshot(int32_t app_id, const std::string& profile_name, const std::string& classpath); -bool dump_profiles(int32_t uid, - const std::string& pkgname, - const std::string& profile_name, - const std::string& code_path); +bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& profile_name, + const std::string& code_path, bool dump_classes_and_methods); bool copy_system_profile(const std::string& system_profile, uid_t packageUid, const std::string& pkgname, const std::string& profile_name); -// Prepare the app profile for the given code path: -// - create the current profile using profile_name -// - merge the profile from the dex metadata file (if present) into -// the reference profile. +// Prepares the app profile for the package at the given path: +// - Creates the current profile for the given user ID, unless the user ID is `USER_NULL`. +// - Merges the profile from the dex metadata file (if present) into the reference profile. bool prepare_app_profile(const std::string& package_name, userid_t user_id, appid_t app_id, @@ -121,11 +118,18 @@ bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid, const std::optional<std::string>& volume_uuid, int storage_flag, std::vector<uint8_t>* out_secondary_dex_hash); +// completed pass false if it is canceled. Otherwise it will be true even if there is other +// error. int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set, int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter, const char* volume_uuid, const char* class_loader_context, const char* se_info, bool downgrade, int target_sdk_version, const char* profile_name, - const char* dexMetadataPath, const char* compilation_reason, std::string* error_msg); + const char* dexMetadataPath, const char* compilation_reason, std::string* error_msg, + /* out */ bool* completed = nullptr); + +bool is_dexopt_blocked(); + +void control_dexopt_blocking(bool block); bool calculate_oat_file_path_default(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set); @@ -146,6 +150,12 @@ const char* select_execution_binary( bool is_release, bool is_debuggable_build); +// Returns `ODEX_NOT_FOUND` if the optimized artifacts are not found, or `ODEX_IS_PUBLIC` if the +// optimized artifacts are accessible by all apps, or `ODEX_IS_PRIVATE` if the optimized artifacts +// are only accessible by this app, or -1 if failed to get the visibility of the optimized +// artifacts. +int get_odex_visibility(const char* apk_path, const char* instruction_set, const char* oat_dir); + } // namespace installd } // namespace android diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h index 00d8441607..3623f9b884 100644 --- a/cmds/installd/installd_constants.h +++ b/cmds/installd/installd_constants.h @@ -18,6 +18,8 @@ #ifndef INSTALLD_CONSTANTS_H_ #define INSTALLD_CONSTANTS_H_ +#include "cutils/multiuser.h" + namespace android { namespace installd { @@ -83,6 +85,15 @@ constexpr int PROFILES_ANALYSIS_OPTIMIZE = 1; constexpr int PROFILES_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA = 2; constexpr int PROFILES_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES = 3; +// NOTE: keep in sync with Installer.java +constexpr int ODEX_NOT_FOUND = 0; +constexpr int ODEX_IS_PUBLIC = 1; +constexpr int ODEX_IS_PRIVATE = 2; + +// NOTE: keep in sync with UserHandle.java +constexpr userid_t USER_NULL = -10000; +constexpr userid_t USER_SYSTEM = 0; + #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) } // namespace installd diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index 6aa32b8d8e..e978e79d86 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -140,8 +140,8 @@ class OTAPreoptService { PrepareEnvironmentVariables(); - if (!EnsureBootImageAndDalvikCache()) { - LOG(ERROR) << "Bad boot image."; + if (!EnsureDalvikCache()) { + LOG(ERROR) << "Bad dalvik cache."; return 5; } @@ -349,8 +349,8 @@ private: } } - // Ensure that we have the right boot image and cache file structures. - bool EnsureBootImageAndDalvikCache() const { + // Ensure that we have the right cache file structures. + bool EnsureDalvikCache() const { if (parameters_.instruction_set == nullptr) { LOG(ERROR) << "Instruction set missing."; return false; @@ -376,15 +376,6 @@ private: } } - // Check whether we have a boot image. - // TODO: check that the files are correct wrt/ jars. - std::string preopted_boot_art_path = - StringPrintf("/apex/com.android.art/javalib/%s/boot.art", isa); - if (access(preopted_boot_art_path.c_str(), F_OK) != 0) { - PLOG(ERROR) << "Bad access() to " << preopted_boot_art_path; - return false; - } - return true; } diff --git a/cmds/installd/restorable_file.cpp b/cmds/installd/restorable_file.cpp new file mode 100644 index 0000000000..fd54a232d5 --- /dev/null +++ b/cmds/installd/restorable_file.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2021 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 "restorable_file.h" + +#include <string> + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> + +namespace { + +constexpr char kTmpFileSuffix[] = ".tmp"; +constexpr char kBackupFileSuffix[] = ".backup"; + +std::string GetTmpFilePath(const std::string& path) { + return android::base::StringPrintf("%s%s", path.c_str(), kTmpFileSuffix); +} + +std::string GetBackupFilePath(const std::string& path) { + return android::base::StringPrintf("%s%s", path.c_str(), kBackupFileSuffix); +} + +void UnlinkPossiblyNonExistingFile(const std::string& path) { + if (unlink(path.c_str()) < 0) { + if (errno != ENOENT && errno != EROFS) { // EROFS reported even if it does not exist. + PLOG(ERROR) << "Cannot unlink: " << path; + } + } +} + +// Check if file for the given path exists +bool FileExists(const std::string& path) { + struct stat st; + return ::stat(path.c_str(), &st) == 0; +} + +} // namespace + +namespace android { +namespace installd { + +RestorableFile::RestorableFile() : RestorableFile(-1, "") {} + +RestorableFile::RestorableFile(int value, const std::string& path) : unique_file_(value, path) { + // As cleanup is null, this does not make much difference but use unique_file_ only for closing + // tmp file. + unique_file_.DisableCleanup(); +} + +RestorableFile::~RestorableFile() { + reset(); +} + +void RestorableFile::reset() { + // need to copy before reset clears it. + std::string path(unique_file_.path()); + unique_file_.reset(); + if (!path.empty()) { + UnlinkPossiblyNonExistingFile(GetTmpFilePath(path)); + } +} + +bool RestorableFile::CreateBackupFile() { + if (path().empty() || !FileExists(path())) { + return true; + } + std::string backup = GetBackupFilePath(path()); + UnlinkPossiblyNonExistingFile(backup); + if (rename(path().c_str(), backup.c_str()) < 0) { + PLOG(ERROR) << "Cannot rename " << path() << " to " << backup; + return false; + } + return true; +} + +bool RestorableFile::CommitWorkFile() { + std::string path(unique_file_.path()); + // Keep the path with Commit for debugging purpose. + unique_file_.reset(-1, path); + if (!path.empty()) { + if (rename(GetTmpFilePath(path).c_str(), path.c_str()) < 0) { + PLOG(ERROR) << "Cannot rename " << GetTmpFilePath(path) << " to " << path; + // Remove both files as renaming can fail due to the original file as well. + UnlinkPossiblyNonExistingFile(path); + UnlinkPossiblyNonExistingFile(GetTmpFilePath(path)); + return false; + } + } + + return true; +} + +bool RestorableFile::RestoreBackupFile() { + std::string backup = GetBackupFilePath(path()); + if (path().empty() || !FileExists(backup)) { + return true; + } + UnlinkPossiblyNonExistingFile(path()); + if (rename(backup.c_str(), path().c_str()) < 0) { + PLOG(ERROR) << "Cannot rename " << backup << " to " << path(); + return false; + } + return true; +} + +void RestorableFile::RemoveBackupFile() { + UnlinkPossiblyNonExistingFile(GetBackupFilePath(path())); +} + +const UniqueFile& RestorableFile::GetUniqueFile() const { + return unique_file_; +} + +void RestorableFile::ResetAndRemoveAllFiles() { + std::string path(unique_file_.path()); + reset(); + RemoveAllFiles(path); +} + +RestorableFile RestorableFile::CreateWritableFile(const std::string& path, int permissions) { + std::string tmp_file_path = GetTmpFilePath(path); + // If old tmp file exists, delete it. + UnlinkPossiblyNonExistingFile(tmp_file_path); + int fd = -1; + if (!path.empty()) { + fd = open(tmp_file_path.c_str(), O_RDWR | O_CREAT, permissions); + if (fd < 0) { + PLOG(ERROR) << "Cannot create file: " << tmp_file_path; + } + } + RestorableFile rf(fd, path); + return rf; +} + +void RestorableFile::RemoveAllFiles(const std::string& path) { + UnlinkPossiblyNonExistingFile(GetTmpFilePath(path)); + UnlinkPossiblyNonExistingFile(GetBackupFilePath(path)); + UnlinkPossiblyNonExistingFile(path); +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/restorable_file.h b/cmds/installd/restorable_file.h new file mode 100644 index 0000000000..eda229241a --- /dev/null +++ b/cmds/installd/restorable_file.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INSTALLD_RESTORABLE_FILE_H +#define ANDROID_INSTALLD_RESTORABLE_FILE_H + +#include <functional> +#include <string> + +#include "unique_file.h" + +namespace android { +namespace installd { + +// This is a file abstraction which allows restoring to the original file while temporary work +// file is updated. +// +// Typical flow for this API will be: +// RestorableFile rf = RestorableFile::CreateWritableFile(...) +// write to file using file descriptor acquired from: rf.fd() +// Make work file into a regular file with: rf.CommitWorkFile() +// Or throw away the work file by destroying the instance without calling CommitWorkFile(). +// The temporary work file is closed / removed when an instance is destroyed without calling +// CommitWorkFile(). The original file, if CommitWorkFile() is not called, will be kept. +// +// For safer restoration of original file when commit fails, following 3 steps can be taken: +// 1. CreateBackupFile(): This renames an existing regular file into a separate backup file. +// 2. CommitWorkFile(): Rename the work file into the regular file. +// 3. RemoveBackupFile(): Removes the backup file +// If CommitWorkFile fails, client can call RestoreBackupFile() which will restore regular file from +// the backup. +class RestorableFile { +public: + // Creates invalid instance with no fd (=-1) and empty path. + RestorableFile(); + RestorableFile(RestorableFile&& other) = default; + ~RestorableFile(); + + // Passes all contents of other file into the current file. + // Files kept for the current file will be either deleted or committed depending on + // CommitWorkFile() and DisableCleanUp() calls made before this. + RestorableFile& operator=(RestorableFile&& other) = default; + + // Gets file descriptor for backing work (=temporary) file. If work file does not exist, it will + // return -1. + int fd() const { return unique_file_.fd(); } + + // Gets the path name for the regular file (not temporary file). + const std::string& path() const { return unique_file_.path(); } + + // Closes work file, deletes it and resets all internal states into default states. + void reset(); + + // Closes work file and closes all files including work file, backup file and regular file. + void ResetAndRemoveAllFiles(); + + // Creates a backup file by renaming existing regular file. This will return false if renaming + // fails. If regular file for renaming does not exist, it will return true. + bool CreateBackupFile(); + + // Closes existing work file and makes it a regular file. + // Note that the work file is closed and fd() will return -1 after this. path() will still + // return the original path. + // This will return false when committing fails (=cannot rename). Both the regular file and tmp + // file will be deleted when it fails. + bool CommitWorkFile(); + + // Cancels the commit and restores the backup file into the regular one. If renaming fails, + // it will return false. This returns true if the backup file does not exist. + bool RestoreBackupFile(); + + // Removes the backup file. + void RemoveBackupFile(); + + // Gets UniqueFile with the same path and fd() pointing to the work file. + const UniqueFile& GetUniqueFile() const; + + // Creates writable RestorableFile. This involves creating tmp file for writing. + static RestorableFile CreateWritableFile(const std::string& path, int permissions); + + // Removes the specified file together with tmp file generated as RestorableFile. + static void RemoveAllFiles(const std::string& path); + +private: + RestorableFile(int value, const std::string& path); + + // Used as a storage for work file fd and path string. + UniqueFile unique_file_; +}; + +} // namespace installd +} // namespace android + +#endif // ANDROID_INSTALLD_RESTORABLE_FILE_H diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp index b6616841f0..51c4589440 100644 --- a/cmds/installd/run_dex2oat.cpp +++ b/cmds/installd/run_dex2oat.cpp @@ -50,10 +50,6 @@ static constexpr bool kMinidebugInfoSystemPropertyDefault = false; static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info"; static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none"; -// Location of the JIT Zygote image. -static const char* kJitZygoteImage = - "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof"; - std::vector<std::string> SplitBySpaces(const std::string& str) { if (str.empty()) { return {}; @@ -84,9 +80,9 @@ void RunDex2Oat::Initialize(const UniqueFile& output_oat, int target_sdk_version, bool enable_hidden_api_checks, bool generate_compact_dex, - bool use_jitzygote_image, + bool use_jitzygote, const char* compilation_reason) { - PrepareBootImageFlags(use_jitzygote_image); + PrepareBootImageFlags(use_jitzygote); PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex, dex_metadata, profile, swap_fd, class_loader_context, @@ -112,14 +108,14 @@ void RunDex2Oat::Initialize(const UniqueFile& output_oat, RunDex2Oat::~RunDex2Oat() {} -void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote_image) { - std::string boot_image; - if (use_jitzygote_image) { - boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage); +void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote) { + if (use_jitzygote) { + // Don't pass a boot image because JIT Zygote should decide which image to use. Typically, + // it does not use any boot image on disk. + AddArg("--force-jit-zygote"); } else { - boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s"); + AddArg(MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s")); } - AddArg(boot_image); } void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat, diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h index 475e12413d..559244f2b7 100644 --- a/cmds/installd/run_dex2oat.h +++ b/cmds/installd/run_dex2oat.h @@ -50,13 +50,13 @@ class RunDex2Oat { int target_sdk_version, bool enable_hidden_api_checks, bool generate_compact_dex, - bool use_jitzygote_image, + bool use_jitzygote, const char* compilation_reason); void Exec(int exit_code); protected: - void PrepareBootImageFlags(bool use_jitzygote_image); + void PrepareBootImageFlags(bool use_jitzygote); void PrepareInputFileFlags(const UniqueFile& output_oat, const UniqueFile& output_vdex, const UniqueFile& output_image, diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp index 0a638cd51b..2a8135a037 100644 --- a/cmds/installd/run_dex2oat_test.cpp +++ b/cmds/installd/run_dex2oat_test.cpp @@ -114,7 +114,7 @@ class RunDex2OatTest : public testing::Test { int target_sdk_version = 0; bool enable_hidden_api_checks = false; bool generate_compact_dex = true; - bool use_jitzygote_image = false; + bool use_jitzygote = false; const char* compilation_reason = nullptr; }; @@ -175,6 +175,7 @@ class RunDex2OatTest : public testing::Test { default_expected_flags_["--swap-fd"] = FLAG_UNUSED; default_expected_flags_["--class-loader-context"] = FLAG_UNUSED; default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED; + default_expected_flags_["--boot-image"] = FLAG_UNUSED; // Arch default_expected_flags_["--instruction-set"] = "=arm64"; @@ -190,6 +191,7 @@ class RunDex2OatTest : public testing::Test { default_expected_flags_["--max-image-block-size"] = FLAG_UNUSED; default_expected_flags_["--very-large-app-threshold"] = FLAG_UNUSED; default_expected_flags_["--resolve-startup-const-strings"] = FLAG_UNUSED; + default_expected_flags_["--force-jit-zygote"] = FLAG_UNUSED; // Debug default_expected_flags_["--debuggable"] = FLAG_UNUSED; @@ -256,7 +258,7 @@ class RunDex2OatTest : public testing::Test { args->target_sdk_version, args->enable_hidden_api_checks, args->generate_compact_dex, - args->use_jitzygote_image, + args->use_jitzygote, args->compilation_reason); runner.Exec(/*exit_code=*/ 0); } @@ -557,5 +559,22 @@ TEST_F(RunDex2OatTest, ExtraFlags) { VerifyExpectedFlags(); } +TEST_F(RunDex2OatTest, UseJitZygoteImage) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->use_jitzygote = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--force-jit-zygote", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, BootImage) { + setSystemProperty("dalvik.vm.boot-image", "foo.art:bar.art"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--boot-image", "=foo.art:bar.art"); + VerifyExpectedFlags(); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 024e3af9d3..b3baca5c41 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -25,6 +25,8 @@ cc_test { static_libs: [ "libasync_safe", "libdiskusage", + "libext2_uuid", + "libgmock", "libinstalld", "liblog", ], @@ -53,7 +55,9 @@ cc_test { static_libs: [ "libasync_safe", "libdiskusage", + "libext2_uuid", "libinstalld", + "libziparchive", "liblog", "liblogwrap", ], @@ -92,14 +96,18 @@ cc_test { "libprocessgroup", "libselinux", "libutils", + "packagemanager_aidl-cpp", "server_configurable_flags", ], static_libs: [ "libasync_safe", "libdiskusage", + "libext2_uuid", "libinstalld", + "libziparchive", "liblog", "liblogwrap", + "libc++fs", ], test_config: "installd_service_test.xml", @@ -141,6 +149,7 @@ cc_test { static_libs: [ "libasync_safe", "libdiskusage", + "libext2_uuid", "libinstalld", "liblog", "liblogwrap", @@ -185,3 +194,24 @@ cc_test { "libotapreoptparameters", ], } + +cc_test { + name: "installd_file_test", + test_suites: ["device-tests"], + clang: true, + srcs: ["installd_file_test.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], + shared_libs: [ + "libbase", + "libcutils", + "libutils", + ], + static_libs: [ + "libext2_uuid", + "libinstalld", + "liblog", + ], +} diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp index 863cdfe55b..4976646595 100644 --- a/cmds/installd/tests/installd_cache_test.cpp +++ b/cmds/installd/tests/installd_cache_test.cpp @@ -42,6 +42,7 @@ constexpr int64_t kTbInBytes = 1024 * kGbInBytes; #define FLAG_FREE_CACHE_V2 InstalldNativeService::FLAG_FREE_CACHE_V2 #define FLAG_FREE_CACHE_V2_DEFY_QUOTA InstalldNativeService::FLAG_FREE_CACHE_V2_DEFY_QUOTA +#define FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES InstalldNativeService::FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES int get_property(const char *key, char *value, const char *default_value) { return property_get(key, value, default_value); @@ -122,6 +123,7 @@ protected: service = new InstalldNativeService(); testUuid = kTestUuid; + system("rm -rf /data/local/tmp/user"); system("mkdir -p /data/local/tmp/user/0"); } @@ -145,7 +147,7 @@ TEST_F(CacheTest, FreeCache_All) { EXPECT_EQ(0, exists("com.example/cache/foo/one")); EXPECT_EQ(0, exists("com.example/cache/foo/two")); - service->freeCache(testUuid, kTbInBytes, 0, + service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(0, exists("com.example/normal")); @@ -153,6 +155,62 @@ TEST_F(CacheTest, FreeCache_All) { EXPECT_EQ(-1, exists("com.example/cache/foo/two")); } +TEST_F(CacheTest, FreeCache_NonAggressive) { + LOG(INFO) << "FreeCache_NonAggressive"; + + mkdir("com.example"); + touch("com.example/normal", 1 * kMbInBytes, 60); + mkdir("com.example/cache"); + mkdir("com.example/cache/foo"); + touch("com.example/cache/foo/one", 65 * kMbInBytes, 60); + touch("com.example/cache/foo/two", 2 * kMbInBytes, 120); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(0, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); + + service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(-1, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); + + service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(-1, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); +} + +TEST_F(CacheTest, FreeCache_DefyTargetFreeBytes) { + LOG(INFO) << "FreeCache_DefyTargetFreeBytes"; + + mkdir("com.example"); + touch("com.example/normal", 1 * kMbInBytes, 60); + mkdir("com.example/cache"); + mkdir("com.example/cache/foo"); + touch("com.example/cache/foo/one", 65 * kMbInBytes, 60); + touch("com.example/cache/foo/two", 2 * kMbInBytes, 120); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(0, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); + + service->freeCache(testUuid, kMbInBytes, FLAG_FREE_CACHE_V2 + | FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(-1, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); + + service->freeCache(testUuid, kMbInBytes, FLAG_FREE_CACHE_V2 + | FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES); + + EXPECT_EQ(0, exists("com.example/normal")); + EXPECT_EQ(-1, exists("com.example/cache/foo/one")); + EXPECT_EQ(0, exists("com.example/cache/foo/two")); +} + TEST_F(CacheTest, FreeCache_Age) { LOG(INFO) << "FreeCache_Age"; @@ -162,13 +220,13 @@ TEST_F(CacheTest, FreeCache_Age) { touch("com.example/cache/foo/one", kMbInBytes, 60); touch("com.example/cache/foo/two", kMbInBytes, 120); - service->freeCache(testUuid, free() + kKbInBytes, 0, + service->freeCache(testUuid, free() + kKbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/one")); EXPECT_EQ(0, exists("com.example/cache/foo/two")); - service->freeCache(testUuid, free() + kKbInBytes, 0, + service->freeCache(testUuid, free() + kKbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/one")); @@ -196,7 +254,7 @@ TEST_F(CacheTest, FreeCache_Tombstone) { EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1")); EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2")); - service->freeCache(testUuid, kTbInBytes, 0, + service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/foo1")); @@ -218,7 +276,7 @@ TEST_F(CacheTest, FreeCache_Group) { setxattr("com.example/cache/foo", "user.cache_group"); - service->freeCache(testUuid, free() + kKbInBytes, 0, + service->freeCache(testUuid, free() + kKbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/foo1")); @@ -263,7 +321,7 @@ TEST_F(CacheTest, FreeCache_GroupTombstone) { setxattr("com.example/cache/tomb", "user.cache_tombstone"); setxattr("com.example/cache/tomb/group", "user.cache_group"); - service->freeCache(testUuid, free() + kKbInBytes, 0, + service->freeCache(testUuid, free() + kKbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1")); @@ -284,7 +342,7 @@ TEST_F(CacheTest, FreeCache_GroupTombstone) { EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2")); - service->freeCache(testUuid, free() + kKbInBytes, 0, + service->freeCache(testUuid, free() + kKbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, size("com.example/cache/group/file1")); @@ -305,7 +363,7 @@ TEST_F(CacheTest, FreeCache_GroupTombstone) { EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2")); - service->freeCache(testUuid, kTbInBytes, 0, + service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, size("com.example/cache/group/file1")); diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 7e7e513568..4eb30e2542 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -50,6 +50,8 @@ using android::base::unique_fd; namespace android { namespace installd { +constexpr int kTimeoutMs = 60000; + // TODO(calin): try to dedup this code. #if defined(__arm__) static const std::string kRuntimeIsa = "arm"; @@ -232,6 +234,7 @@ protected: virtual void TearDown() { if (!kDebug) { + service_->controlDexOptBlocking(false); service_->destroyAppData( volume_uuid_, package_name_, kTestUserId, kAppDataFlags, ce_data_inode_); run_cmd("rm -rf " + app_apk_dir_); @@ -286,6 +289,7 @@ protected: kTestUserId, kAppDataFlags, kTestAppUid, + 0 /* previousAppId */, se_info_, kOSdkVersion, &ce_data_inode_); @@ -347,7 +351,7 @@ protected: void CompileSecondaryDex(const std::string& path, int32_t dex_storage_flag, bool should_binder_call_succeed, bool should_dex_be_compiled = true, /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1, - const char* class_loader_context = nullptr) { + const char* class_loader_context = nullptr, bool expect_completed = true) { if (uid == -1) { uid = kTestAppUid; } @@ -364,6 +368,7 @@ protected: std::optional<std::string> dm_path; std::optional<std::string> compilation_reason; + bool completed = false; binder::Status result = service_->dexopt(path, uid, package_name_, @@ -379,8 +384,10 @@ protected: target_sdk_version, profile_name, dm_path, - compilation_reason); + compilation_reason, + &completed); ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str(); + ASSERT_EQ(expect_completed, completed); int expected_access = should_dex_be_compiled ? 0 : -1; std::string odex = GetSecondaryDexArtifact(path, "odex"); std::string vdex = GetSecondaryDexArtifact(path, "vdex"); @@ -431,6 +438,11 @@ protected: ASSERT_EQ(mode, st.st_mode); } + void AssertNoFile(const std::string& file) { + struct stat st; + ASSERT_EQ(-1, stat(file.c_str(), &st)); + } + void CompilePrimaryDexOk(std::string compiler_filter, int32_t dex_flags, const char* oat_dir, @@ -447,6 +459,7 @@ protected: dm_path, downgrade, true, + true, binder_result); } @@ -466,6 +479,27 @@ protected: dm_path, downgrade, false, + true, + binder_result); + } + + void CompilePrimaryDexCancelled(std::string compiler_filter, + int32_t dex_flags, + const char* oat_dir, + int32_t uid, + int32_t dexopt_needed, + binder::Status* binder_result = nullptr, + const char* dm_path = nullptr, + bool downgrade = false) { + CompilePrimaryDex(compiler_filter, + dex_flags, + oat_dir, + uid, + dexopt_needed, + dm_path, + downgrade, + true, // should_binder_call_succeed + false, // expect_completed binder_result); } @@ -477,6 +511,7 @@ protected: const char* dm_path, bool downgrade, bool should_binder_call_succeed, + bool expect_completed, /*out */ binder::Status* binder_result) { std::optional<std::string> out_path = oat_dir ? std::make_optional<std::string>(oat_dir) : std::nullopt; std::string class_loader_context = "PCL[]"; @@ -491,6 +526,7 @@ protected: dm_path_opt, &prof_result)); ASSERT_TRUE(prof_result); + bool completed = false; binder::Status result = service_->dexopt(apk_path_, uid, package_name_, @@ -506,8 +542,10 @@ protected: target_sdk_version, profile_name, dm_path_opt, - compilation_reason); + compilation_reason, + &completed); ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str(); + ASSERT_EQ(expect_completed, completed); if (!should_binder_call_succeed) { if (binder_result != nullptr) { @@ -525,11 +563,20 @@ protected: bool is_public = (dex_flags & DEXOPT_PUBLIC) != 0; mode_t mode = S_IFREG | (is_public ? 0644 : 0640); - CheckFileAccess(odex, kSystemUid, uid, mode); - CheckFileAccess(vdex, kSystemUid, uid, mode); + if (expect_completed) { + CheckFileAccess(odex, kSystemUid, uid, mode); + CheckFileAccess(vdex, kSystemUid, uid, mode); + } else { + AssertNoFile(odex); + AssertNoFile(vdex); + } if (compiler_filter == "speed-profile") { - CheckFileAccess(art, kSystemUid, uid, mode); + if (expect_completed) { + CheckFileAccess(art, kSystemUid, uid, mode); + } else { + AssertNoFile(art); + } } if (binder_result != nullptr) { *binder_result = result; @@ -590,6 +637,7 @@ protected: int64_t bytes_freed; binder::Status result = service_->deleteOdex( + package_name_, apk_path_, kRuntimeIsa, in_dalvik_cache ? std::nullopt : std::make_optional<std::string>(app_oat_dir_.c_str()), @@ -602,6 +650,36 @@ protected: ASSERT_EQ(expected_bytes_freed, bytes_freed); } + + void checkVisibility(bool in_dalvik_cache, int32_t expected_visibility) { + int32_t visibility; + ASSERT_BINDER_SUCCESS(service_->getOdexVisibility(package_name_, apk_path_, kRuntimeIsa, + in_dalvik_cache + ? std::nullopt + : std::make_optional<std::string>( + app_oat_dir_.c_str()), + &visibility)); + EXPECT_EQ(visibility, expected_visibility); + } + + void TestGetOdexVisibility(bool in_dalvik_cache) { + const char* oat_dir = in_dalvik_cache ? nullptr : app_oat_dir_.c_str(); + + checkVisibility(in_dalvik_cache, ODEX_NOT_FOUND); + + CompilePrimaryDexOk("speed-profile", + DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC | + DEXOPT_GENERATE_APP_IMAGE, + oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH, + /*binder_result=*/nullptr, empty_dm_file_.c_str()); + checkVisibility(in_dalvik_cache, ODEX_IS_PUBLIC); + + CompilePrimaryDexOk("speed-profile", + DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE, + oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH, + /*binder_result=*/nullptr, empty_dm_file_.c_str()); + checkVisibility(in_dalvik_cache, ODEX_IS_PRIVATE); + } }; @@ -684,7 +762,7 @@ TEST_F(DexoptTest, DexoptPrimaryPublic) { TEST_F(DexoptTest, DexoptPrimaryPublicCreateOatDir) { LOG(INFO) << "DexoptPrimaryPublic"; - ASSERT_BINDER_SUCCESS(service_->createOatDir(app_oat_dir_, kRuntimeIsa)); + ASSERT_BINDER_SUCCESS(service_->createOatDir(package_name_, app_oat_dir_, kRuntimeIsa)); CompilePrimaryDexOk("verify", DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC, app_oat_dir_.c_str(), @@ -750,6 +828,28 @@ TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) { empty_dm_file_.c_str()); } +TEST_F(DexoptTest, DexoptBlockPrimary) { + LOG(INFO) << "DexoptPrimaryPublic"; + service_->controlDexOptBlocking(true); + CompilePrimaryDexCancelled("verify", + DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH, nullptr, nullptr); + service_->controlDexOptBlocking(false); +} + +TEST_F(DexoptTest, DexoptUnblockPrimary) { + LOG(INFO) << "DexoptPrimaryPublic"; + service_->controlDexOptBlocking(true); + service_->controlDexOptBlocking(false); + CompilePrimaryDexOk("verify", + DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH, nullptr, nullptr); +} + TEST_F(DexoptTest, DeleteDexoptArtifactsData) { LOG(INFO) << "DeleteDexoptArtifactsData"; TestDeleteOdex(/*in_dalvik_cache=*/ false); @@ -760,6 +860,16 @@ TEST_F(DexoptTest, DeleteDexoptArtifactsDalvikCache) { TestDeleteOdex(/*in_dalvik_cache=*/ true); } +TEST_F(DexoptTest, GetOdexVisibilityData) { + LOG(INFO) << "GetOdexVisibilityData"; + TestGetOdexVisibility(/*in_dalvik_cache=*/false); +} + +TEST_F(DexoptTest, GetOdexVisibilityDalvikCache) { + LOG(INFO) << "GetOdexVisibilityDalvikCache"; + TestGetOdexVisibility(/*in_dalvik_cache=*/true); +} + TEST_F(DexoptTest, ResolveStartupConstStrings) { LOG(INFO) << "DexoptDex2oatResolveStartupStrings"; const std::string property = "persist.device_config.runtime.dex2oat_resolve_startup_strings"; @@ -1005,7 +1115,7 @@ class ProfileTest : public DexoptTest { _exit(0); } /* parent */ - ASSERT_TRUE(WIFEXITED(wait_child(pid))); + ASSERT_TRUE(WIFEXITED(wait_child_with_timeout(pid, kTimeoutMs))); } void mergePackageProfiles(const std::string& package_name, @@ -1035,13 +1145,16 @@ class ProfileTest : public DexoptTest { ASSERT_TRUE(AreFilesEqual(ref_profile_content, ref_profile_)); } - // TODO(calin): add dex metadata tests once the ART change is merged. void preparePackageProfile(const std::string& package_name, const std::string& profile_name, - bool expected_result) { + bool has_dex_metadata, bool has_user_id, bool expected_result) { bool result; - ASSERT_BINDER_SUCCESS(service_->prepareAppProfile( - package_name, kTestUserId, kTestAppId, profile_name, apk_path_, - /*dex_metadata*/ {}, &result)); + ASSERT_BINDER_SUCCESS( + service_->prepareAppProfile(package_name, has_user_id ? kTestUserId : USER_NULL, + kTestAppId, profile_name, apk_path_, + has_dex_metadata ? std::make_optional<std::string>( + empty_dm_file_) + : std::nullopt, + &result)); ASSERT_EQ(expected_result, result); if (!expected_result) { @@ -1049,16 +1162,29 @@ class ProfileTest : public DexoptTest { return; } - std::string code_path_cur_prof = create_current_profile_path( - kTestUserId, package_name, profile_name, /*is_secondary_dex*/ false); - std::string code_path_ref_profile = create_reference_profile_path(package_name, - profile_name, /*is_secondary_dex*/ false); + std::string code_path_cur_prof = + create_current_profile_path(kTestUserId, package_name, profile_name, + /*is_secondary_dex*/ false); + std::string code_path_ref_profile = + create_reference_profile_path(package_name, profile_name, + /*is_secondary_dex*/ false); - // Check that we created the current profile. - CheckFileAccess(code_path_cur_prof, kTestAppUid, kTestAppUid, 0600 | S_IFREG); + if (has_user_id) { + // Check that we created the current profile. + CheckFileAccess(code_path_cur_prof, kTestAppUid, kTestAppUid, 0600 | S_IFREG); + } else { + // Without a user ID, we don't generate a current profile. + ASSERT_EQ(-1, access(code_path_cur_prof.c_str(), R_OK)); + } - // Without dex metadata we don't generate a reference profile. - ASSERT_EQ(-1, access(code_path_ref_profile.c_str(), R_OK)); + if (has_dex_metadata) { + int32_t uid = has_user_id ? kTestAppUid : multiuser_get_uid(USER_SYSTEM, kTestAppId); + // Check that we created the reference profile. + CheckFileAccess(code_path_ref_profile, uid, uid, 0640 | S_IFREG); + } else { + // Without dex metadata, we don't generate a reference profile. + ASSERT_EQ(-1, access(code_path_ref_profile.c_str(), R_OK)); + } } protected: @@ -1191,6 +1317,7 @@ TEST_F(ProfileTest, ProfileDirOkAfterFixup) { kTestUserId, kAppDataFlags, kTestAppUid, + 0 /* previousAppId */, se_info_, kOSdkVersion, &ce_data_inode_)); @@ -1202,12 +1329,32 @@ TEST_F(ProfileTest, ProfileDirOkAfterFixup) { TEST_F(ProfileTest, ProfilePrepareOk) { LOG(INFO) << "ProfilePrepareOk"; - preparePackageProfile(package_name_, "split.prof", /*expected_result*/ true); + preparePackageProfile(package_name_, "split.prof", /*has_dex_metadata*/ true, + /*has_user_id*/ true, /*expected_result*/ true); +} + +TEST_F(ProfileTest, ProfilePrepareOkNoUser) { + LOG(INFO) << "ProfilePrepareOk"; + preparePackageProfile(package_name_, "split.prof", /*has_dex_metadata*/ true, + /*has_user_id*/ false, /*expected_result*/ true); +} + +TEST_F(ProfileTest, ProfilePrepareOkNoDm) { + LOG(INFO) << "ProfilePrepareOk"; + preparePackageProfile(package_name_, "split.prof", /*has_dex_metadata*/ false, + /*has_user_id*/ true, /*expected_result*/ true); +} + +TEST_F(ProfileTest, ProfilePrepareOkNoUserNoDm) { + LOG(INFO) << "ProfilePrepareOk"; + preparePackageProfile(package_name_, "split.prof", /*has_dex_metadata*/ false, + /*has_user_id*/ false, /*expected_result*/ true); } TEST_F(ProfileTest, ProfilePrepareFailInvalidPackage) { LOG(INFO) << "ProfilePrepareFailInvalidPackage"; - preparePackageProfile("not.there.package", "split.prof", /*expected_result*/ false); + preparePackageProfile("not.there.package", "split.prof", /*has_dex_metadata*/ true, + /*has_user_id*/ true, /*expected_result*/ false); } TEST_F(ProfileTest, ProfilePrepareFailProfileChangedUid) { @@ -1215,7 +1362,8 @@ TEST_F(ProfileTest, ProfilePrepareFailProfileChangedUid) { SetupProfiles(/*setup_ref*/ false); // Change the uid on the profile to trigger a failure. ::chown(cur_profile_.c_str(), kTestAppUid + 1, kTestAppGid + 1); - preparePackageProfile(package_name_, "primary.prof", /*expected_result*/ false); + preparePackageProfile(package_name_, "primary.prof", /*has_dex_metadata*/ true, + /*has_user_id*/ true, /*expected_result*/ false); } @@ -1254,6 +1402,7 @@ class BootProfileTest : public ProfileTest { kTestUserId, kAppDataFlags, kTestAppUid, + 0 /* previousAppId */, se_info_, kOSdkVersion, &ce_data_inode)); @@ -1307,7 +1456,7 @@ class BootProfileTest : public ProfileTest { _exit(0); } /* parent */ - ASSERT_TRUE(WIFEXITED(wait_child(pid))); + ASSERT_TRUE(WIFEXITED(wait_child_with_timeout(pid, kTimeoutMs))); } protected: std::string intial_android_profiles_dir; diff --git a/cmds/installd/tests/installd_file_test.cpp b/cmds/installd/tests/installd_file_test.cpp new file mode 100644 index 0000000000..00fb30875f --- /dev/null +++ b/cmds/installd/tests/installd_file_test.cpp @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2021 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/file.h> +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <stdlib.h> +#include <string.h> + +#include "restorable_file.h" +#include "unique_file.h" +#include "utils.h" + +#undef LOG_TAG +#define LOG_TAG "installd_file_test" + +namespace { + +constexpr char kFileTestDir[] = "/data/local/tmp/installd_file_test_data"; +constexpr char kTmpFileSuffix[] = ".tmp"; +constexpr char kBackupFileSuffix[] = ".backup"; + +void UnlinkWithAssert(const std::string& path) { + ASSERT_EQ(0, unlink(path.c_str())); +} + +} // namespace + +namespace android { +namespace installd { + +// Add these as macros as functions make it hard to tell where the failure has happened. +#define ASSERT_FILE_NOT_EXISTING(path) \ + { \ + struct stat st; \ + ASSERT_NE(0, ::stat(path.c_str(), &st)); \ + } +#define ASSERT_FILE_EXISTING(path) \ + { \ + struct stat st; \ + ASSERT_EQ(0, ::stat(path.c_str(), &st)); \ + } +#define ASSERT_FILE_CONTENT(path, expectedContent) ASSERT_EQ(expectedContent, ReadTestFile(path)) +#define ASSERT_FILE_OPEN(path, fd) \ + { \ + fd = open(path.c_str(), O_RDWR); \ + ASSERT_TRUE(fd >= 0); \ + } +#define ASSERT_WRITE_TO_FD(fd, content) \ + ASSERT_TRUE(android::base::WriteStringToFd(content, android::base::borrowed_fd(fd))) + +class FileTest : public testing::Test { +protected: + virtual void SetUp() { + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(nullptr); + + ASSERT_EQ(0, create_dir_if_needed(kFileTestDir, 0777)); + } + + virtual void TearDown() { + system(android::base::StringPrintf("rm -rf %s", kFileTestDir).c_str()); + } + + std::string GetTestFilePath(const std::string& fileName) { + return android::base::StringPrintf("%s/%s", kFileTestDir, fileName.c_str()); + } + + void CreateTestFileWithContents(const std::string& path, const std::string& content) { + ALOGI("CreateTestFileWithContents:%s", path.c_str()); + ASSERT_TRUE(android::base::WriteStringToFile(content, path)); + } + + std::string GetTestName() { + std::string name(testing::UnitTest::GetInstance()->current_test_info()->name()); + return name; + } + + std::string ReadTestFile(const std::string& path) { + std::string content; + bool r = android::base::ReadFileToString(path, &content); + if (!r) { + PLOG(ERROR) << "Cannot read file:" << path; + } + return content; + } +}; + +TEST_F(FileTest, TestUniqueFileMoveConstruction) { + const int fd = 101; + std::string testFile = GetTestFilePath(GetTestName()); + UniqueFile uf1(fd, testFile); + uf1.DisableAutoClose(); + + UniqueFile uf2(std::move(uf1)); + + ASSERT_EQ(fd, uf2.fd()); + ASSERT_EQ(testFile, uf2.path()); +} + +TEST_F(FileTest, TestUniqueFileAssignment) { + const int fd1 = 101; + const int fd2 = 102; + std::string testFile1 = GetTestFilePath(GetTestName()); + std::string testFile2 = GetTestFilePath(GetTestName() + "2"); + + UniqueFile uf1(fd1, testFile1); + uf1.DisableAutoClose(); + + UniqueFile uf2(fd2, testFile2); + uf2.DisableAutoClose(); + + ASSERT_EQ(fd2, uf2.fd()); + ASSERT_EQ(testFile2, uf2.path()); + + uf2 = std::move(uf1); + + ASSERT_EQ(fd1, uf2.fd()); + ASSERT_EQ(testFile1, uf2.path()); +} + +TEST_F(FileTest, TestUniqueFileCleanup) { + std::string testFile = GetTestFilePath(GetTestName()); + CreateTestFileWithContents(testFile, "OriginalContent"); + + int fd; + ASSERT_FILE_OPEN(testFile, fd); + + { UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert); } + + ASSERT_FILE_NOT_EXISTING(testFile); +} + +TEST_F(FileTest, TestUniqueFileNoCleanup) { + std::string testFile = GetTestFilePath(GetTestName()); + CreateTestFileWithContents(testFile, "OriginalContent"); + + int fd; + ASSERT_FILE_OPEN(testFile, fd); + + { + UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert); + uf.DisableCleanup(); + } + + ASSERT_FILE_CONTENT(testFile, "OriginalContent"); +} + +TEST_F(FileTest, TestUniqueFileFd) { + std::string testFile = GetTestFilePath(GetTestName()); + CreateTestFileWithContents(testFile, "OriginalContent"); + + int fd; + ASSERT_FILE_OPEN(testFile, fd); + + UniqueFile uf(fd, testFile, UnlinkWithAssert); + + ASSERT_EQ(fd, uf.fd()); + + uf.reset(); + + ASSERT_EQ(-1, uf.fd()); +} + +TEST_F(FileTest, TestRestorableFileMoveConstruction) { + std::string testFile = GetTestFilePath(GetTestName()); + + RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile, 0600); + int fd = rf1.fd(); + + RestorableFile rf2(std::move(rf1)); + + ASSERT_EQ(fd, rf2.fd()); + ASSERT_EQ(testFile, rf2.path()); +} + +TEST_F(FileTest, TestRestorableFileAssignment) { + std::string testFile1 = GetTestFilePath(GetTestName()); + std::string testFile2 = GetTestFilePath(GetTestName() + "2"); + + RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile1, 0600); + int fd1 = rf1.fd(); + + RestorableFile rf2 = RestorableFile::CreateWritableFile(testFile2, 0600); + int fd2 = rf2.fd(); + + ASSERT_EQ(fd2, rf2.fd()); + ASSERT_EQ(testFile2, rf2.path()); + + rf2 = std::move(rf1); + + ASSERT_EQ(fd1, rf2.fd()); + ASSERT_EQ(testFile1, rf2.path()); +} + +TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithReset) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + + ASSERT_FILE_EXISTING(tmpFile); + + const UniqueFile& uf = rf.GetUniqueFile(); + + ASSERT_EQ(rf.fd(), uf.fd()); + ASSERT_EQ(rf.path(), uf.path()); + + rf.reset(); + + ASSERT_EQ(rf.fd(), uf.fd()); + ASSERT_EQ(rf.path(), uf.path()); + ASSERT_EQ(-1, rf.fd()); + ASSERT_TRUE(rf.path().empty()); + } +} + +TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithCommit) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + + ASSERT_FILE_EXISTING(tmpFile); + + const UniqueFile& uf = rf.GetUniqueFile(); + + ASSERT_EQ(rf.fd(), uf.fd()); + ASSERT_EQ(rf.path(), uf.path()); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(backupFile); + + rf.CommitWorkFile(); + + ASSERT_EQ(rf.fd(), uf.fd()); + ASSERT_EQ(rf.path(), uf.path()); + ASSERT_EQ(-1, rf.fd()); + ASSERT_EQ(testFile, rf.path()); + } +} + +TEST_F(FileTest, TestRestorableFileNewFileNotCommitted) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + + ASSERT_FILE_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); +} + +TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginal) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + ASSERT_FILE_EXISTING(testFile); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_CONTENT(testFile, "OriginalContent"); +} + +TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginalAndOldTmp) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + ASSERT_FILE_EXISTING(testFile); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_CONTENT(testFile, "OriginalContent"); +} + +TEST_F(FileTest, TestRestorableFileNewFileCommitted) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + + ASSERT_FILE_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(backupFile); + + ASSERT_TRUE(rf.CommitWorkFile()); + rf.RemoveBackupFile(); + + ASSERT_FILE_CONTENT(testFile, "NewContent"); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(backupFile); + ASSERT_FILE_CONTENT(testFile, "NewContent"); +} + +TEST_F(FileTest, TestRestorableFileCommittedWithOriginal) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_EXISTING(backupFile); + + ASSERT_TRUE(rf.CommitWorkFile()); + + ASSERT_FILE_EXISTING(backupFile); + ASSERT_FILE_CONTENT(testFile, "NewContent"); + + rf.RemoveBackupFile(); + + ASSERT_FILE_NOT_EXISTING(backupFile); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_CONTENT(testFile, "NewContent"); +} + +TEST_F(FileTest, TestRestorableFileCommittedWithOriginalAndOldTmp) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + + ASSERT_TRUE(rf.CommitWorkFile()); + + ASSERT_FILE_CONTENT(testFile, "NewContent"); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_CONTENT(testFile, "NewContent"); +} + +TEST_F(FileTest, TestRestorableFileCommitFailureNoOriginal) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(backupFile); + + // Now remove tmp file to force commit failure. + close(rf.fd()); + ASSERT_EQ(0, unlink(tmpFile.c_str())); + ASSERT_FILE_NOT_EXISTING(tmpFile); + + ASSERT_FALSE(rf.CommitWorkFile()); + + ASSERT_EQ(-1, rf.fd()); + ASSERT_EQ(testFile, rf.path()); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(tmpFile); + + ASSERT_TRUE(rf.RestoreBackupFile()); + } + + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(backupFile); +} + +TEST_F(FileTest, TestRestorableFileCommitFailureAndRollback) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_EXISTING(backupFile); + + // Now remove tmp file to force commit failure. + close(rf.fd()); + ASSERT_EQ(0, unlink(tmpFile.c_str())); + ASSERT_FILE_NOT_EXISTING(tmpFile); + + ASSERT_FALSE(rf.CommitWorkFile()); + + ASSERT_EQ(-1, rf.fd()); + ASSERT_EQ(testFile, rf.path()); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_EXISTING(backupFile); + + ASSERT_TRUE(rf.RestoreBackupFile()); + } + + ASSERT_FILE_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(backupFile); +} + +TEST_F(FileTest, TestRestorableFileResetAndRemoveAllFiles) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_EXISTING(backupFile); + + rf.ResetAndRemoveAllFiles(); + + ASSERT_EQ(-1, rf.fd()); + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(backupFile); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(backupFile); +} + +TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentFile) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + + RestorableFile::RemoveAllFiles(testFile); + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(backupFile); +} + +TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentAndTmpFile) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + CreateTestFileWithContents(testFile + kTmpFileSuffix, "TmpContent"); + + RestorableFile::RemoveAllFiles(testFile); + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(backupFile); +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/tests/installd_file_test.xml b/cmds/installd/tests/installd_file_test.xml new file mode 100644 index 0000000000..5ec6e3f019 --- /dev/null +++ b/cmds/installd/tests/installd_file_test.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 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. +--> +<!-- Note: this is derived from the autogenerated configuration. We require + root support. --> +<configuration description="Runs installd_file_test."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" + value="installd_file_test->/data/local/tmp/installd_file_test" /> + </target_preparer> + + <!-- The test requires root for file access (rollback. --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="installd_file_test" /> + </test> +</configuration> diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index b910ac103b..f86f1d55fa 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -32,16 +32,20 @@ #include <android-base/stringprintf.h> #include <cutils/properties.h> #include <gtest/gtest.h> +#include <filesystem> +#include <fstream> #include <android/content/pm/IPackageManagerNative.h> #include <binder/IServiceManager.h> #include "InstalldNativeService.h" +#include "binder/Status.h" #include "binder_test_utils.h" #include "dexopt.h" #include "globals.h" #include "utils.h" using android::base::StringPrintf; +using std::filesystem::is_empty; namespace android { std::string get_package_name(uid_t uid) { @@ -74,7 +78,20 @@ std::string get_package_name(uid_t uid) { } namespace installd { -constexpr const char* kTestUuid = "TEST"; +static constexpr const char* kTestUuid = "TEST"; +static const std::string kTestPath = "/data/local/tmp"; +static constexpr const uid_t kNobodyUid = 9999; +static constexpr const uid_t kSystemUid = 1000; +static constexpr const int32_t kTestUserId = 0; +static constexpr const uid_t kTestAppId = 19999; +static constexpr const int FLAG_STORAGE_SDK = InstalldNativeService::FLAG_STORAGE_SDK; +static constexpr const int FLAG_CLEAR_CACHE_ONLY = InstalldNativeService::FLAG_CLEAR_CACHE_ONLY; +static constexpr const int FLAG_CLEAR_CODE_CACHE_ONLY = + InstalldNativeService::FLAG_CLEAR_CODE_CACHE_ONLY; + +const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId); +const gid_t kTestCacheGid = multiuser_get_cache_gid(kTestUserId, kTestAppId); +const uid_t kTestSdkSandboxUid = multiuser_get_sdk_sandbox_uid(kTestUserId, kTestAppId); #define FLAG_FORCE InstalldNativeService::FLAG_FORCE @@ -96,23 +113,27 @@ bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *ins return create_cache_path_default(path, src, instruction_set); } -static std::string get_full_path(const char* path) { - return StringPrintf("/data/local/tmp/user/0/%s", path); +static std::string get_full_path(const std::string& path) { + return StringPrintf("%s/%s", kTestPath.c_str(), path.c_str()); } -static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) { +static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) { const std::string fullPath = get_full_path(path); EXPECT_EQ(::mkdir(fullPath.c_str(), mode), 0); EXPECT_EQ(::chown(fullPath.c_str(), owner, group), 0); 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 std::string& 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 std::string& path, uid_t owner, gid_t group, mode_t mode) { + EXPECT_EQ(::close(create(path.c_str(), owner, group, mode)), 0); } static int stat_gid(const char* path) { @@ -127,6 +148,35 @@ static int stat_mode(const char* path) { return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID); } +static bool exists(const std::string& 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(const std::string& rootDirectory) { + return find_file((kTestPath + rootDirectory).c_str(), [](const std::string& name, bool is_dir) { + return is_dir && is_renamed_deleted_dir(name); + }); +} + class ServiceTest : public testing::Test { protected: InstalldNativeService* service; @@ -138,69 +188,206 @@ protected: service = new InstalldNativeService(); testUuid = kTestUuid; + system("rm -rf /data/local/tmp/user"); + system("rm -rf /data/local/tmp/misc_ce"); + system("rm -rf /data/local/tmp/misc_de"); system("mkdir -p /data/local/tmp/user/0"); - + system("mkdir -p /data/local/tmp/misc_ce/0/sdksandbox"); + system("mkdir -p /data/local/tmp/misc_de/0/sdksandbox"); init_globals_from_data_and_root(); } virtual void TearDown() { delete service; system("rm -rf /data/local/tmp/user"); + system("rm -rf /data/local/tmp/misc_ce"); + system("rm -rf /data/local/tmp/misc_de"); } }; TEST_F(ServiceTest, FixupAppData_Upgrade) { LOG(INFO) << "FixupAppData_Upgrade"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/normal", 10000, 10000, 0700); - mkdir("com.example/cache", 10000, 10000, 0700); - touch("com.example/cache/file", 10000, 10000, 0700); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/normal", 10000, 10000, 0700); + mkdir("user/0/com.example/cache", 10000, 10000, 0700); + touch("user/0/com.example/cache/file", 10000, 10000, 0700); service->fixupAppData(testUuid, 0); - EXPECT_EQ(10000, stat_gid("com.example/normal")); - EXPECT_EQ(20000, stat_gid("com.example/cache")); - EXPECT_EQ(20000, stat_gid("com.example/cache/file")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/normal")); + EXPECT_EQ(20000, stat_gid("user/0/com.example/cache")); + EXPECT_EQ(20000, stat_gid("user/0/com.example/cache/file")); - EXPECT_EQ(0700, stat_mode("com.example/normal")); - EXPECT_EQ(02771, stat_mode("com.example/cache")); - EXPECT_EQ(0700, stat_mode("com.example/cache/file")); + EXPECT_EQ(0700, stat_mode("user/0/com.example/normal")); + EXPECT_EQ(02771, stat_mode("user/0/com.example/cache")); + EXPECT_EQ(0700, stat_mode("user/0/com.example/cache/file")); } TEST_F(ServiceTest, FixupAppData_Moved) { LOG(INFO) << "FixupAppData_Moved"; - 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("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0700); + mkdir("user/0/com.example/bar", 10000, 20000, 0700); + touch("user/0/com.example/bar/file", 10000, 20000, 0700); service->fixupAppData(testUuid, 0); - EXPECT_EQ(10000, stat_gid("com.example/foo")); - EXPECT_EQ(20000, stat_gid("com.example/foo/file")); - EXPECT_EQ(10000, stat_gid("com.example/bar")); - EXPECT_EQ(10000, stat_gid("com.example/bar/file")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/foo")); + EXPECT_EQ(20000, stat_gid("user/0/com.example/foo/file")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/bar")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/bar/file")); service->fixupAppData(testUuid, FLAG_FORCE); - EXPECT_EQ(10000, stat_gid("com.example/foo")); - EXPECT_EQ(10000, stat_gid("com.example/foo/file")); - EXPECT_EQ(10000, stat_gid("com.example/bar")); - EXPECT_EQ(10000, stat_gid("com.example/bar/file")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/foo")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/foo/file")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/bar")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/bar/file")); +} + +TEST_F(ServiceTest, DestroyUserData) { + LOG(INFO) << "DestroyUserData"; + + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0700); + mkdir("user/0/com.example/bar", 10000, 20000, 0700); + touch("user/0/com.example/bar/file", 10000, 20000, 0700); + + EXPECT_TRUE(exists("user/0/com.example/foo")); + EXPECT_TRUE(exists("user/0/com.example/foo/file")); + EXPECT_TRUE(exists("user/0/com.example/bar")); + EXPECT_TRUE(exists("user/0/com.example/bar/file")); + + service->destroyUserData(testUuid, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE); + + EXPECT_FALSE(exists("user/0/com.example/foo")); + EXPECT_FALSE(exists("user/0/com.example/foo/file")); + EXPECT_FALSE(exists("user/0/com.example/bar")); + EXPECT_FALSE(exists("user/0/com.example/bar/file")); + + EXPECT_FALSE(exists_renamed_deleted_dir("/user/0")); +} + +TEST_F(ServiceTest, DestroyAppData) { + LOG(INFO) << "DestroyAppData"; + + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0700); + mkdir("user/0/com.example/bar", 10000, 20000, 0700); + touch("user/0/com.example/bar/file", 10000, 20000, 0700); + + EXPECT_TRUE(exists("user/0/com.example/foo")); + EXPECT_TRUE(exists("user/0/com.example/foo/file")); + EXPECT_TRUE(exists("user/0/com.example/bar")); + EXPECT_TRUE(exists("user/0/com.example/bar/file")); + + service->destroyAppData(testUuid, "com.example", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, 0); + + EXPECT_FALSE(exists("user/0/com.example/foo")); + EXPECT_FALSE(exists("user/0/com.example/foo/file")); + EXPECT_FALSE(exists("user/0/com.example/bar")); + EXPECT_FALSE(exists("user/0/com.example/bar/file")); + + EXPECT_FALSE(exists_renamed_deleted_dir("/user/0")); +} + +TEST_F(ServiceTest, CleanupInvalidPackageDirs) { + LOG(INFO) << "CleanupInvalidPackageDirs"; + + std::string rootDirectoryPrefix[] = {"user/0", "misc_ce/0/sdksandbox", "misc_de/0/sdksandbox"}; + for (auto& prefix : rootDirectoryPrefix) { + mkdir(prefix + "/5b14b6458a44==deleted==", 10000, 10000, 0700); + mkdir(prefix + "/5b14b6458a44==deleted==/foo", 10000, 10000, 0700); + touch(prefix + "/5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700); + mkdir(prefix + "/5b14b6458a44==deleted==/bar", 10000, 20000, 0700); + touch(prefix + "/5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700); + + auto fd = create(prefix + "/5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700); + + mkdir(prefix + "/b14b6458a44NOTdeleted", 10000, 10000, 0700); + mkdir(prefix + "/b14b6458a44NOTdeleted/foo", 10000, 10000, 0700); + touch(prefix + "/b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700); + mkdir(prefix + "/b14b6458a44NOTdeleted/bar", 10000, 20000, 0700); + touch(prefix + "/b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700); + + mkdir(prefix + "/com.example", 10000, 10000, 0700); + mkdir(prefix + "/com.example/foo", 10000, 10000, 0700); + touch(prefix + "/com.example/foo/file", 10000, 20000, 0700); + mkdir(prefix + "/com.example/bar", 10000, 20000, 0700); + touch(prefix + "/com.example/bar/file", 10000, 20000, 0700); + + mkdir(prefix + "/==deleted==", 10000, 10000, 0700); + mkdir(prefix + "/==deleted==/foo", 10000, 10000, 0700); + touch(prefix + "/==deleted==/foo/file", 10000, 20000, 0700); + mkdir(prefix + "/==deleted==/bar", 10000, 20000, 0700); + touch(prefix + "/==deleted==/bar/file", 10000, 20000, 0700); + + EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/foo")); + EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/foo/file")); + EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar")); + EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar/file")); + EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar/opened_file")); + + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo/file")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar/file")); + + EXPECT_TRUE(exists(prefix + "/com.example/foo")); + EXPECT_TRUE(exists(prefix + "/com.example/foo/file")); + EXPECT_TRUE(exists(prefix + "/com.example/bar")); + EXPECT_TRUE(exists(prefix + "/com.example/bar/file")); + + EXPECT_TRUE(exists(prefix + "/==deleted==/foo")); + EXPECT_TRUE(exists(prefix + "/==deleted==/foo/file")); + EXPECT_TRUE(exists(prefix + "/==deleted==/bar")); + EXPECT_TRUE(exists(prefix + "/==deleted==/bar/file")); + + EXPECT_TRUE(exists_renamed_deleted_dir("/" + prefix)); + + service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE); + + EXPECT_EQ(::close(fd), 0); + + EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/foo")); + EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/foo/file")); + EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar")); + EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar/file")); + EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar/opened_file")); + + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo/file")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar/file")); + + EXPECT_TRUE(exists(prefix + "/com.example/foo")); + EXPECT_TRUE(exists(prefix + "/com.example/foo/file")); + EXPECT_TRUE(exists(prefix + "/com.example/bar")); + EXPECT_TRUE(exists(prefix + "/com.example/bar/file")); + + EXPECT_FALSE(exists(prefix + "/==deleted==/foo")); + EXPECT_FALSE(exists(prefix + "/==deleted==/foo/file")); + EXPECT_FALSE(exists(prefix + "/==deleted==/bar")); + EXPECT_FALSE(exists(prefix + "/==deleted==/bar/file")); + + EXPECT_FALSE(exists_renamed_deleted_dir(prefix)); + } } TEST_F(ServiceTest, HashSecondaryDex) { LOG(INFO) << "HashSecondaryDex"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/foo", 10000, 10000, 0700); - touch("com.example/foo/file", 10000, 20000, 0700); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0700); std::vector<uint8_t> result; - std::string dexPath = get_full_path("com.example/foo/file"); + std::string dexPath = get_full_path("user/0/com.example/foo/file"); EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); @@ -220,7 +407,7 @@ TEST_F(ServiceTest, HashSecondaryDex_NoSuch) { LOG(INFO) << "HashSecondaryDex_NoSuch"; std::vector<uint8_t> result; - std::string dexPath = get_full_path("com.example/foo/file"); + std::string dexPath = get_full_path("user/0/com.example/foo/file"); EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); @@ -230,12 +417,12 @@ TEST_F(ServiceTest, HashSecondaryDex_NoSuch) { TEST_F(ServiceTest, HashSecondaryDex_Unreadable) { LOG(INFO) << "HashSecondaryDex_Unreadable"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/foo", 10000, 10000, 0700); - touch("com.example/foo/file", 10000, 20000, 0300); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0300); std::vector<uint8_t> result; - std::string dexPath = get_full_path("com.example/foo/file"); + std::string dexPath = get_full_path("user/0/com.example/foo/file"); EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); @@ -245,12 +432,12 @@ TEST_F(ServiceTest, HashSecondaryDex_Unreadable) { TEST_F(ServiceTest, HashSecondaryDex_WrongApp) { LOG(INFO) << "HashSecondaryDex_WrongApp"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/foo", 10000, 10000, 0700); - touch("com.example/foo/file", 10000, 20000, 0700); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0700); std::vector<uint8_t> result; - std::string dexPath = get_full_path("com.example/foo/file"); + std::string dexPath = get_full_path("user/0/com.example/foo/file"); EXPECT_BINDER_FAIL(service->hashSecondaryDexFile( dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result)); } @@ -278,7 +465,7 @@ TEST_F(ServiceTest, CalculateCache) { EXPECT_TRUE(create_cache_path(buf, "/path/to/file.apk", "isa")); EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } -TEST_F(ServiceTest, GetAppSize) { +TEST_F(ServiceTest, GetAppSizeManualForMedia) { struct stat s; std::string externalPicDir = @@ -322,6 +509,7 @@ TEST_F(ServiceTest, GetAppSize) { system(removeCommand.c_str()); } } + TEST_F(ServiceTest, GetAppSizeWrongSizes) { int32_t externalStorageAppId = -1; std::vector<int64_t> externalStorageSize; @@ -780,5 +968,364 @@ TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot_WrongVolumeUuid) { "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE)); } +class SdkSandboxDataTest : public testing::Test { +public: + void CheckFileAccess(const std::string& path, uid_t uid, gid_t gid, mode_t mode) { + const auto fullPath = "/data/local/tmp/" + path; + ASSERT_TRUE(exists(fullPath.c_str())) << "For path: " << fullPath; + struct stat st; + ASSERT_EQ(0, stat(fullPath.c_str(), &st)); + ASSERT_EQ(uid, st.st_uid) << "For path: " << fullPath; + ASSERT_EQ(gid, st.st_gid) << "For path: " << fullPath; + ASSERT_EQ(mode, st.st_mode) << "For path: " << fullPath; + } + + bool exists(const char* path) { return ::access(path, F_OK) == 0; } + + // Creates a default CreateAppDataArgs object + android::os::CreateAppDataArgs createAppDataArgs(const std::string& packageName) { + android::os::CreateAppDataArgs args; + args.uuid = kTestUuid; + args.packageName = packageName; + args.userId = kTestUserId; + args.appId = kTestAppId; + args.seInfo = "default"; + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE | FLAG_STORAGE_SDK; + return args; + } + + android::os::ReconcileSdkDataArgs reconcileSdkDataArgs( + const std::string& packageName, const std::vector<std::string>& subDirNames) { + android::os::ReconcileSdkDataArgs args; + args.uuid = kTestUuid; + args.packageName = packageName; + for (const auto& subDirName : subDirNames) { + args.subDirNames.push_back(subDirName); + } + args.userId = kTestUserId; + args.appId = kTestAppId; + args.previousAppId = -1; + args.seInfo = "default"; + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + return args; + } + +protected: + InstalldNativeService* service; + + virtual void SetUp() { + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(nullptr); + + service = new InstalldNativeService(); + clearAppData(); + ASSERT_TRUE(mkdirs("/data/local/tmp/user/0", 0700)); + ASSERT_TRUE(mkdirs("/data/local/tmp/user_de/0", 0700)); + ASSERT_TRUE(mkdirs("/data/local/tmp/misc_ce/0/sdksandbox", 0700)); + ASSERT_TRUE(mkdirs("/data/local/tmp/misc_de/0/sdksandbox", 0700)); + + init_globals_from_data_and_root(); + } + + virtual void TearDown() { + delete service; + clearAppData(); + } + +private: + void clearAppData() { + ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user", true)); + ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true)); + ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_ce", true)); + ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_de", true)); + } +}; + +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + + const std::string fooCePath = "misc_ce/0/sdksandbox/com.foo"; + CheckFileAccess(fooCePath, kSystemUid, kSystemUid, S_IFDIR | 0751); + + const std::string fooDePath = "misc_de/0/sdksandbox/com.foo"; + CheckFileAccess(fooDePath, kSystemUid, kSystemUid, S_IFDIR | 0751); +} + +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutSdkFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); +} + +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutSdkFlagDeletesExisting) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); + + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); +} + +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutDeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_SDK; + + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + + // Only CE paths should exist + CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, kSystemUid, S_IFDIR | 0751); + + // DE paths should not exist + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); +} + +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutCeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.flags = FLAG_STORAGE_DE | FLAG_STORAGE_SDK; + + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + + // CE paths should not exist + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + + // Only DE paths should exist + CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, kSystemUid, S_IFDIR | 0751); +} + +TEST_F(SdkSandboxDataTest, ReconcileSdkData) { + android::os::ReconcileSdkDataArgs args = + reconcileSdkDataArgs("com.foo", {"bar@random1", "baz@random2"}); + + // Create the sdk data. + ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args)); + + const std::string barCePath = "misc_ce/0/sdksandbox/com.foo/bar@random1"; + CheckFileAccess(barCePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | S_ISGID | 0700); + CheckFileAccess(barCePath + "/cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + CheckFileAccess(barCePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + + const std::string bazCePath = "misc_ce/0/sdksandbox/com.foo/baz@random2"; + CheckFileAccess(bazCePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | S_ISGID | 0700); + CheckFileAccess(bazCePath + "/cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + CheckFileAccess(bazCePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + + const std::string barDePath = "misc_de/0/sdksandbox/com.foo/bar@random1"; + CheckFileAccess(barDePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | S_ISGID | 0700); + CheckFileAccess(barDePath + "/cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + CheckFileAccess(barDePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + + const std::string bazDePath = "misc_de/0/sdksandbox/com.foo/baz@random2"; + CheckFileAccess(bazDePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | S_ISGID | 0700); + CheckFileAccess(bazDePath + "/cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + CheckFileAccess(bazDePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); +} + +TEST_F(SdkSandboxDataTest, ReconcileSdkData_ExtraCodeDirectoriesAreDeleted) { + android::os::ReconcileSdkDataArgs args = + reconcileSdkDataArgs("com.foo", {"bar@random1", "baz@random2"}); + + // Create the sdksandbox data. + ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args)); + + // Retry with different package name + args.subDirNames[0] = "bar.diff@random1"; + + // Create the sdksandbox data again + ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args)); + + // New directoris should exist + CheckFileAccess("misc_ce/0/sdksandbox/com.foo/bar.diff@random1", kTestSdkSandboxUid, kNobodyUid, + S_IFDIR | S_ISGID | 0700); + CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, kNobodyUid, + S_IFDIR | S_ISGID | 0700); + // Directory for old unreferred sdksandbox package name should be removed + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/bar@random1")); +} + +class DestroyAppDataTest : public SdkSandboxDataTest {}; + +TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithCeAndDeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.packageName = "com.foo"; + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + // Destroy the app user data. + ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId, + args.flags, result.ceDataInode)); + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); +} + +TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithoutDeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.packageName = "com.foo"; + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + // Destroy the app user data. + ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId, + FLAG_STORAGE_CE, result.ceDataInode)); + ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); +} + +TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithoutCeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.packageName = "com.foo"; + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + // Destroy the app user data. + ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId, + FLAG_STORAGE_DE, result.ceDataInode)); + ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); +} + +class ClearAppDataTest : public SdkSandboxDataTest { +public: + void createTestSdkData(const std::string& packageName, std::vector<std::string> sdkNames) { + const auto& cePackagePath = "/data/local/tmp/misc_ce/0/sdksandbox/" + packageName; + const auto& dePackagePath = "/data/local/tmp/misc_de/0/sdksandbox/" + packageName; + ASSERT_TRUE(mkdirs(cePackagePath, 0700)); + ASSERT_TRUE(mkdirs(dePackagePath, 0700)); + const std::vector<std::string> packagePaths = {cePackagePath, dePackagePath}; + for (const auto& packagePath : packagePaths) { + for (auto sdkName : sdkNames) { + ASSERT_TRUE(mkdirs(packagePath + "/" + sdkName + "/cache", 0700)); + ASSERT_TRUE(mkdirs(packagePath + "/" + sdkName + "/code_cache", 0700)); + std::ofstream{packagePath + "/" + sdkName + "/cache/cachedTestData.txt"}; + std::ofstream{packagePath + "/" + sdkName + "/code_cache/cachedTestData.txt"}; + } + } + } +}; + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_CE | FLAG_CLEAR_CACHE_ONLY, -1)); + + const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCodeCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_CE | FLAG_CLEAR_CODE_CACHE_ONLY, -1)); + + const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/code_cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data + ASSERT_BINDER_SUCCESS( + service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_DE | (InstalldNativeService::FLAG_CLEAR_CACHE_ONLY), + -1)); + + const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCodeCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_DE | FLAG_CLEAR_CODE_CACHE_ONLY, -1)); + + const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/code_cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndWithoutAnyCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, FLAG_STORAGE_CE, -1)); + + const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndWithoutAnyCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, FLAG_STORAGE_DE, -1)); + + const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2")); +} + +class DestroyUserDataTest : public SdkSandboxDataTest {}; + +TEST_F(DestroyUserDataTest, DestroySdkData_WithCeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.packageName = "com.foo"; + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + // Destroy user data + ASSERT_BINDER_SUCCESS(service->destroyUserData(args.uuid, args.userId, FLAG_STORAGE_CE)); + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox")); + ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox")); +} + +TEST_F(DestroyUserDataTest, DestroySdkData_WithDeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.packageName = "com.foo"; + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + // Destroy user data + ASSERT_BINDER_SUCCESS(service->destroyUserData(args.uuid, args.userId, FLAG_STORAGE_DE)); + ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox")); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index ed87b672ce..910cd630f3 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -14,11 +14,14 @@ * limitations under the License. */ +#include <errno.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <android-base/logging.h> #include <android-base/scopeguard.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include "InstalldNativeService.h" @@ -45,6 +48,8 @@ namespace android { namespace installd { +using ::testing::UnorderedElementsAre; + class UtilsTest : public testing::Test { protected: virtual void SetUp() { @@ -555,6 +560,24 @@ TEST_F(UtilsTest, MatchExtension_Invalid) { 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")); @@ -638,5 +661,98 @@ TEST_F(UtilsTest, TestCreateDirIfNeeded) { ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700)); } +TEST_F(UtilsTest, TestForEachSubdir) { + auto deleter = [&]() { + delete_dir_contents_and_dir("/data/local/tmp/user/0", true /* ignore_if_missing */); + }; + auto scope_guard = android::base::make_scope_guard(deleter); + + system("mkdir -p /data/local/tmp/user/0/com.foo"); + system("mkdir -p /data/local/tmp/user/0/com.bar"); + system("touch /data/local/tmp/user/0/some-file"); + + std::vector<std::string> result; + foreach_subdir("/data/local/tmp/user/0", + [&](const std::string &filename) { result.push_back(filename); }); + + EXPECT_THAT(result, UnorderedElementsAre("com.foo", "com.bar")); +} + +TEST_F(UtilsTest, TestSdkSandboxDataPaths) { + // Ce data paths + EXPECT_EQ("/data/misc_ce/0/sdksandbox", + create_data_misc_sdk_sandbox_path(nullptr, /*isCeData=*/true, 0)); + EXPECT_EQ("/data/misc_ce/10/sdksandbox", create_data_misc_sdk_sandbox_path(nullptr, true, 10)); + + EXPECT_EQ("/data/misc_ce/0/sdksandbox/com.foo", + create_data_misc_sdk_sandbox_package_path(nullptr, true, 0, "com.foo")); + EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo", + create_data_misc_sdk_sandbox_package_path(nullptr, true, 10, "com.foo")); + + EXPECT_EQ("/data/misc_ce/0/sdksandbox/com.foo/shared", + create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 0, "com.foo", "shared")); + EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/shared", + create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 10, "com.foo", "shared")); + EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/bar@random", + create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 10, "com.foo", "bar@random")); + + // De data paths + EXPECT_EQ("/data/misc_de/0/sdksandbox", + create_data_misc_sdk_sandbox_path(nullptr, /*isCeData=*/false, 0)); + EXPECT_EQ("/data/misc_de/10/sdksandbox", create_data_misc_sdk_sandbox_path(nullptr, false, 10)); + + EXPECT_EQ("/data/misc_de/0/sdksandbox/com.foo", + create_data_misc_sdk_sandbox_package_path(nullptr, false, 0, "com.foo")); + EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo", + create_data_misc_sdk_sandbox_package_path(nullptr, false, 10, "com.foo")); + + EXPECT_EQ("/data/misc_de/0/sdksandbox/com.foo/shared", + create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 0, "com.foo", "shared")); + EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/shared", + create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 10, "com.foo", "shared")); + EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/bar@random", + create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 10, "com.foo", "bar@random")); +} + +TEST_F(UtilsTest, WaitChild) { + pid_t pid = fork(); + if (pid == 0) { + /* child */ + // Do nothing. + _exit(0); + } + /* parent */ + int return_code = wait_child_with_timeout(pid, /*timeout_ms=*/100); + EXPECT_TRUE(WIFEXITED(return_code)); + EXPECT_EQ(WEXITSTATUS(return_code), 0); +} + +TEST_F(UtilsTest, WaitChildTimeout) { + pid_t pid = fork(); + if (pid == 0) { + /* child */ + sleep(1); + _exit(0); + } + /* parent */ + int return_code = wait_child_with_timeout(pid, /*timeout_ms=*/1); + EXPECT_FALSE(WIFEXITED(return_code)); + EXPECT_EQ(WTERMSIG(return_code), SIGKILL); +} + +TEST_F(UtilsTest, RemoveFileAtFd) { + std::string filename = "/data/local/tmp/tempfile-XXXXXX"; + int fd = mkstemp(filename.data()); + ASSERT_GE(fd, 0); + ASSERT_EQ(access(filename.c_str(), F_OK), 0); + + std::string actual_filename; + remove_file_at_fd(fd, &actual_filename); + EXPECT_NE(access(filename.c_str(), F_OK), 0); + EXPECT_EQ(filename, actual_filename); + + close(fd); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index c4ecd070c1..45aeab6fa8 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -19,20 +19,25 @@ #include <errno.h> #include <fcntl.h> #include <fts.h> +#include <poll.h> #include <stdlib.h> #include <sys/capability.h> +#include <sys/pidfd.h> #include <sys/stat.h> +#include <sys/statvfs.h> #include <sys/wait.h> #include <sys/xattr.h> -#include <sys/statvfs.h> +#include <unistd.h> +#include <uuid/uuid.h> #include <android-base/file.h> #include <android-base/logging.h> -#include <android-base/strings.h> #include <android-base/stringprintf.h> +#include <android-base/strings.h> #include <android-base/unique_fd.h> #include <cutils/fs.h> #include <cutils/properties.h> +#include <linux/fs.h> #include <log/log.h> #include <private/android_filesystem_config.h> #include <private/android_projectid_config.h> @@ -47,6 +52,7 @@ #define DEBUG_XATTRS 0 +using android::base::Dirname; using android::base::EndsWith; using android::base::Fdopendir; using android::base::StringPrintf; @@ -55,6 +61,10 @@ using android::base::unique_fd; 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. @@ -188,6 +198,45 @@ std::string create_data_user_de_path(const char* volume_uuid, userid_t userid) { return StringPrintf("%s/user_de/%u", data.c_str(), userid); } +/** + * Create the path name where sdk_sandbox data for all apps will be stored. + * E.g. /data/misc_ce/0/sdksandbox + */ +std::string create_data_misc_sdk_sandbox_path(const char* uuid, bool isCeData, userid_t user) { + std::string data(create_data_path(uuid)); + if (isCeData) { + return StringPrintf("%s/misc_ce/%d/sdksandbox", data.c_str(), user); + } else { + return StringPrintf("%s/misc_de/%d/sdksandbox", data.c_str(), user); + } +} + +/** + * Create the path name where code data for all codes in a particular app will be stored. + * E.g. /data/misc_ce/0/sdksandbox/<package-name> + */ +std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData, + userid_t user, const char* package_name) { + check_package_name(package_name); + return StringPrintf("%s/%s", + create_data_misc_sdk_sandbox_path(volume_uuid, isCeData, user).c_str(), + package_name); +} + +/** + * Create the path name where sdk data for a particular sdk will be stored. + * E.g. /data/misc_ce/0/sdksandbox/<package-name>/com.foo@randomstrings + */ +std::string create_data_misc_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData, + userid_t user, const char* package_name, + const char* sub_dir_name) { + return StringPrintf("%s/%s", + create_data_misc_sdk_sandbox_package_path(volume_uuid, isCeData, user, + package_name) + .c_str(), + sub_dir_name); +} + std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) { return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user); } @@ -376,6 +425,45 @@ std::vector<userid_t> get_known_users(const char* volume_uuid) { return users; } +long get_project_id(uid_t uid, long start_project_id_range) { + return uid - AID_APP_START + start_project_id_range; +} + +int set_quota_project_id(const std::string& path, long project_id, bool set_inherit) { + struct fsxattr fsx; + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << path << " to set project id."; + return -1; + } + + if (ioctl(fd, FS_IOC_FSGETXATTR, &fsx) == -1) { + PLOG(ERROR) << "Failed to get extended attributes for " << path << " to get project id."; + return -1; + } + + fsx.fsx_projid = project_id; + if (ioctl(fd, FS_IOC_FSSETXATTR, &fsx) == -1) { + PLOG(ERROR) << "Failed to set project id on " << path; + return -1; + } + if (set_inherit) { + unsigned int flags; + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) { + PLOG(ERROR) << "Failed to get flags for " << path << " to set project id inheritance."; + return -1; + } + + flags |= FS_PROJINHERIT_FL; + + if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) { + PLOG(ERROR) << "Failed to set flags for " << path << " to set project id inheritance."; + return -1; + } + } + return 0; +} + int calculate_tree_size(const std::string& path, int64_t* size, int32_t include_gid, int32_t exclude_gid, bool exclude_apps) { FTS *fts; @@ -595,6 +683,125 @@ int delete_dir_contents(const char *pathname, 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()); + + auto dir_to_delete = temp_dir_path.c_str(); + if (::rename(pathname.c_str(), dir_to_delete)) { + if (ignore_if_missing && (errno == ENOENT)) { + return 0; + } + ALOGE("Couldn't rename %s -> %s: %s \n", pathname.c_str(), dir_to_delete, strerror(errno)); + dir_to_delete = pathname.c_str(); + } + + return delete_dir_contents(dir_to_delete, 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)); +} + +// Collects filename of subdirectories of given directory and passes it to the function +int foreach_subdir(const std::string& pathname, const std::function<void(const std::string&)> fn) { + auto dir = open_dir(pathname.c_str()); + if (!dir) return -1; + + int dfd = dirfd(dir.get()); + if (dfd < 0) { + ALOGE("Couldn't dirfd %s: %s\n", pathname.c_str(), strerror(errno)); + return -1; + } + + 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; + } + fn(name); + } + + return 0; +} + +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; @@ -829,7 +1036,7 @@ void remove_path_xattr(const std::string& path, const char* inode_xattr) { * to top level directories (i.e. have ".."). */ static int validate_path(const std::string& dir, const std::string& path, int maxSubdirs) { - // Argument sanity checking + // Argument check if (dir.find('/') != 0 || dir.rfind('/') != dir.size() - 1 || dir.find("..") != std::string::npos) { LOG(ERROR) << "Invalid directory " << dir; @@ -962,30 +1169,45 @@ int ensure_config_user_dirs(userid_t userid) { return fs_prepare_dir(path.c_str(), 0750, uid, gid); } -int wait_child(pid_t pid) -{ +static int wait_child(pid_t pid) { int status; - pid_t got_pid; + pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, /*options=*/0)); - while (1) { - got_pid = waitpid(pid, &status, 0); - if (got_pid == -1 && errno == EINTR) { - printf("waitpid interrupted, retrying\n"); - } else { - break; - } - } if (got_pid != pid) { - ALOGW("waitpid failed: wanted %d, got %d: %s\n", - (int) pid, (int) got_pid, strerror(errno)); - return 1; + PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid; + return W_EXITCODE(/*exit_code=*/255, /*signal_number=*/0); } - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { - return 0; - } else { - return status; /* always nonzero */ + return status; +} + +int wait_child_with_timeout(pid_t pid, int timeout_ms) { + int pidfd = pidfd_open(pid, /*flags=*/0); + if (pidfd < 0) { + PLOG(ERROR) << "pidfd_open failed for pid " << pid; + kill(pid, SIGKILL); + return wait_child(pid); } + + struct pollfd pfd; + pfd.fd = pidfd; + pfd.events = POLLIN; + int poll_ret = TEMP_FAILURE_RETRY(poll(&pfd, /*nfds=*/1, timeout_ms)); + + close(pidfd); + + if (poll_ret < 0) { + PLOG(ERROR) << "poll failed for pid " << pid; + kill(pid, SIGKILL); + return wait_child(pid); + } + if (poll_ret == 0) { + LOG(WARNING) << "Child process " << pid << " timed out after " << timeout_ms + << "ms. Killing it"; + kill(pid, SIGKILL); + return wait_child(pid); + } + return wait_child(pid); } /** @@ -1198,5 +1420,27 @@ void drop_capabilities(uid_t uid) { } } +bool remove_file_at_fd(int fd, /*out*/ std::string* path) { + char path_buffer[PATH_MAX + 1]; + std::string proc_path = android::base::StringPrintf("/proc/self/fd/%d", fd); + ssize_t len = readlink(proc_path.c_str(), path_buffer, PATH_MAX); + if (len < 0) { + PLOG(WARNING) << "Could not remove file at fd " << fd << ": Failed to get file path"; + return false; + } + path_buffer[len] = '\0'; + if (path != nullptr) { + *path = path_buffer; + } + if (unlink(path_buffer) != 0) { + if (errno == ENOENT) { + return true; + } + PLOG(WARNING) << "Could not remove file at path " << path_buffer; + return false; + } + return true; +} + } // namespace installd } // namespace android diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 549fc6cf04..ecea1d2b1c 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -32,6 +32,7 @@ #define MEASURE_DEBUG 0 #define FIXUP_DEBUG 0 +#define SDK_DEBUG 1 #define BYPASS_QUOTA 0 #define BYPASS_SDCARDFS 0 @@ -60,6 +61,14 @@ std::string create_data_user_de_package_path(const char* volume_uuid, std::string create_data_user_ce_package_path_as_user_link( const char* volume_uuid, userid_t userid, const char* package_name); +std::string create_data_misc_sdk_sandbox_path(const char* volume_uuid, bool isCeData, + userid_t userid); +std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData, + userid_t userid, const char* package_name); +std::string create_data_misc_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData, + userid_t userid, const char* package_name, + const char* sub_dir_name); + std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user); std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user); std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user, @@ -120,6 +129,13 @@ int create_dir_if_needed(const std::string& pathname, mode_t mode); 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); + +int foreach_subdir(const std::string& pathname, std::function<void(const std::string&)> fn); + +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), @@ -148,12 +164,15 @@ int validate_apk_path_subdirs(const char *path); int ensure_config_user_dirs(userid_t userid); -int wait_child(pid_t pid); +// Waits for a child process, or kills it if it times out. Returns the exit code. +int wait_child_with_timeout(pid_t pid, int timeout_ms); int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, uid_t uid, gid_t gid); bool supports_sdcardfs(); +long get_project_id(uid_t uid, long start_project_id_range); +int set_quota_project_id(const std::string& path, long project_id, bool set_inherit); int64_t get_occupied_app_space_external(const std::string& uuid, int32_t userId, int32_t appId); int64_t get_occupied_app_cache_space_external(const std::string& uuid, int32_t userId, int32_t appId); @@ -165,6 +184,10 @@ bool collect_profiles(std::vector<std::string>* profiles_paths); void drop_capabilities(uid_t uid); +// Removes a file specified by a file descriptor. Returns true on success. Reports the file path to +// `path` if present. +bool remove_file_at_fd(int fd, /*out*/ std::string* path = nullptr); + } // namespace installd } // namespace android diff --git a/cmds/installd/view_compiler.cpp b/cmds/installd/view_compiler.cpp index 60d6492e70..8c000a11c9 100644 --- a/cmds/installd/view_compiler.cpp +++ b/cmds/installd/view_compiler.cpp @@ -33,7 +33,13 @@ namespace android { namespace installd { -using base::unique_fd; +namespace { + +using ::android::base::unique_fd; + +constexpr int kTimeoutMs = 300000; + +} // namespace bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file, int uid) { @@ -88,7 +94,17 @@ bool view_compiler(const char* apk_path, const char* package_name, const char* o _exit(1); } - return wait_child(pid) == 0; + int return_code = wait_child_with_timeout(pid, kTimeoutMs); + if (!WIFEXITED(return_code)) { + LOG(WARNING) << "viewcompiler failed for " << package_name << ":" << apk_path; + if (WTERMSIG(return_code) == SIGKILL) { + // If the subprocess is killed while it's writing to the file, the file is likely + // corrupted, so we should remove it. + remove_file_at_fd(outfd.get()); + } + return false; + } + return WEXITSTATUS(return_code) == 0; } } // namespace installd diff --git a/cmds/ip-up-vpn/Android.bp b/cmds/ip-up-vpn/Android.bp new file mode 100644 index 0000000000..c746f7fde3 --- /dev/null +++ b/cmds/ip-up-vpn/Android.bp @@ -0,0 +1,31 @@ +// Copyright 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_binary { + name: "ip-up-vpn", + + srcs: ["ip-up-vpn.c"], + cflags: [ + "-Wall", + "-Werror", + ], + shared_libs: [ + "libcutils", + "liblog", + ], +} diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk deleted file mode 100644 index 396ae9db04..0000000000 --- a/cmds/ip-up-vpn/Android.mk +++ /dev/null @@ -1,30 +0,0 @@ -# -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := ip-up-vpn.c -LOCAL_CFLAGS := -Wall -Werror -LOCAL_SHARED_LIBRARIES := libcutils liblog -LOCAL_MODULE := ip-up-vpn -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp -LOCAL_MODULE_TAGS := optional - -include $(BUILD_EXECUTABLE) diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index 2722e214e8..ff73c9499f 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -28,6 +28,7 @@ #include <sstream> #include <android-base/file.h> +#include <android-base/hex.h> #include <android-base/logging.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <hidl-hash/Hash.h> @@ -691,8 +692,7 @@ Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager, } auto&& hashArray = hashChain[hashIndex]; - std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()}; - entry->hash = Hash::hexString(hashVec); + entry->hash = android::base::HexString(hashArray.data(), hashArray.size()); }); if (!hashRet.isOk()) { handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description()); diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp index bc99f4d473..a5f98c2a9e 100644 --- a/cmds/lshal/Lshal.cpp +++ b/cmds/lshal/Lshal.cpp @@ -142,12 +142,10 @@ Status Lshal::emitDebugInfo( } } - PipeRelay relay(out, err, interfaceName, instanceName); - - if (relay.initCheck() != OK) { - std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck()); - err << msg << std::endl; - LOG(ERROR) << msg; + auto relay = PipeRelay::create(out, err, interfaceName + "/" + instanceName); + if (!relay.ok()) { + err << "Unable to create PipeRelay: " << relay.error() << std::endl; + LOG(ERROR) << "Unable to create PipeRelay: " << relay.error(); return IO_ERROR; } @@ -155,7 +153,7 @@ Status Lshal::emitDebugInfo( native_handle_create(1 /* numFds */, 0 /* numInts */), native_handle_delete); - fdHandle->data[0] = relay.fd(); + fdHandle->data[0] = relay.value()->fd().get(); hardware::Return<void> ret = base->debug(fdHandle.get(), convert(options)); diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp index 4e9763635b..863490d314 100644 --- a/cmds/lshal/PipeRelay.cpp +++ b/cmds/lshal/PipeRelay.cpp @@ -16,143 +16,94 @@ #include "PipeRelay.h" -#include <sys/select.h> -#include <sys/time.h> +#include <sys/poll.h> #include <sys/types.h> #include <unistd.h> -#include <atomic> +#include <chrono> +#include <optional> -#include <utils/Thread.h> +#include <android-base/unique_fd.h> + +using android::base::borrowed_fd; +using android::base::Result; +using android::base::unique_fd; +using std::chrono_literals::operator""ms; namespace android { namespace lshal { - -static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 }; - -static std::string getThreadName(std::string interfaceName, const std::string &instanceName) { - auto dot = interfaceName.rfind("."); - if (dot != std::string::npos) interfaceName = interfaceName.substr(dot + 1); - return "RelayThread_" + interfaceName + "_" + instanceName; -} - -struct PipeRelay::RelayThread : public Thread { - explicit RelayThread(int fd, std::ostream &os, const NullableOStream<std::ostream> &err, - const std::string &fqName); - - bool threadLoop() override; - void setFinished(); - -private: - int mFd; - std::ostream &mOutStream; - NullableOStream<std::ostream> mErrStream; - - // If we were to use requestExit() and exitPending() instead, threadLoop() - // may not run at all by the time ~PipeRelay is called (i.e. debug() has - // returned from HAL). By using our own flag, we ensure that select() and - // read() are executed until data are drained. - std::atomic_bool mFinished; - - std::string mFqName; - - DISALLOW_COPY_AND_ASSIGN(RelayThread); -}; - -//////////////////////////////////////////////////////////////////////////////// - -PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os, - const NullableOStream<std::ostream> &err, - const std::string &fqName) - : mFd(fd), mOutStream(os), mErrStream(err), mFinished(false), mFqName(fqName) {} - -bool PipeRelay::RelayThread::threadLoop() { - char buffer[1024]; - - fd_set set; - FD_ZERO(&set); - FD_SET(mFd, &set); - - struct timeval timeout = READ_TIMEOUT; - - int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout)); - if (res < 0) { - mErrStream << "debug " << mFqName << ": select() failed"; - return false; - } - - if (res == 0 || !FD_ISSET(mFd, &set)) { - if (mFinished) { - mErrStream << "debug " << mFqName - << ": timeout reading from pipe, output may be truncated."; - return false; - } - // timeout, but debug() has not returned, so wait for HAL to finish. - return true; - } - - // FD_ISSET(mFd, &set) == true. Data available, start reading - ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer))); - - if (n < 0) { - mErrStream << "debug " << mFqName << ": read() failed"; +Result<std::unique_ptr<PipeRelay>> PipeRelay::create(std::ostream& os, + const NullableOStream<std::ostream>& err, + const std::string& fqName) { + auto pipeRelay = std::unique_ptr<PipeRelay>(new PipeRelay()); + unique_fd rfd; + if (!android::base::Pipe(&rfd, &pipeRelay->mWrite)) { + return android::base::ErrnoError() << "pipe()"; } - - if (n <= 0) { - return false; + // Workaround for b/111997867: need a separate FD trigger because rfd can't receive POLLHUP + // when the write end is closed after the write end was sent through hwbinder. + unique_fd rfdTrigger; + if (!android::base::Pipe(&rfdTrigger, &pipeRelay->mWriteTrigger)) { + return android::base::ErrnoError() << "pipe() for trigger"; } - - mOutStream.write(buffer, n); - - return true; -} - -void PipeRelay::RelayThread::setFinished() { - mFinished = true; + pipeRelay->mThread = + std::make_unique<std::thread>(&PipeRelay::thread, std::move(rfd), std::move(rfdTrigger), + &os, &err, fqName); + return pipeRelay; } -//////////////////////////////////////////////////////////////////////////////// - -PipeRelay::PipeRelay(std::ostream &os, const NullableOStream<std::ostream> &err, - const std::string &interfaceName, const std::string &instanceName) - : mInitCheck(NO_INIT) { - int res = pipe(mFds); - - if (res < 0) { - mInitCheck = -errno; - return; - } - - mThread = new RelayThread(mFds[0], os, err, interfaceName + "/" + instanceName); - mInitCheck = mThread->run(getThreadName(interfaceName, instanceName).c_str()); -} +void PipeRelay::thread(unique_fd rfd, unique_fd rfdTrigger, std::ostream* out, + const NullableOStream<std::ostream>* err, std::string fqName) { + while (true) { + pollfd pfd[2]; + pfd[0] = {.fd = rfd.get(), .events = POLLIN}; + pfd[1] = {.fd = rfdTrigger.get(), .events = 0}; + + int pollRes = poll(pfd, arraysize(pfd), -1 /* infinite timeout */); + if (pollRes < 0) { + int savedErrno = errno; + (*err) << "debug " << fqName << ": poll() failed: " << strerror(savedErrno) + << std::endl; + break; + } -void PipeRelay::CloseFd(int *fd) { - if (*fd >= 0) { - close(*fd); - *fd = -1; + if (pfd[0].revents & POLLIN) { + char buffer[1024]; + ssize_t n = TEMP_FAILURE_RETRY(read(rfd.get(), buffer, sizeof(buffer))); + if (n < 0) { + int savedErrno = errno; + (*err) << "debug " << fqName << ": read() failed: " << strerror(savedErrno) + << std::endl; + break; + } + if (n == 0) { + (*err) << "Warning: debug " << fqName << ": poll() indicates POLLIN but no data" + << std::endl; + continue; + } + out->write(buffer, n); + continue; + } + if (pfd[0].revents & POLLHUP) { + break; + } + if (pfd[1].revents & POLLHUP) { + // ~PipeRelay is called on the main thread. |mWrite| has been flushed and closed. + // Ensure that our read end of the pipe doesn't have pending data, then exit. + if ((pfd[0].revents & POLLIN) == 0) { + break; + } + } } } PipeRelay::~PipeRelay() { - CloseFd(&mFds[1]); - - if (mThread != nullptr) { - mThread->setFinished(); + mWrite.reset(); + mWriteTrigger.reset(); + if (mThread != nullptr && mThread->joinable()) { mThread->join(); - mThread.clear(); } - - CloseFd(&mFds[0]); -} - -status_t PipeRelay::initCheck() const { - return mInitCheck; -} - -int PipeRelay::fd() const { - return mFds[1]; } -} // namespace lshal -} // namespace android +} // namespace lshal +} // namespace android diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h index bd994b48b8..45ba98278d 100644 --- a/cmds/lshal/PipeRelay.h +++ b/cmds/lshal/PipeRelay.h @@ -16,42 +16,43 @@ #pragma once +#include <thread> + #include <android-base/macros.h> -#include <ostream> +#include <android-base/result.h> +#include <android-base/unique_fd.h> #include <utils/Errors.h> #include <utils/RefBase.h> +#include <ostream> #include "NullableOStream.h" namespace android { namespace lshal { -/* Creates an AF_UNIX socketpair and spawns a thread that relays any data +/** + * Creates a pipe and spawns a thread that relays any data * written to the "write"-end of the pair to the specified output stream "os". */ struct PipeRelay { - explicit PipeRelay(std::ostream& os, - const NullableOStream<std::ostream>& err, - const std::string& interfaceName, - const std::string& instanceName); + static android::base::Result<std::unique_ptr<PipeRelay>> create( + std::ostream& os, const NullableOStream<std::ostream>& err, const std::string& fqName); ~PipeRelay(); - status_t initCheck() const; - // Returns the file descriptor corresponding to the "write"-end of the // connection. - int fd() const; + android::base::borrowed_fd fd() const { return mWrite; } private: - struct RelayThread; - - status_t mInitCheck; - int mFds[2]; - sp<RelayThread> mThread; - - static void CloseFd(int *fd); - + PipeRelay() = default; DISALLOW_COPY_AND_ASSIGN(PipeRelay); + static void thread(android::base::unique_fd rfd, android::base::unique_fd rfdTrigger, + std::ostream* out, const NullableOStream<std::ostream>* err, + std::string fqName); + + android::base::unique_fd mWrite; + android::base::unique_fd mWriteTrigger; + std::unique_ptr<std::thread> mThread; }; } // namespace lshal diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp index 8e21975efa..1753343deb 100644 --- a/cmds/lshal/TableEntry.cpp +++ b/cmds/lshal/TableEntry.cpp @@ -18,6 +18,7 @@ #include <map> +#include <android-base/hex.h> #include <android-base/strings.h> #include <hidl-hash/Hash.h> #include <vintf/parse_string.h> @@ -104,7 +105,8 @@ std::string TableEntry::getField(TableColumnType type) const { } std::string TableEntry::isReleased() const { - static const std::string unreleased = Hash::hexString(Hash::kEmptyHash); + static const std::string unreleased = android::base::HexString(Hash::kEmptyHash.data(), + Hash::kEmptyHash.size()); if (hash.empty()) { return "?"; diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index 6f08f74690..cba7c4bf2a 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -22,9 +22,10 @@ #include <thread> #include <vector> -#include <gtest/gtest.h> -#include <gmock/gmock.h> +#include <android-base/parseint.h> #include <android/hardware/tests/inheritance/1.0/IChild.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> #include <hidl/HidlTransportSupport.h> #include <vintf/parse_xml.h> @@ -77,6 +78,13 @@ struct Child : android::hardware::tests::inheritance::V1_0::IChild { content += "\n"; content += option.c_str(); } + if (options.size() > 0) { + uint64_t len; + if (android::base::ParseUint(options[0], &len)) { + content += "\n"; + content += std::string(len, 'X'); + } + } ssize_t written = write(fd, content.c_str(), content.size()); if (written != (ssize_t)content.size()) { LOG(WARNING) << "SERVER(Child) debug writes " << written << " bytes < " @@ -189,6 +197,16 @@ TEST_F(DebugTest, Debug3) { EXPECT_THAT(err.str(), HasSubstr("does not exist")); } +TEST_F(DebugTest, DebugLarge) { + EXPECT_EQ(0u, callMain(lshal, { + "lshal", "debug", "android.hardware.tests.inheritance@1.0::IChild/default", "10000" + })); + EXPECT_THAT(out.str(), + StrEq("android.hardware.tests.inheritance@1.0::IChild\n10000\n" + + std::string(10000, 'X'))); + EXPECT_THAT(err.str(), IsEmpty()); +} + TEST_F(DebugTest, DebugParent) { EXPECT_EQ(0u, callMain(lshal, { "lshal", "debug", "android.hardware.tests.inheritance@1.0::IParent", "calling parent" diff --git a/cmds/rss_hwm_reset/rss_hwm_reset.rc b/cmds/rss_hwm_reset/rss_hwm_reset.rc index fbbc8200b2..271cbf89f4 100644 --- a/cmds/rss_hwm_reset/rss_hwm_reset.rc +++ b/cmds/rss_hwm_reset/rss_hwm_reset.rc @@ -18,7 +18,7 @@ service rss_hwm_reset /system/bin/rss_hwm_reset oneshot user nobody group nobody readproc - writepid /dev/cpuset/system-background/tasks + task_profiles ServiceCapacityLow capabilities DAC_OVERRIDE on property:sys.rss_hwm_reset.on=1 diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp index 3e8e3f67f8..21ac11b4cf 100644 --- a/cmds/service/Android.bp +++ b/cmds/service/Android.bp @@ -52,3 +52,21 @@ cc_binary { "-Werror", ], } + +cc_binary_host { + name: "aservice", + + srcs: ["service.cpp"], + + shared_libs: [ + "libcutils", + "libutils", + "libbinder", + ], + + cflags: [ + "-DXP_UNIX", + "-Wall", + "-Werror", + ], +} diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index 18b6b58a9e..d5ca725eb9 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp @@ -21,6 +21,7 @@ #include <cutils/ashmem.h> #include <getopt.h> +#include <libgen.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -45,37 +46,14 @@ void writeString16(Parcel& parcel, const char* string) } } -// get the name of the generic interface we hold a reference to -static String16 get_interface_name(sp<IBinder> service) -{ - if (service != nullptr) { - Parcel data, reply; - status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply); - if (err == NO_ERROR) { - return reply.readString16(); - } - } - return String16(); -} - -static String8 good_old_string(const String16& src) -{ - String8 name8; - char ch8[2]; - ch8[1] = 0; - for (unsigned j = 0; j < src.size(); j++) { - char16_t ch = src[j]; - if (ch < 128) ch8[0] = (char)ch; - name8.append(ch8); - } - return name8; -} - int main(int argc, char* const argv[]) { bool wantsUsage = false; int result = 0; + /* Strip path off the program name. */ + char* prog_name = basename(argv[0]); + while (1) { int ic = getopt(argc, argv, "h?"); if (ic < 0) @@ -87,7 +65,7 @@ int main(int argc, char* const argv[]) wantsUsage = true; break; default: - aerr << "service: Unknown option -" << ic << endl; + aerr << prog_name << ": Unknown option -" << ic << endl; wantsUsage = true; result = 10; break; @@ -96,10 +74,13 @@ int main(int argc, char* const argv[]) #ifdef VENDORSERVICES ProcessState::initWithDriver("/dev/vndbinder"); #endif +#ifndef __ANDROID__ + setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1})); +#endif sp<IServiceManager> sm = defaultServiceManager(); fflush(stdout); if (sm == nullptr) { - aerr << "service: Unable to get default service manager!" << endl; + aerr << prog_name << ": Unable to get default service manager!" << endl; return 20; } @@ -113,7 +94,7 @@ int main(int argc, char* const argv[]) aout << "Service " << argv[optind] << (service == nullptr ? ": not found" : ": found") << endl; } else { - aerr << "service: No service specified for check" << endl; + aerr << prog_name << ": No service specified for check" << endl; wantsUsage = true; result = 10; } @@ -125,8 +106,8 @@ int main(int argc, char* const argv[]) String16 name = services[i]; sp<IBinder> service = sm->checkService(name); aout << i - << "\t" << good_old_string(name) - << ": [" << good_old_string(get_interface_name(service)) << "]" + << "\t" << name + << ": [" << (service ? service->getInterfaceDescriptor() : String16()) << "]" << endl; } } else if (strcmp(argv[optind], "call") == 0) { @@ -134,10 +115,11 @@ int main(int argc, char* const argv[]) if (optind+1 < argc) { int serviceArg = optind; sp<IBinder> service = sm->checkService(String16(argv[optind++])); - String16 ifName = get_interface_name(service); + String16 ifName = (service ? service->getInterfaceDescriptor() : String16()); int32_t code = atoi(argv[optind++]); if (service != nullptr && ifName.size() > 0) { Parcel data, reply; + data.markForBinder(service); // the interface name is first data.writeInterfaceToken(ifName); @@ -147,7 +129,7 @@ int main(int argc, char* const argv[]) if (strcmp(argv[optind], "i32") == 0) { optind++; if (optind >= argc) { - aerr << "service: no integer supplied for 'i32'" << endl; + aerr << prog_name << ": no integer supplied for 'i32'" << endl; wantsUsage = true; result = 10; break; @@ -156,7 +138,7 @@ int main(int argc, char* const argv[]) } else if (strcmp(argv[optind], "i64") == 0) { optind++; if (optind >= argc) { - aerr << "service: no integer supplied for 'i64'" << endl; + aerr << prog_name << ": no integer supplied for 'i64'" << endl; wantsUsage = true; result = 10; break; @@ -165,7 +147,7 @@ int main(int argc, char* const argv[]) } else if (strcmp(argv[optind], "s16") == 0) { optind++; if (optind >= argc) { - aerr << "service: no string supplied for 's16'" << endl; + aerr << prog_name << ": no string supplied for 's16'" << endl; wantsUsage = true; result = 10; break; @@ -174,7 +156,7 @@ int main(int argc, char* const argv[]) } else if (strcmp(argv[optind], "f") == 0) { optind++; if (optind >= argc) { - aerr << "service: no number supplied for 'f'" << endl; + aerr << prog_name << ": no number supplied for 'f'" << endl; wantsUsage = true; result = 10; break; @@ -183,7 +165,7 @@ int main(int argc, char* const argv[]) } else if (strcmp(argv[optind], "d") == 0) { optind++; if (optind >= argc) { - aerr << "service: no number supplied for 'd'" << endl; + aerr << prog_name << ": no number supplied for 'd'" << endl; wantsUsage = true; result = 10; break; @@ -195,7 +177,7 @@ int main(int argc, char* const argv[]) } else if (strcmp(argv[optind], "fd") == 0) { optind++; if (optind >= argc) { - aerr << "service: no path supplied for 'fd'" << endl; + aerr << prog_name << ": no path supplied for 'fd'" << endl; wantsUsage = true; result = 10; break; @@ -203,7 +185,7 @@ int main(int argc, char* const argv[]) const char *path = argv[optind++]; int fd = open(path, O_RDONLY); if (fd < 0) { - aerr << "service: could not open '" << path << "'" << endl; + aerr << prog_name << ": could not open '" << path << "'" << endl; wantsUsage = true; result = 10; break; @@ -212,7 +194,7 @@ int main(int argc, char* const argv[]) } else if (strcmp(argv[optind], "afd") == 0) { optind++; if (optind >= argc) { - aerr << "service: no path supplied for 'afd'" << endl; + aerr << prog_name << ": no path supplied for 'afd'" << endl; wantsUsage = true; result = 10; break; @@ -221,7 +203,8 @@ int main(int argc, char* const argv[]) int fd = open(path, O_RDONLY); struct stat statbuf; if (fd < 0 || fstat(fd, &statbuf) != 0) { - aerr << "service: could not open or stat '" << path << "'" << endl; + aerr << prog_name << ": could not open or stat" + << " '" << path << "'" << endl; wantsUsage = true; result = 10; break; @@ -229,13 +212,14 @@ int main(int argc, char* const argv[]) int afd = ashmem_create_region("test", statbuf.st_size); void* ptr = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, afd, 0); - read(fd, ptr, statbuf.st_size); + (void)read(fd, ptr, statbuf.st_size); close(fd); data.writeFileDescriptor(afd, true /* take ownership */); } else if (strcmp(argv[optind], "nfd") == 0) { optind++; if (optind >= argc) { - aerr << "service: no file descriptor supplied for 'nfd'" << endl; + aerr << prog_name << ": no file descriptor supplied for" + << " 'nfd'" << endl; wantsUsage = true; result = 10; break; @@ -322,7 +306,7 @@ int main(int argc, char* const argv[]) // for now just set the extra field to be null. data.writeInt32(-1); } else { - aerr << "service: unknown option " << argv[optind] << endl; + aerr << prog_name << ": unknown option " << argv[optind] << endl; wantsUsage = true; result = 10; break; @@ -332,44 +316,44 @@ int main(int argc, char* const argv[]) service->transact(code, data, &reply); aout << "Result: " << reply << endl; } else { - aerr << "service: Service " << argv[serviceArg] + aerr << prog_name << ": Service " << argv[serviceArg] << " does not exist" << endl; result = 10; } } else { if (optind < argc) { - aerr << "service: No service specified for call" << endl; + aerr << prog_name << ": No service specified for call" << endl; } else { - aerr << "service: No code specified for call" << endl; + aerr << prog_name << ": No code specified for call" << endl; } wantsUsage = true; result = 10; } } else { - aerr << "service: Unknown command " << argv[optind] << endl; + aerr << prog_name << ": Unknown command " << argv[optind] << endl; wantsUsage = true; result = 10; } } if (wantsUsage) { - aout << "Usage: service [-h|-?]\n" - " service list\n" - " service check SERVICE\n" - " service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR | null" - " | fd f | nfd n | afd f ] ...\n" + aout << "Usage: " << prog_name << " [-h|-?]\n" + " " << prog_name << " list\n" + " " << prog_name << " check SERVICE\n" + " " << prog_name << " call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR" + " | null | fd f | nfd n | afd f ] ...\n" "Options:\n" " i32: Write the 32-bit integer N into the send parcel.\n" " i64: Write the 64-bit integer N into the send parcel.\n" - " f: Write the 32-bit single-precision number N into the send parcel.\n" - " d: Write the 64-bit double-precision number N into the send parcel.\n" + " f: Write the 32-bit single-precision number N into the send parcel.\n" + " d: Write the 64-bit double-precision number N into the send parcel.\n" " s16: Write the UTF-16 string STR into the send parcel.\n" " null: Write a null binder into the send parcel.\n" - " fd: Write a file descriptor for the file f to the send parcel.\n" - " nfd: Write file descriptor n to the send parcel.\n" - " afd: Write an ashmem file descriptor for a region containing the data from" - " file f to the send parcel.\n"; -// " intent: Write and Intent int the send parcel. ARGS can be\n" + " fd: Write a file descriptor for the file f into the send parcel.\n" + " nfd: Write the file descriptor n into the send parcel.\n" + " afd: Write an ashmem file descriptor for a region containing the data from\n" + " file f into the send parcel.\n"; +// " intent: Write an Intent into the send parcel. ARGS can be\n" // " action=STR data=STR type=STR launchFlags=INT component=STR categories=STR[,STR,...]\n"; return result; } diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index 3ebdeee7aa..32922ca24c 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -47,6 +47,23 @@ cc_binary { } cc_binary { + name: "servicemanager.microdroid", + defaults: ["servicemanager_defaults"], + init_rc: ["servicemanager.microdroid.rc"], + srcs: ["main.cpp"], + bootstrap: true, +} + +cc_binary { + name: "servicemanager.recovery", + stem: "servicemanager", + recovery: true, + defaults: ["servicemanager_defaults"], + init_rc: ["servicemanager.recovery.rc"], + srcs: ["main.cpp"], +} + +cc_binary { name: "vndservicemanager", defaults: ["servicemanager_defaults"], init_rc: ["vndservicemanager.rc"], diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 90db5091e1..3cfe5297ca 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -28,6 +28,9 @@ #ifndef VENDORSERVICEMANAGER #include <vintf/VintfObject.h> +#ifdef __ANDROID_RECOVERY__ +#include <vintf/VintfObjectRecovery.h> +#endif // __ANDROID_RECOVERY__ #include <vintf/constants.h> #endif // !VENDORSERVICEMANAGER @@ -37,16 +40,33 @@ using ::android::internal::Stability; namespace android { #ifndef VENDORSERVICEMANAGER + struct ManifestWithDescription { std::shared_ptr<const vintf::HalManifest> manifest; const char* description; }; +static std::vector<ManifestWithDescription> GetManifestsWithDescription() { +#ifdef __ANDROID_RECOVERY__ + auto vintfObject = vintf::VintfObjectRecovery::GetInstance(); + if (vintfObject == nullptr) { + LOG(ERROR) << "NULL VintfObjectRecovery!"; + return {}; + } + return {ManifestWithDescription{vintfObject->getRecoveryHalManifest(), "recovery"}}; +#else + auto vintfObject = vintf::VintfObject::GetInstance(); + if (vintfObject == nullptr) { + LOG(ERROR) << "NULL VintfObject!"; + return {}; + } + return {ManifestWithDescription{vintfObject->getDeviceHalManifest(), "device"}, + ManifestWithDescription{vintfObject->getFrameworkHalManifest(), "framework"}}; +#endif +} + // func true -> stop search and forEachManifest will return true static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) { - for (const ManifestWithDescription& mwd : { - ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" }, - ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" }, - }) { + for (const ManifestWithDescription& mwd : GetManifestsWithDescription()) { if (mwd.manifest == nullptr) { LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description; // note, we explicitly do not retry here, so that we can detect VINTF @@ -93,8 +113,8 @@ static bool isVintfDeclared(const std::string& name) { if (!found) { // Although it is tested, explicitly rebuilding qualified name, in case it // becomes something unexpected. - LOG(ERROR) << "Could not find " << aname.package << "." << aname.iface << "/" - << aname.instance << " in the VINTF manifest."; + LOG(INFO) << "Could not find " << aname.package << "." << aname.iface << "/" + << aname.instance << " in the VINTF manifest."; } return found; @@ -121,6 +141,35 @@ static std::optional<std::string> getVintfUpdatableApex(const std::string& name) return updatableViaApex; } +static std::optional<ConnectionInfo> getVintfConnectionInfo(const std::string& name) { + AidlName aname; + if (!AidlName::fill(name, &aname)) return std::nullopt; + + std::optional<std::string> ip; + std::optional<uint64_t> port; + forEachManifest([&](const ManifestWithDescription& mwd) { + mwd.manifest->forEachInstance([&](const auto& manifestInstance) { + if (manifestInstance.format() != vintf::HalFormat::AIDL) return true; + if (manifestInstance.package() != aname.package) return true; + if (manifestInstance.interface() != aname.iface) return true; + if (manifestInstance.instance() != aname.instance) return true; + ip = manifestInstance.ip(); + port = manifestInstance.port(); + return false; // break (libvintf uses opposite convention) + }); + return false; // continue + }); + + if (ip.has_value() && port.has_value()) { + ConnectionInfo info; + info.ipAddress = *ip; + info.port = *port; + return std::make_optional<ConnectionInfo>(info); + } else { + return std::nullopt; + } +} + static std::vector<std::string> getVintfInstances(const std::string& interface) { size_t lastDot = interface.rfind('.'); if (lastDot == std::string::npos) { @@ -246,28 +295,27 @@ bool isValidServiceName(const std::string& name) { Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) { auto ctx = mAccess->getCallingContext(); - // apps cannot add services if (multiuser_get_app_id(ctx.uid) >= AID_APP) { - return Status::fromExceptionCode(Status::EX_SECURITY); + return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services"); } if (!mAccess->canAdd(ctx, name)) { - return Status::fromExceptionCode(Status::EX_SECURITY); + return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denial"); } if (binder == nullptr) { - return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Null binder"); } if (!isValidServiceName(name)) { LOG(ERROR) << "Invalid service name: " << name; - return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name"); } #ifndef VENDORSERVICEMANAGER if (!meetsDeclarationRequirements(binder, name)) { // already logged - return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "VINTF declaration error"); } #endif // !VENDORSERVICEMANAGER @@ -275,7 +323,7 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi if (binder->remoteBinder() != nullptr && binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) { LOG(ERROR) << "Could not linkToDeath when adding " << name; - return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure"); } // Overwrite the old service if it exists @@ -437,6 +485,22 @@ Status ServiceManager::updatableViaApex(const std::string& name, return Status::ok(); } +Status ServiceManager::getConnectionInfo(const std::string& name, + std::optional<ConnectionInfo>* outReturn) { + auto ctx = mAccess->getCallingContext(); + + if (!mAccess->canFind(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + *outReturn = std::nullopt; + +#ifndef VENDORSERVICEMANAGER + *outReturn = getVintfConnectionInfo(name); +#endif + return Status::ok(); +} + void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who, ServiceCallbackMap::iterator* it, bool* found) { diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h index 4f23c21078..5e403194d7 100644 --- a/cmds/servicemanager/ServiceManager.h +++ b/cmds/servicemanager/ServiceManager.h @@ -24,6 +24,7 @@ namespace android { +using os::ConnectionInfo; using os::IClientCallback; using os::IServiceCallback; using os::ServiceDebugInfo; @@ -48,6 +49,8 @@ public: binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override; binder::Status updatableViaApex(const std::string& name, std::optional<std::string>* outReturn) override; + binder::Status getConnectionInfo(const std::string& name, + std::optional<ConnectionInfo>* outReturn) override; binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service, const sp<IClientCallback>& cb) override; binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override; diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index 8c1beaca20..2fb9c2bc9a 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -111,6 +111,10 @@ private: }; int main(int argc, char** argv) { +#ifdef __ANDROID_RECOVERY__ + android::base::InitLogging(argv, android::base::KernelLogger); +#endif + if (argc > 2) { LOG(FATAL) << "usage: " << argv[0] << " [binder driver]"; } diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc new file mode 100644 index 0000000000..e01f132c64 --- /dev/null +++ b/cmds/servicemanager/servicemanager.microdroid.rc @@ -0,0 +1,8 @@ +service servicemanager /system/bin/servicemanager.microdroid + class core + user system + group system readproc + critical + onrestart restart apexd + task_profiles ServiceCapacityLow + shutdown critical diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc index 6d5070fa04..e5d689ff91 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc @@ -6,8 +6,8 @@ service servicemanager /system/bin/servicemanager onrestart restart apexd onrestart restart audioserver onrestart restart gatekeeperd - onrestart class_restart main - onrestart class_restart hal - onrestart class_restart early_hal - writepid /dev/cpuset/system-background/tasks + onrestart class_restart --only-enabled main + onrestart class_restart --only-enabled hal + onrestart class_restart --only-enabled early_hal + task_profiles ServiceCapacityLow shutdown critical diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc new file mode 100644 index 0000000000..067faf9c8f --- /dev/null +++ b/cmds/servicemanager/servicemanager.recovery.rc @@ -0,0 +1,4 @@ +service servicemanager /system/bin/servicemanager + disabled + group system readproc + seclabel u:r:servicemanager:s0 diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc index 756f6c3bc8..c9305a1c60 100644 --- a/cmds/servicemanager/vndservicemanager.rc +++ b/cmds/servicemanager/vndservicemanager.rc @@ -2,7 +2,7 @@ service vndservicemanager /vendor/bin/vndservicemanager /dev/vndbinder class core user system group system readproc - writepid /dev/cpuset/system-background/tasks + task_profiles ServiceCapacityLow onrestart class_restart main onrestart class_restart hal onrestart class_restart early_hal diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index cfd42fec30..3f7c7d6a7b 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -528,7 +528,7 @@ void Replayer::setTransparentRegionHint(SurfaceComposerClient::Transaction& t, void Replayer::setLayerStack(SurfaceComposerClient::Transaction& t, layer_id id, const LayerStackChange& lsc) { ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack()); - t.setLayerStack(mLayers[id], lsc.layer_stack()); + t.setLayerStack(mLayers[id], ui::LayerStack::fromValue(lsc.layer_stack())); } void Replayer::setHiddenFlag(SurfaceComposerClient::Transaction& t, @@ -566,7 +566,7 @@ void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t, void Replayer::setDisplayLayerStack(SurfaceComposerClient::Transaction& t, display_id id, const LayerStackChange& lsc) { - t.setDisplayLayerStack(mDisplays[id], lsc.layer_stack()); + t.setDisplayLayerStack(mDisplays[id], ui::LayerStack::fromValue(lsc.layer_stack())); } void Replayer::setDisplaySize(SurfaceComposerClient::Transaction& t, |