diff options
author | android-build-prod (mdb) <android-build-team-robot@google.com> | 2019-08-13 02:10:49 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2019-08-13 02:10:49 +0000 |
commit | 72cfb98c38a247aaf59d3f35745fee0548385a63 (patch) | |
tree | 5dd26b2f4810f1d135ad0c93fb8d6e9d583ae199 | |
parent | 2b61ab57074cd968f7deb80cd4e16a54c7f6f57a (diff) | |
parent | 4415fb821de1e148269ccfdd8fb1eeb69fa0400c (diff) | |
download | native-72cfb98c38a247aaf59d3f35745fee0548385a63.tar.gz |
Merge "Snap for 5798008 from 87cf89ca6aff0d2282af0c9d8e17340307d27bdd to sdk-release" into sdk-releaseplatform-tools-29.0.4platform-tools-29.0.3
54 files changed, 2100 insertions, 1766 deletions
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 4020480479..93bbe902c0 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -86,15 +86,14 @@ cc_defaults { "libdumpstateaidl", "libdumpstateutil", "libdumputils", + "libhardware_legacy", "libhidlbase", "libhidltransport", "liblog", "libutils", ], srcs: [ - "DumpstateSectionReporter.cpp", "DumpstateService.cpp", - "utils.cpp", ], static_libs: [ "libincidentcompanion", @@ -146,7 +145,11 @@ cc_test { ], static_libs: ["libgmock"], test_config: "dumpstate_test.xml", - data: [":dumpstate_test_fixture", "tests/testdata/**/*"] + data: [ + ":dumpstate_test_fixture", + "tests/testdata/**/*", + ], + test_suites: ["device-tests"], } cc_test { diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp index 33e35f7274..bbc724c4c0 100644 --- a/cmds/dumpstate/DumpstateInternal.cpp +++ b/cmds/dumpstate/DumpstateInternal.cpp @@ -68,7 +68,8 @@ bool DropRootUser() { } static const std::vector<std::string> group_names{ - "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats", "readproc", "bluetooth"}; + "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats", + "readproc", "bluetooth", "wakelock"}; std::vector<gid_t> groups(group_names.size(), 0); for (size_t i = 0; i < group_names.size(); ++i) { grp = getgrnam(group_names[i].c_str()); @@ -116,6 +117,11 @@ bool DropRootUser() { capdata[cap_syslog_index].effective |= cap_syslog_mask; } + const uint32_t cap_block_suspend_mask = CAP_TO_MASK(CAP_BLOCK_SUSPEND); + const uint32_t cap_block_suspend_index = CAP_TO_INDEX(CAP_BLOCK_SUSPEND); + capdata[cap_block_suspend_index].permitted |= cap_block_suspend_mask; + capdata[cap_block_suspend_index].effective |= cap_block_suspend_mask; + if (capset(&capheader, &capdata[0]) != 0) { MYLOGE("capset({%#x, %#x}) failed: %s\n", capdata[0].effective, capdata[1].effective, strerror(errno)); diff --git a/cmds/dumpstate/DumpstateSectionReporter.cpp b/cmds/dumpstate/DumpstateSectionReporter.cpp deleted file mode 100644 index f814bde26d..0000000000 --- a/cmds/dumpstate/DumpstateSectionReporter.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "dumpstate" - -#include "DumpstateSectionReporter.h" - -namespace android { -namespace os { -namespace dumpstate { - -DumpstateSectionReporter::DumpstateSectionReporter(const std::string& title, - sp<android::os::IDumpstateListener> listener, - bool sendReport) - : title_(title), listener_(listener), sendReport_(sendReport), status_(OK), size_(-1) { - started_ = std::chrono::steady_clock::now(); -} - -DumpstateSectionReporter::~DumpstateSectionReporter() { - if ((listener_ != nullptr) && (sendReport_)) { - auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now() - started_); - listener_->onSectionComplete(title_, status_, size_, (int32_t)elapsed.count()); - } -} - -} // namespace dumpstate -} // namespace os -} // namespace android diff --git a/cmds/dumpstate/DumpstateSectionReporter.h b/cmds/dumpstate/DumpstateSectionReporter.h deleted file mode 100644 index e971de84c5..0000000000 --- a/cmds/dumpstate/DumpstateSectionReporter.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ -#define ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ - -#include <android/os/IDumpstateListener.h> -#include <utils/StrongPointer.h> - -namespace android { -namespace os { -namespace dumpstate { - - -/* - * Helper class used to report per section details to a listener. - * - * Typical usage: - * - * DumpstateSectionReporter sectionReporter(title, listener, sendReport); - * sectionReporter.setSize(5000); - * - */ -class DumpstateSectionReporter { - public: - DumpstateSectionReporter(const std::string& title, sp<android::os::IDumpstateListener> listener, - bool sendReport); - - ~DumpstateSectionReporter(); - - void setStatus(status_t status) { - status_ = status; - } - - void setSize(int size) { - size_ = size; - } - - private: - std::string title_; - android::sp<android::os::IDumpstateListener> listener_; - bool sendReport_; - status_t status_; - int size_; - std::chrono::time_point<std::chrono::steady_clock> started_; -}; - -} // namespace dumpstate -} // namespace os -} // namespace android - -#endif // ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index 37ba4f906e..f98df99534 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -200,8 +200,7 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "id: %d\n", ds_->id_); dprintf(fd, "pid: %d\n", ds_->pid_); dprintf(fd, "update_progress: %s\n", ds_->options_->do_progress_updates ? "true" : "false"); - dprintf(fd, "update_progress_threshold: %d\n", ds_->update_progress_threshold_); - dprintf(fd, "last_updated_progress: %d\n", ds_->last_updated_progress_); + dprintf(fd, "last_percent_progress: %d\n", ds_->last_reported_percent_progress_); dprintf(fd, "progress:\n"); ds_->progress_->Dump(fd, " "); dprintf(fd, "args: %s\n", ds_->options_->args.c_str()); diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING new file mode 100644 index 0000000000..083944f729 --- /dev/null +++ b/cmds/dumpstate/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "dumpstate_test" + } + ] +}
\ No newline at end of file diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl index ea1e467dca..e486460753 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl @@ -61,21 +61,4 @@ interface IDumpstateListener { * Called when taking bugreport finishes successfully. */ void onFinished(); - - // TODO(b/111441001): Remove old methods when not used anymore. - void onProgressUpdated(int progress); - void onMaxProgressUpdated(int maxProgress); - - /** - * Called after every section is complete. - * - * @param name section name - * @param status values from status_t - * {@code OK} section completed successfully - * {@code TIMEOUT} dump timed out - * {@code != OK} error - * @param size size in bytes, may be invalid if status != OK - * @param durationMs duration in ms - */ - void onSectionComplete(@utf8InCpp String name, int status, int size, int durationMs); } diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 1fc8107f9e..e37e32548c 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -21,6 +21,8 @@ #include <fcntl.h> #include <libgen.h> #include <limits.h> +#include <math.h> +#include <poll.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -31,6 +33,13 @@ #include <sys/stat.h> #include <sys/time.h> #include <sys/wait.h> +#include <signal.h> +#include <stdarg.h> +#include <string.h> +#include <sys/capability.h> +#include <sys/inotify.h> +#include <sys/klog.h> +#include <time.h> #include <unistd.h> #include <chrono> @@ -55,17 +64,19 @@ #include <android/os/IIncidentCompanion.h> #include <cutils/native_handle.h> #include <cutils/properties.h> +#include <cutils/sockets.h> #include <debuggerd/client.h> #include <dumpsys.h> #include <dumputils/dump_utils.h> +#include <hardware_legacy/power.h> #include <hidl/ServiceManagement.h> +#include <log/log.h> #include <openssl/sha.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> #include <serviceutils/PriorityDumper.h> #include <utils/StrongPointer.h> #include "DumpstateInternal.h" -#include "DumpstateSectionReporter.h" #include "DumpstateService.h" #include "dumpstate.h" @@ -90,9 +101,28 @@ using android::base::StringPrintf; using android::os::IDumpstateListener; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; -using android::os::dumpstate::DumpstateSectionReporter; using android::os::dumpstate::PropertiesHelper; +// Keep in sync with +// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java +static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds + +/* Most simple commands have 10 as timeout, so 5 is a good estimate */ +static const int32_t WEIGHT_FILE = 5; + +// TODO: temporary variables and functions used during C++ refactoring +static Dumpstate& ds = Dumpstate::GetInstance(); +static int RunCommand(const std::string& title, const std::vector<std::string>& full_command, + const CommandOptions& options = CommandOptions::DEFAULT) { + return ds.RunCommand(title, full_command, options); +} + +// Reasonable value for max stats. +static const int STATS_MAX_N_RUNS = 1000; +static const long STATS_MAX_AVERAGE = 100000; + +CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build(); + typedef Dumpstate::ConsentCallback::ConsentResult UserConsentResult; /* read before root is shed */ @@ -129,7 +159,6 @@ static const std::string ANR_DIR = "/data/anr/"; static const std::string ANR_FILE_PREFIX = "anr_"; // TODO: temporary variables and functions used during C++ refactoring -static Dumpstate& ds = Dumpstate::GetInstance(); #define RETURN_IF_USER_DENIED_CONSENT() \ if (ds.IsUserConsentDenied()) { \ @@ -144,6 +173,8 @@ static Dumpstate& ds = Dumpstate::GetInstance(); func_ptr(__VA_ARGS__); \ RETURN_IF_USER_DENIED_CONSENT(); +static const char* WAKE_LOCK_NAME = "dumpstate_wakelock"; + namespace android { namespace os { namespace { @@ -207,10 +238,6 @@ static bool IsFileEmpty(const std::string& file_path) { } // namespace os } // namespace android -static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand, - const CommandOptions& options = CommandOptions::DEFAULT) { - return ds.RunCommand(title, fullCommand, options); -} static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs, const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS, long dumpsysTimeoutMs = 0) { @@ -987,7 +1014,6 @@ static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, i RETURN_IF_USER_DENIED_CONSENT(); std::string path(title); path.append(" - ").append(String8(service).c_str()); - DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); size_t bytes_written = 0; status_t status = dumpsys.startDumpThread(service, args); if (status == OK) { @@ -995,12 +1021,10 @@ static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, i std::chrono::duration<double> elapsed_seconds; status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout, /* as_proto = */ false, elapsed_seconds, bytes_written); - section_reporter.setSize(bytes_written); dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds); bool dump_complete = (status == OK); dumpsys.stopDumpThread(dump_complete); } - section_reporter.setStatus(status); auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - start); @@ -1063,7 +1087,6 @@ static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priori path.append("_HIGH"); } path.append(kProtoExt); - DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); status_t status = dumpsys.startDumpThread(service, args); if (status == OK) { status = ds.AddZipEntryFromFd(path, dumpsys.getDumpFd(), service_timeout); @@ -1072,8 +1095,6 @@ static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priori } ZipWriter::FileEntry file_entry; ds.zip_writer_->GetLastEntry(&file_entry); - section_reporter.setSize(file_entry.compressed_size); - section_reporter.setStatus(status); auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - start); @@ -1210,7 +1231,6 @@ static Dumpstate::RunStatus dumpstate() { DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources"); DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"); - DumpFile("KERNEL SYNC", "/d/sync"); RunCommand("PROCESSES AND THREADS", {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"}); @@ -2405,6 +2425,13 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, MYLOGI("begin\n"); + if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) { + MYLOGE("Failed to acquire wake lock: %s\n", strerror(errno)); + } else { + // Wake lock will be released automatically on process death + MYLOGD("Wake lock acquired.\n"); + } + register_sig_handler(); // TODO(b/111441001): maybe skip if already started? @@ -2535,7 +2562,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // Dump state for the default case. This also drops root. RunStatus s = DumpstateDefault(); if (s != RunStatus::OK) { - if (s == RunStatus::USER_CONSENT_TIMED_OUT) { + if (s == RunStatus::USER_CONSENT_DENIED) { HandleUserConsentDenied(); } return s; @@ -2731,3 +2758,940 @@ int run_main(int argc, char* argv[]) { exit(2); } } + +// TODO(111441001): Default DumpOptions to sensible values. +Dumpstate::Dumpstate(const std::string& version) + : pid_(getpid()), + options_(new Dumpstate::DumpOptions()), + last_reported_percent_progress_(0), + version_(version), + now_(time(nullptr)) { +} + +Dumpstate& Dumpstate::GetInstance() { + static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT)); + return singleton_; +} + +DurationReporter::DurationReporter(const std::string& title, bool logcat_only) + : title_(title), logcat_only_(logcat_only) { + if (!title_.empty()) { + started_ = Nanotime(); + } +} + +DurationReporter::~DurationReporter() { + if (!title_.empty()) { + float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC; + if (elapsed < .5f) { + return; + } + MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed); + if (logcat_only_) { + return; + } + // Use "Yoda grammar" to make it easier to grep|sort sections. + printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); + } +} + +const int32_t Progress::kDefaultMax = 5000; + +Progress::Progress(const std::string& path) : Progress(Progress::kDefaultMax, 1.1, path) { +} + +Progress::Progress(int32_t initial_max, int32_t progress, float growth_factor) + : Progress(initial_max, growth_factor, "") { + progress_ = progress; +} + +Progress::Progress(int32_t initial_max, float growth_factor, const std::string& path) + : initial_max_(initial_max), + progress_(0), + max_(initial_max), + growth_factor_(growth_factor), + n_runs_(0), + average_max_(0), + path_(path) { + if (!path_.empty()) { + Load(); + } +} + +void Progress::Load() { + MYLOGD("Loading stats from %s\n", path_.c_str()); + std::string content; + if (!android::base::ReadFileToString(path_, &content)) { + MYLOGI("Could not read stats from %s; using max of %d\n", path_.c_str(), max_); + return; + } + if (content.empty()) { + MYLOGE("No stats (empty file) on %s; using max of %d\n", path_.c_str(), max_); + return; + } + std::vector<std::string> lines = android::base::Split(content, "\n"); + + if (lines.size() < 1) { + MYLOGE("Invalid stats on file %s: not enough lines (%d). Using max of %d\n", path_.c_str(), + (int)lines.size(), max_); + return; + } + char* ptr; + n_runs_ = strtol(lines[0].c_str(), &ptr, 10); + average_max_ = strtol(ptr, nullptr, 10); + if (n_runs_ <= 0 || average_max_ <= 0 || n_runs_ > STATS_MAX_N_RUNS || + average_max_ > STATS_MAX_AVERAGE) { + MYLOGE("Invalid stats line on file %s: %s\n", path_.c_str(), lines[0].c_str()); + initial_max_ = Progress::kDefaultMax; + } else { + initial_max_ = average_max_; + } + max_ = initial_max_; + + MYLOGI("Average max progress: %d in %d runs; estimated max: %d\n", average_max_, n_runs_, max_); +} + +void Progress::Save() { + int32_t total = n_runs_ * average_max_ + progress_; + int32_t runs = n_runs_ + 1; + int32_t average = floor(((float)total) / runs); + MYLOGI("Saving stats (total=%d, runs=%d, average=%d) on %s\n", total, runs, average, + path_.c_str()); + if (path_.empty()) { + return; + } + + std::string content = android::base::StringPrintf("%d %d\n", runs, average); + if (!android::base::WriteStringToFile(content, path_)) { + MYLOGE("Could not save stats on %s\n", path_.c_str()); + } +} + +int32_t Progress::Get() const { + return progress_; +} + +bool Progress::Inc(int32_t delta_sec) { + bool changed = false; + if (delta_sec >= 0) { + progress_ += delta_sec; + if (progress_ > max_) { + int32_t old_max = max_; + max_ = floor((float)progress_ * growth_factor_); + MYLOGD("Adjusting max progress from %d to %d\n", old_max, max_); + changed = true; + } + } + return changed; +} + +int32_t Progress::GetMax() const { + return max_; +} + +int32_t Progress::GetInitialMax() const { + return initial_max_; +} + +void Progress::Dump(int fd, const std::string& prefix) const { + const char* pr = prefix.c_str(); + dprintf(fd, "%sprogress: %d\n", pr, progress_); + dprintf(fd, "%smax: %d\n", pr, max_); + dprintf(fd, "%sinitial_max: %d\n", pr, initial_max_); + dprintf(fd, "%sgrowth_factor: %0.2f\n", pr, growth_factor_); + dprintf(fd, "%spath: %s\n", pr, path_.c_str()); + dprintf(fd, "%sn_runs: %d\n", pr, n_runs_); + 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); +} + +std::string Dumpstate::GetPath(const std::string& directory, const std::string& suffix) const { + return android::base::StringPrintf("%s/%s-%s%s", directory.c_str(), base_name_.c_str(), + name_.c_str(), suffix.c_str()); +} + +void Dumpstate::SetProgress(std::unique_ptr<Progress> progress) { + progress_ = std::move(progress); +} + +void for_each_userid(void (*func)(int), const char *header) { + std::string title = header == nullptr ? "for_each_userid" : android::base::StringPrintf( + "for_each_userid(%s)", header); + DurationReporter duration_reporter(title); + if (PropertiesHelper::IsDryRun()) return; + + DIR *d; + struct dirent *de; + + if (header) printf("\n------ %s ------\n", header); + func(0); + + if (!(d = opendir("/data/system/users"))) { + printf("Failed to open /data/system/users (%s)\n", strerror(errno)); + return; + } + + while ((de = readdir(d))) { + int userid; + if (de->d_type != DT_DIR || !(userid = atoi(de->d_name))) { + continue; + } + func(userid); + } + + closedir(d); +} + +static void __for_each_pid(void (*helper)(int, const char *, void *), const char *header, void *arg) { + DIR *d; + struct dirent *de; + + if (!(d = opendir("/proc"))) { + printf("Failed to open /proc (%s)\n", strerror(errno)); + return; + } + + if (header) printf("\n------ %s ------\n", header); + while ((de = readdir(d))) { + if (ds.IsUserConsentDenied()) { + MYLOGE( + "Returning early because user denied consent to share bugreport with calling app."); + closedir(d); + return; + } + int pid; + int fd; + char cmdpath[255]; + char cmdline[255]; + + if (!(pid = atoi(de->d_name))) { + continue; + } + + memset(cmdline, 0, sizeof(cmdline)); + + snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/cmdline", pid); + if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { + TEMP_FAILURE_RETRY(read(fd, cmdline, sizeof(cmdline) - 2)); + close(fd); + if (cmdline[0]) { + helper(pid, cmdline, arg); + continue; + } + } + + // if no cmdline, a kernel thread has comm + snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/comm", pid); + if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { + TEMP_FAILURE_RETRY(read(fd, cmdline + 1, sizeof(cmdline) - 4)); + close(fd); + if (cmdline[1]) { + cmdline[0] = '['; + size_t len = strcspn(cmdline, "\f\b\r\n"); + cmdline[len] = ']'; + cmdline[len+1] = '\0'; + } + } + if (!cmdline[0]) { + strcpy(cmdline, "N/A"); + } + helper(pid, cmdline, arg); + } + + closedir(d); +} + +static void for_each_pid_helper(int pid, const char *cmdline, void *arg) { + for_each_pid_func *func = (for_each_pid_func*) arg; + func(pid, cmdline); +} + +void for_each_pid(for_each_pid_func func, const char *header) { + std::string title = header == nullptr ? "for_each_pid" + : android::base::StringPrintf("for_each_pid(%s)", header); + DurationReporter duration_reporter(title); + if (PropertiesHelper::IsDryRun()) return; + + __for_each_pid(for_each_pid_helper, header, (void *) func); +} + +static void for_each_tid_helper(int pid, const char *cmdline, void *arg) { + DIR *d; + struct dirent *de; + char taskpath[255]; + for_each_tid_func *func = (for_each_tid_func *) arg; + + snprintf(taskpath, sizeof(taskpath), "/proc/%d/task", pid); + + if (!(d = opendir(taskpath))) { + printf("Failed to open %s (%s)\n", taskpath, strerror(errno)); + return; + } + + func(pid, pid, cmdline); + + while ((de = readdir(d))) { + if (ds.IsUserConsentDenied()) { + MYLOGE( + "Returning early because user denied consent to share bugreport with calling app."); + closedir(d); + return; + } + int tid; + int fd; + char commpath[255]; + char comm[255]; + + if (!(tid = atoi(de->d_name))) { + continue; + } + + if (tid == pid) + continue; + + snprintf(commpath, sizeof(commpath), "/proc/%d/comm", tid); + memset(comm, 0, sizeof(comm)); + if ((fd = TEMP_FAILURE_RETRY(open(commpath, O_RDONLY | O_CLOEXEC))) < 0) { + strcpy(comm, "N/A"); + } else { + char *c; + TEMP_FAILURE_RETRY(read(fd, comm, sizeof(comm) - 2)); + close(fd); + + c = strrchr(comm, '\n'); + if (c) { + *c = '\0'; + } + } + func(pid, tid, comm); + } + + closedir(d); +} + +void for_each_tid(for_each_tid_func func, const char *header) { + std::string title = header == nullptr ? "for_each_tid" + : android::base::StringPrintf("for_each_tid(%s)", header); + DurationReporter duration_reporter(title); + + if (PropertiesHelper::IsDryRun()) return; + + __for_each_pid(for_each_tid_helper, header, (void *) func); +} + +void show_wchan(int pid, int tid, const char *name) { + if (PropertiesHelper::IsDryRun()) return; + + char path[255]; + char buffer[255]; + int fd, ret, save_errno; + char name_buffer[255]; + + memset(buffer, 0, sizeof(buffer)); + + snprintf(path, sizeof(path), "/proc/%d/wchan", tid); + if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { + printf("Failed to open '%s' (%s)\n", path, strerror(errno)); + return; + } + + ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + save_errno = errno; + close(fd); + + if (ret < 0) { + printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); + return; + } + + snprintf(name_buffer, sizeof(name_buffer), "%*s%s", + pid == tid ? 0 : 3, "", name); + + printf("%-7d %-32s %s\n", tid, name_buffer, buffer); + + return; +} + +// print time in centiseconds +static void snprcent(char *buffer, size_t len, size_t spc, + unsigned long long time) { + static long hz; // cache discovered hz + + if (hz <= 0) { + hz = sysconf(_SC_CLK_TCK); + if (hz <= 0) { + hz = 1000; + } + } + + // convert to centiseconds + time = (time * 100 + (hz / 2)) / hz; + + char str[16]; + + snprintf(str, sizeof(str), " %llu.%02u", + time / 100, (unsigned)(time % 100)); + size_t offset = strlen(buffer); + snprintf(buffer + offset, (len > offset) ? len - offset : 0, + "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); +} + +// print permille as a percent +static void snprdec(char *buffer, size_t len, size_t spc, unsigned permille) { + char str[16]; + + snprintf(str, sizeof(str), " %u.%u%%", permille / 10, permille % 10); + size_t offset = strlen(buffer); + snprintf(buffer + offset, (len > offset) ? len - offset : 0, + "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); +} + +void show_showtime(int pid, const char *name) { + if (PropertiesHelper::IsDryRun()) return; + + char path[255]; + char buffer[1023]; + int fd, ret, save_errno; + + memset(buffer, 0, sizeof(buffer)); + + snprintf(path, sizeof(path), "/proc/%d/stat", pid); + if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { + printf("Failed to open '%s' (%s)\n", path, strerror(errno)); + return; + } + + ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + save_errno = errno; + close(fd); + + if (ret < 0) { + printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); + return; + } + + // field 14 is utime + // field 15 is stime + // field 42 is iotime + unsigned long long utime = 0, stime = 0, iotime = 0; + if (sscanf(buffer, + "%*u %*s %*s %*d %*d %*d %*d %*d %*d %*d %*d " + "%*d %*d %llu %llu %*d %*d %*d %*d %*d %*d " + "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " + "%*d %*d %*d %*d %*d %*d %*d %*d %*d %llu ", + &utime, &stime, &iotime) != 3) { + return; + } + + unsigned long long total = utime + stime; + if (!total) { + return; + } + + unsigned permille = (iotime * 1000 + (total / 2)) / total; + if (permille > 1000) { + permille = 1000; + } + + // try to beautify and stabilize columns at <80 characters + snprintf(buffer, sizeof(buffer), "%-6d%s", pid, name); + if ((name[0] != '[') || utime) { + snprcent(buffer, sizeof(buffer), 57, utime); + } + snprcent(buffer, sizeof(buffer), 65, stime); + if ((name[0] != '[') || iotime) { + snprcent(buffer, sizeof(buffer), 73, iotime); + } + if (iotime) { + snprdec(buffer, sizeof(buffer), 79, permille); + } + puts(buffer); // adds a trailing newline + + return; +} + +void do_dmesg() { + const char *title = "KERNEL LOG (dmesg)"; + DurationReporter duration_reporter(title); + printf("------ %s ------\n", title); + + if (PropertiesHelper::IsDryRun()) return; + + /* Get size of kernel buffer */ + int size = klogctl(KLOG_SIZE_BUFFER, nullptr, 0); + if (size <= 0) { + printf("Unexpected klogctl return value: %d\n\n", size); + return; + } + char *buf = (char *) malloc(size + 1); + if (buf == nullptr) { + printf("memory allocation failed\n\n"); + return; + } + int retval = klogctl(KLOG_READ_ALL, buf, size); + if (retval < 0) { + printf("klogctl failure\n\n"); + free(buf); + return; + } + buf[retval] = '\0'; + printf("%s\n\n", buf); + free(buf); + return; +} + +void do_showmap(int pid, const char *name) { + char title[255]; + char arg[255]; + + snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name); + snprintf(arg, sizeof(arg), "%d", pid); + RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT); +} + +int Dumpstate::DumpFile(const std::string& title, const std::string& path) { + DurationReporter duration_reporter(title); + + int status = DumpFileToFd(STDOUT_FILENO, title, path); + + UpdateProgress(WEIGHT_FILE); + + return status; +} + +int read_file_as_long(const char *path, long int *output) { + int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC)); + if (fd < 0) { + int err = errno; + MYLOGE("Error opening file descriptor for %s: %s\n", path, strerror(err)); + return -1; + } + char buffer[50]; + ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + if (bytes_read == -1) { + MYLOGE("Error reading file %s: %s\n", path, strerror(errno)); + return -2; + } + if (bytes_read == 0) { + MYLOGE("File %s is empty\n", path); + return -3; + } + *output = atoi(buffer); + return 0; +} + +/* calls skip to gate calling dump_from_fd recursively + * in the specified directory. dump_from_fd defaults to + * dump_file_from_fd above when set to NULL. skip defaults + * to false when set to NULL. dump_from_fd will always be + * called with title NULL. + */ +int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path), + int (*dump_from_fd)(const char* title, const char* path, int fd)) { + DurationReporter duration_reporter(title); + DIR *dirp; + struct dirent *d; + char *newpath = nullptr; + const char *slash = "/"; + int retval = 0; + + if (!title.empty()) { + printf("------ %s (%s) ------\n", title.c_str(), dir); + } + if (PropertiesHelper::IsDryRun()) return 0; + + if (dir[strlen(dir) - 1] == '/') { + ++slash; + } + dirp = opendir(dir); + if (dirp == nullptr) { + retval = -errno; + MYLOGE("%s: %s\n", dir, strerror(errno)); + return retval; + } + + if (!dump_from_fd) { + dump_from_fd = dump_file_from_fd; + } + for (; ((d = readdir(dirp))); free(newpath), newpath = nullptr) { + if ((d->d_name[0] == '.') + && (((d->d_name[1] == '.') && (d->d_name[2] == '\0')) + || (d->d_name[1] == '\0'))) { + continue; + } + asprintf(&newpath, "%s%s%s%s", dir, slash, d->d_name, + (d->d_type == DT_DIR) ? "/" : ""); + if (!newpath) { + retval = -errno; + continue; + } + if (skip && (*skip)(newpath)) { + continue; + } + if (d->d_type == DT_DIR) { + int ret = dump_files("", newpath, skip, dump_from_fd); + if (ret < 0) { + retval = ret; + } + continue; + } + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC))); + if (fd.get() < 0) { + retval = -1; + printf("*** %s: %s\n", newpath, strerror(errno)); + continue; + } + (*dump_from_fd)(nullptr, newpath, fd.get()); + } + closedir(dirp); + if (!title.empty()) { + printf("\n"); + } + return retval; +} + +/* fd must have been opened with the flag O_NONBLOCK. With this flag set, + * it's possible to avoid issues where opening the file itself can get + * stuck. + */ +int dump_file_from_fd(const char *title, const char *path, int fd) { + if (PropertiesHelper::IsDryRun()) return 0; + + int flags = fcntl(fd, F_GETFL); + if (flags == -1) { + printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno)); + return -1; + } else if (!(flags & O_NONBLOCK)) { + printf("*** %s: fd must have O_NONBLOCK set.\n", path); + return -1; + } + return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun()); +} + +int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command, + const CommandOptions& options) { + DurationReporter duration_reporter(title); + + int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options); + + /* TODO: for now we're simplifying the progress calculation by using the + * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys, + * where its weight should be much higher proportionally to its timeout. + * Ideally, it should use a options.EstimatedDuration() instead...*/ + UpdateProgress(options.Timeout()); + + return status; +} + +void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args, + const CommandOptions& options, long dumpsysTimeoutMs) { + long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs(); + std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)}; + dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end()); + RunCommand(title, dumpsys, options); +} + +int open_socket(const char *service) { + int s = android_get_control_socket(service); + if (s < 0) { + MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno)); + return -1; + } + fcntl(s, F_SETFD, FD_CLOEXEC); + + // Set backlog to 0 to make sure that queue size will be minimum. + // In Linux, because the minimum queue will be 1, connect() will be blocked + // if the other clients already called connect() and the connection request was not accepted. + if (listen(s, 0) < 0) { + MYLOGE("listen(control socket): %s\n", strerror(errno)); + return -1; + } + + struct sockaddr addr; + socklen_t alen = sizeof(addr); + int fd = accept(s, &addr, &alen); + + // Close socket just after accept(), to make sure that connect() by client will get error + // when the socket is used by the other services. + // There is still a race condition possibility between accept and close, but there is no way + // to close-on-accept atomically. + // See detail; b/123306389#comment25 + close(s); + + if (fd < 0) { + MYLOGE("accept(control socket): %s\n", strerror(errno)); + return -1; + } + + return fd; +} + +/* redirect output to a service control socket */ +bool redirect_to_socket(FILE* redirect, const char* service) { + int fd = open_socket(service); + if (fd == -1) { + return false; + } + fflush(redirect); + // TODO: handle dup2 failure + TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); + close(fd); + return true; +} + +// TODO: should call is_valid_output_file and/or be merged into it. +void create_parent_dirs(const char *path) { + char *chp = const_cast<char *> (path); + + /* skip initial slash */ + if (chp[0] == '/') + chp++; + + /* create leading directories, if necessary */ + struct stat dir_stat; + while (chp && chp[0]) { + chp = strchr(chp, '/'); + if (chp) { + *chp = 0; + if (stat(path, &dir_stat) == -1 || !S_ISDIR(dir_stat.st_mode)) { + MYLOGI("Creating directory %s\n", path); + if (mkdir(path, 0770)) { /* drwxrwx--- */ + MYLOGE("Unable to create directory %s: %s\n", path, strerror(errno)); + } else if (chown(path, AID_SHELL, AID_SHELL)) { + MYLOGE("Unable to change ownership of dir %s: %s\n", path, strerror(errno)); + } + } + *chp++ = '/'; + } + } +} + +bool _redirect_to_file(FILE* redirect, char* path, int truncate_flag) { + create_parent_dirs(path); + + int fd = TEMP_FAILURE_RETRY(open(path, + O_WRONLY | O_CREAT | truncate_flag | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); + if (fd < 0) { + MYLOGE("%s: %s\n", path, strerror(errno)); + return false; + } + + TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); + close(fd); + return true; +} + +bool redirect_to_file(FILE* redirect, char* path) { + return _redirect_to_file(redirect, path, O_TRUNC); +} + +bool redirect_to_existing_file(FILE* redirect, char* path) { + return _redirect_to_file(redirect, path, O_APPEND); +} + +void dump_route_tables() { + DurationReporter duration_reporter("DUMP ROUTE TABLES"); + if (PropertiesHelper::IsDryRun()) return; + const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables"; + ds.DumpFile("RT_TABLES", RT_TABLES_PATH); + FILE* fp = fopen(RT_TABLES_PATH, "re"); + if (!fp) { + printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno)); + return; + } + char table[16]; + // Each line has an integer (the table number), a space, and a string (the table name). We only + // need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name. + // Add a fixed max limit so this doesn't go awry. + for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) { + RunCommand("ROUTE TABLE IPv4", {"ip", "-4", "route", "show", "table", table}); + RunCommand("ROUTE TABLE IPv6", {"ip", "-6", "route", "show", "table", table}); + } + fclose(fp); +} + +// TODO: make this function thread safe if sections are generated in parallel. +void Dumpstate::UpdateProgress(int32_t delta_sec) { + if (progress_ == nullptr) { + MYLOGE("UpdateProgress: progress_ not set\n"); + return; + } + + // Always update progess so stats can be tuned... + progress_->Inc(delta_sec); + + // ...but only notifiy listeners when necessary. + if (!options_->do_progress_updates) return; + + int progress = progress_->Get(); + int max = progress_->GetMax(); + int percent = 100 * progress / max; + + if (last_reported_percent_progress_ > 0 && percent <= last_reported_percent_progress_) { + return; + } + last_reported_percent_progress_ = percent; + + if (control_socket_fd_ >= 0) { + dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress, max); + fsync(control_socket_fd_); + } + + if (listener_ != nullptr) { + if (percent % 5 == 0) { + // We don't want to spam logcat, so only log multiples of 5. + MYLOGD("Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), progress, max, + percent); + } else { + // stderr is ignored on normal invocations, but useful when calling + // /system/bin/dumpstate directly for debuggging. + fprintf(stderr, "Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), + progress, max, percent); + } + + listener_->onProgress(percent); + } +} + +void Dumpstate::TakeScreenshot(const std::string& path) { + const std::string& real_path = path.empty() ? screenshot_path_ : path; + int status = + RunCommand("", {"/system/bin/screencap", "-p", real_path}, + CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); + if (status == 0) { + MYLOGD("Screenshot saved on %s\n", real_path.c_str()); + } else { + MYLOGE("Failed to take screenshot on %s\n", real_path.c_str()); + } +} + +bool is_dir(const char* pathname) { + struct stat info; + if (stat(pathname, &info) == -1) { + return false; + } + return S_ISDIR(info.st_mode); +} + +time_t get_mtime(int fd, time_t default_mtime) { + struct stat info; + if (fstat(fd, &info) == -1) { + return default_mtime; + } + return info.st_mtime; +} + +void dump_emmc_ecsd(const char *ext_csd_path) { + // List of interesting offsets + struct hex { + char str[2]; + }; + static const size_t EXT_CSD_REV = 192 * sizeof(hex); + static const size_t EXT_PRE_EOL_INFO = 267 * sizeof(hex); + static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_A = 268 * sizeof(hex); + static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_B = 269 * sizeof(hex); + + std::string buffer; + if (!android::base::ReadFileToString(ext_csd_path, &buffer)) { + return; + } + + printf("------ %s Extended CSD ------\n", ext_csd_path); + + if (buffer.length() < (EXT_CSD_REV + sizeof(hex))) { + printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length()); + return; + } + + int ext_csd_rev = 0; + std::string sub = buffer.substr(EXT_CSD_REV, sizeof(hex)); + if (sscanf(sub.c_str(), "%2x", &ext_csd_rev) != 1) { + printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n", ext_csd_path, sub.c_str()); + return; + } + + static const char *ver_str[] = { + "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0" + }; + printf("rev 1.%d (MMC %s)\n", ext_csd_rev, + (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? ver_str[ext_csd_rev] + : "Unknown"); + if (ext_csd_rev < 7) { + printf("\n"); + return; + } + + if (buffer.length() < (EXT_PRE_EOL_INFO + sizeof(hex))) { + printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length()); + return; + } + + int ext_pre_eol_info = 0; + sub = buffer.substr(EXT_PRE_EOL_INFO, sizeof(hex)); + if (sscanf(sub.c_str(), "%2x", &ext_pre_eol_info) != 1) { + printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n", ext_csd_path, sub.c_str()); + return; + } + + static const char *eol_str[] = { + "Undefined", + "Normal", + "Warning (consumed 80% of reserve)", + "Urgent (consumed 90% of reserve)" + }; + printf( + "PRE_EOL_INFO %d (MMC %s)\n", ext_pre_eol_info, + eol_str[(ext_pre_eol_info < (int)(sizeof(eol_str) / sizeof(eol_str[0]))) ? ext_pre_eol_info + : 0]); + + for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A; + lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B; + lifetime += sizeof(hex)) { + int ext_device_life_time_est; + static const char *est_str[] = { + "Undefined", + "0-10% of device lifetime used", + "10-20% of device lifetime used", + "20-30% of device lifetime used", + "30-40% of device lifetime used", + "40-50% of device lifetime used", + "50-60% of device lifetime used", + "60-70% of device lifetime used", + "70-80% of device lifetime used", + "80-90% of device lifetime used", + "90-100% of device lifetime used", + "Exceeded the maximum estimated device lifetime", + }; + + if (buffer.length() < (lifetime + sizeof(hex))) { + printf("*** %s: truncated content %zu\n", ext_csd_path, buffer.length()); + break; + } + + ext_device_life_time_est = 0; + sub = buffer.substr(lifetime, sizeof(hex)); + if (sscanf(sub.c_str(), "%2x", &ext_device_life_time_est) != 1) { + printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n", ext_csd_path, + (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A', + sub.c_str()); + continue; + } + printf("DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n", + (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A', + ext_device_life_time_est, + est_str[(ext_device_life_time_est < (int)(sizeof(est_str) / sizeof(est_str[0]))) + ? ext_device_life_time_est + : 0]); + } + + printf("\n"); +} + diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index ae6a72171a..f137fc927a 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -400,12 +400,8 @@ class Dumpstate { // Runtime options. std::unique_ptr<DumpOptions> options_; - // How frequently the progess should be updated;the listener will only be notificated when the - // delta from the previous update is more than the threshold. - int32_t update_progress_threshold_ = 100; - - // Last progress that triggered a listener updated - int32_t last_updated_progress_; + // Last progress that was sent to the listener [0-100]. + int last_reported_percent_progress_ = 0; // Whether it should take an screenshot earlier in the process. bool do_early_screenshot_ = false; diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp index 5bde7db287..181046a7a7 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -14,20 +14,21 @@ * limitations under the License. */ -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <fcntl.h> -#include <libgen.h> - #include <android-base/file.h> #include <android/os/BnDumpstate.h> #include <android/os/BnDumpstateListener.h> #include <binder/IServiceManager.h> #include <binder/ProcessState.h> #include <cutils/properties.h> +#include <fcntl.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <libgen.h> #include <ziparchive/zip_archive.h> +#include <fstream> +#include <regex> + #include "dumpstate.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) @@ -44,6 +45,11 @@ class DumpstateListener; namespace { +struct SectionInfo { + std::string name; + int32_t size_bytes; +}; + sp<IDumpstate> GetDumpstateService() { return android::interface_cast<IDumpstate>( android::defaultServiceManager()->getService(String16("dumpstate"))); @@ -55,14 +61,79 @@ int OpenForWrite(const std::string& filename) { S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); } -} // namespace +void GetEntry(const ZipArchiveHandle archive, const std::string_view entry_name, ZipEntry* data) { + int32_t e = FindEntry(archive, entry_name, data); + EXPECT_EQ(e, 0) << ErrorCodeString(e) << " entry name: " << entry_name; +} -struct SectionInfo { - std::string name; - status_t status; - int32_t size_bytes; - int32_t duration_ms; -}; +// Extracts the main bugreport txt from the given archive and writes into output_fd. +void ExtractBugreport(const ZipArchiveHandle* handle, int output_fd) { + // Read contents of main_entry.txt which is a single line indicating the name of the zip entry + // that contains the main bugreport txt. + ZipEntry main_entry; + GetEntry(*handle, "main_entry.txt", &main_entry); + std::string bugreport_txt_name; + bugreport_txt_name.resize(main_entry.uncompressed_length); + ExtractToMemory(*handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()), + main_entry.uncompressed_length); + + // Read the main bugreport txt and extract to output_fd. + ZipEntry entry; + GetEntry(*handle, bugreport_txt_name, &entry); + ExtractEntryToFile(*handle, &entry, output_fd); +} + +bool IsSectionStart(const std::string& line, std::string* section_name) { + static const std::regex kSectionStart = std::regex{"DUMP OF SERVICE (.*):"}; + std::smatch match; + if (std::regex_match(line, match, kSectionStart)) { + *section_name = match.str(1); + return true; + } + return false; +} + +bool IsSectionEnd(const std::string& line) { + // Not all lines that contain "was the duration of" is a section end, but all section ends do + // contain "was the duration of". The disambiguation can be done by the caller. + return (line.find("was the duration of") != std::string::npos); +} + +// Extracts the zipped bugreport and identifies the sections. +void ParseSections(const std::string& zip_path, std::vector<SectionInfo>* sections) { + // Open the archive + ZipArchiveHandle handle; + ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0); + + // Extract the main entry to a temp file + TemporaryFile tmp_binary; + ASSERT_NE(-1, tmp_binary.fd); + ExtractBugreport(&handle, tmp_binary.fd); + + // Read line by line and identify sections + std::ifstream ifs(tmp_binary.path, std::ifstream::in); + std::string line; + int section_bytes = 0; + std::string current_section_name; + while (std::getline(ifs, line)) { + std::string section_name; + if (IsSectionStart(line, §ion_name)) { + section_bytes = 0; + current_section_name = section_name; + } else if (IsSectionEnd(line)) { + if (!current_section_name.empty()) { + sections->push_back({current_section_name, section_bytes}); + } + current_section_name = ""; + } else if (!current_section_name.empty()) { + section_bytes += line.length(); + } + } + + CloseArchive(handle); +} + +} // namespace /** * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the @@ -96,26 +167,6 @@ class DumpstateListener : public BnDumpstateListener { return binder::Status::ok(); } - binder::Status onProgressUpdated(int32_t progress) override { - dprintf(out_fd_, "\rIn progress %d/%d", progress, max_progress_); - return binder::Status::ok(); - } - - binder::Status onMaxProgressUpdated(int32_t max_progress) override { - std::lock_guard<std::mutex> lock(lock_); - max_progress_ = max_progress; - return binder::Status::ok(); - } - - binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes, - int32_t duration_ms) override { - std::lock_guard<std::mutex> lock(lock_); - if (sections_.get() != nullptr) { - sections_->push_back({name, status, size_bytes, duration_ms}); - } - return binder::Status::ok(); - } - bool getIsFinished() { std::lock_guard<std::mutex> lock(lock_); return is_finished_; @@ -128,7 +179,6 @@ class DumpstateListener : public BnDumpstateListener { private: int out_fd_; - int max_progress_ = 5000; int error_code_ = -1; bool is_finished_ = false; std::shared_ptr<std::vector<SectionInfo>> sections_; @@ -208,29 +258,30 @@ class ZippedBugReportContentsTest : public Test { void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) { ZipEntry entry; - EXPECT_EQ(FindEntry(handle, filename, &entry), 0); + GetEntry(handle, filename, &entry); EXPECT_GT(entry.uncompressed_length, minsize); EXPECT_LT(entry.uncompressed_length, maxsize); } }; TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) { - ZipEntry mainEntryLoc; + ZipEntry main_entry; // contains main entry name file - EXPECT_EQ(FindEntry(handle, "main_entry.txt", &mainEntryLoc), 0); + GetEntry(handle, "main_entry.txt", &main_entry); - char* buf = new char[mainEntryLoc.uncompressed_length]; - ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length); - delete[] buf; + std::string bugreport_txt_name; + bugreport_txt_name.resize(main_entry.uncompressed_length); + ExtractToMemory(handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()), + main_entry.uncompressed_length); // contains main entry file - FileExists(buf, 1000000U, 50000000U); + FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U); } TEST_F(ZippedBugReportContentsTest, ContainsVersion) { ZipEntry entry; // contains main entry name file - EXPECT_EQ(FindEntry(handle, "version.txt", &entry), 0); + GetEntry(handle, "version.txt", &entry); char* buf = new char[entry.uncompressed_length + 1]; ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length); @@ -244,6 +295,10 @@ TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) { FileExists("dumpstate_board.txt", 100000U, 1000000U); } +TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) { + FileExists("proto/activity.proto", 100000U, 1000000U); +} + // Spot check on some files pulled from the file system TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) { // FS/proc/*/mountinfo size > 0 @@ -258,6 +313,11 @@ TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) { */ class BugreportSectionTest : public Test { public: + static void SetUpTestCase() { + ParseSections(ZippedBugreportGenerationTest::getZipFilePath(), + ZippedBugreportGenerationTest::sections.get()); + } + int numMatches(const std::string& substring) { int matches = 0; for (auto const& section : *ZippedBugreportGenerationTest::sections) { @@ -267,10 +327,11 @@ class BugreportSectionTest : public Test { } return matches; } + void SectionExists(const std::string& sectionName, int minsize) { for (auto const& section : *ZippedBugreportGenerationTest::sections) { if (sectionName == section.name) { - EXPECT_GE(section.size_bytes, minsize); + EXPECT_GE(section.size_bytes, minsize) << " for section:" << sectionName; return; } } @@ -278,71 +339,59 @@ class BugreportSectionTest : public Test { } }; -// Test all sections are generated without timeouts or errors -TEST_F(BugreportSectionTest, GeneratedWithoutErrors) { - for (auto const& section : *ZippedBugreportGenerationTest::sections) { - EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status; - } -} - TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) { - int numSections = numMatches("DUMPSYS CRITICAL"); + int numSections = numMatches("CRITICAL"); EXPECT_GE(numSections, 3); } TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) { - int numSections = numMatches("DUMPSYS HIGH"); + int numSections = numMatches("HIGH"); EXPECT_GE(numSections, 2); } TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) { - int allSections = numMatches("DUMPSYS"); - int criticalSections = numMatches("DUMPSYS CRITICAL"); - int highSections = numMatches("DUMPSYS HIGH"); + int allSections = ZippedBugreportGenerationTest::sections->size(); + int criticalSections = numMatches("CRITICAL"); + int highSections = numMatches("HIGH"); int normalSections = allSections - criticalSections - highSections; EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections << "High:" << highSections << "Normal:" << normalSections << ")"; } -TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) { - int numSections = numMatches("proto/"); - EXPECT_GE(numSections, 1); -} - // Test if some critical sections are being generated. TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) { - SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000); + SectionExists("CRITICAL SurfaceFlinger", /* bytes= */ 10000); } TEST_F(BugreportSectionTest, ActivitySectionsGenerated) { - SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000); - SectionExists("DUMPSYS - activity", /* bytes= */ 10000); + SectionExists("CRITICAL activity", /* bytes= */ 5000); + SectionExists("activity", /* bytes= */ 10000); } TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) { - SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000); + SectionExists("CRITICAL cpuinfo", /* bytes= */ 1000); } TEST_F(BugreportSectionTest, WindowSectionGenerated) { - SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000); + SectionExists("CRITICAL window", /* bytes= */ 20000); } TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) { - SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000); - SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000); + SectionExists("HIGH connectivity", /* bytes= */ 3000); + SectionExists("connectivity", /* bytes= */ 5000); } TEST_F(BugreportSectionTest, MeminfoSectionGenerated) { - SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000); + SectionExists("HIGH meminfo", /* bytes= */ 100000); } TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) { - SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000); + SectionExists("batterystats", /* bytes= */ 1000); } TEST_F(BugreportSectionTest, WifiSectionGenerated) { - SectionExists("DUMPSYS - wifi", /* bytes= */ 100000); + SectionExists("wifi", /* bytes= */ 100000); } class DumpstateBinderTest : public Test { diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index 4e6b084ff6..cff1d439d9 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -62,10 +62,6 @@ class DumpstateListenerMock : public IDumpstateListener { MOCK_METHOD1(onProgress, binder::Status(int32_t progress)); MOCK_METHOD1(onError, binder::Status(int32_t error_code)); MOCK_METHOD0(onFinished, binder::Status()); - MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress)); - MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress)); - MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status, - int32_t size, int32_t durationMs)); protected: MOCK_METHOD0(onAsBinder, IBinder*()); @@ -590,7 +586,6 @@ class DumpstateTest : public DumpstateBaseTest { SetDryRun(false); SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)")); ds.progress_.reset(new Progress()); - ds.update_progress_threshold_ = 0; ds.options_.reset(new Dumpstate::DumpOptions()); } @@ -615,10 +610,9 @@ class DumpstateTest : public DumpstateBaseTest { return status; } - void SetProgress(long progress, long initial_max, long threshold = 0) { + void SetProgress(long progress, long initial_max) { + ds.last_reported_percent_progress_ = 0; ds.options_->do_progress_updates = true; - ds.update_progress_threshold_ = threshold; - ds.last_updated_progress_ = 0; ds.progress_.reset(new Progress(initial_max, progress, 1.2)); } @@ -796,73 +790,36 @@ TEST_F(DumpstateTest, RunCommandProgress) { ds.listener_name_ = "FoxMulder"; SetProgress(0, 30); - EXPECT_CALL(*listener, onProgressUpdated(20)); EXPECT_CALL(*listener, onProgress(66)); // 20/30 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build())); std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); - EXPECT_CALL(*listener, onProgressUpdated(30)); - EXPECT_CALL(*listener, onProgress(100)); // 35/35 % - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Build())); - progress_message = GetProgressMessage(ds.listener_name_, 30, 30); - EXPECT_THAT(out, StrEq("stdout\n")); - EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); - - // Run a command that will increase maximum timeout. - EXPECT_CALL(*listener, onProgressUpdated(31)); - EXPECT_CALL(*listener, onMaxProgressUpdated(37)); - EXPECT_CALL(*listener, onProgress(83)); // 31/37 % - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); - progress_message = GetProgressMessage(ds.listener_name_, 31, 37, 30); // 20% increase + EXPECT_CALL(*listener, onProgress(80)); // 24/30 % + EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build())); + progress_message = GetProgressMessage(ds.listener_name_, 24, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); // Make sure command ran while in dry_run is counted. SetDryRun(true); - EXPECT_CALL(*listener, onProgressUpdated(35)); - EXPECT_CALL(*listener, onProgress(94)); // 35/37 % - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build())); - progress_message = GetProgressMessage(ds.listener_name_, 35, 37); + EXPECT_CALL(*listener, onProgress(90)); // 27/30 % + EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build())); + progress_message = GetProgressMessage(ds.listener_name_, 27, 30); EXPECT_THAT(out, IsEmpty()); EXPECT_THAT(err, StrEq(progress_message)); - ds.listener_.clear(); -} - -TEST_F(DumpstateTest, RunCommandProgressIgnoreThreshold) { - sp<DumpstateListenerMock> listener(new DumpstateListenerMock()); - ds.listener_ = listener; - ds.listener_name_ = "FoxMulder"; - SetProgress(0, 8, 5); // 8 max, 5 threshold - - // First update should always be sent. - EXPECT_CALL(*listener, onProgressUpdated(1)); - EXPECT_CALL(*listener, onProgress(12)); // 1/12 % - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); - std::string progress_message = GetProgressMessage(ds.listener_name_, 1, 8); + SetDryRun(false); + EXPECT_CALL(*listener, onProgress(96)); // 29/30 % + EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(2).Build())); + progress_message = GetProgressMessage(ds.listener_name_, 29, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); - // Fourth update should be ignored because it's between the threshold (5 -1 = 4 < 5). - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build())); - EXPECT_THAT(out, StrEq("stdout\n")); - EXPECT_THAT(err, StrEq("stderr\n")); - - // Third update should be sent because it reaches threshold (6 - 1 = 5). - EXPECT_CALL(*listener, onProgressUpdated(6)); - EXPECT_CALL(*listener, onProgress(75)); // 6/8 % + EXPECT_CALL(*listener, onProgress(100)); // 30/30 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); - progress_message = GetProgressMessage(ds.listener_name_, 6, 8); - EXPECT_THAT(out, StrEq("stdout\n")); - EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); - - // Fourth update should be ignored because it's between the threshold (9 - 6 = 3 < 5). - // But max update should be sent. - EXPECT_CALL(*listener, onMaxProgressUpdated(10)); // 9 * 120% = 10.8 = 10 - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build())); - progress_message = GetProgressMessage(ds.listener_name_, 9, 10, 8, false); + progress_message = GetProgressMessage(ds.listener_name_, 30, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); @@ -1090,7 +1047,6 @@ TEST_F(DumpstateTest, DumpFileUpdateProgress) { ds.listener_name_ = "FoxMulder"; SetProgress(0, 30); - EXPECT_CALL(*listener, onProgressUpdated(5)); EXPECT_CALL(*listener, onProgress(16)); // 5/30 % EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt")); diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp deleted file mode 100644 index e08c80627c..0000000000 --- a/cmds/dumpstate/utils.cpp +++ /dev/null @@ -1,1024 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "dumpstate" - -#include "dumpstate.h" - -#include <dirent.h> -#include <fcntl.h> -#include <libgen.h> -#include <math.h> -#include <poll.h> -#include <signal.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/capability.h> -#include <sys/inotify.h> -#include <sys/klog.h> -#include <sys/prctl.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <time.h> -#include <unistd.h> - -#include <memory> -#include <set> -#include <string> -#include <vector> - -#include <android-base/file.h> -#include <android-base/properties.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <android-base/unique_fd.h> -#include <cutils/properties.h> -#include <cutils/sockets.h> -#include <log/log.h> -#include <private/android_filesystem_config.h> - -#include "DumpstateInternal.h" - -// TODO: remove once moved to namespace -using android::os::dumpstate::CommandOptions; -using android::os::dumpstate::DumpFileToFd; -using android::os::dumpstate::PropertiesHelper; - -// Keep in sync with -// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java -static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds - -/* Most simple commands have 10 as timeout, so 5 is a good estimate */ -static const int32_t WEIGHT_FILE = 5; - -// TODO: temporary variables and functions used during C++ refactoring -static Dumpstate& ds = Dumpstate::GetInstance(); -static int RunCommand(const std::string& title, const std::vector<std::string>& full_command, - const CommandOptions& options = CommandOptions::DEFAULT) { - return ds.RunCommand(title, full_command, options); -} - -// Reasonable value for max stats. -static const int STATS_MAX_N_RUNS = 1000; -static const long STATS_MAX_AVERAGE = 100000; - -CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build(); - -// TODO(111441001): Default DumpOptions to sensible values. -Dumpstate::Dumpstate(const std::string& version) - : pid_(getpid()), - options_(new Dumpstate::DumpOptions()), - version_(version), - now_(time(nullptr)) { -} - -Dumpstate& Dumpstate::GetInstance() { - static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT)); - return singleton_; -} - -DurationReporter::DurationReporter(const std::string& title, bool logcat_only) - : title_(title), logcat_only_(logcat_only) { - if (!title_.empty()) { - started_ = Nanotime(); - } -} - -DurationReporter::~DurationReporter() { - if (!title_.empty()) { - float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC; - if (elapsed < .5f) { - return; - } - MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed); - if (logcat_only_) { - return; - } - // Use "Yoda grammar" to make it easier to grep|sort sections. - printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); - } -} - -const int32_t Progress::kDefaultMax = 5000; - -Progress::Progress(const std::string& path) : Progress(Progress::kDefaultMax, 1.1, path) { -} - -Progress::Progress(int32_t initial_max, int32_t progress, float growth_factor) - : Progress(initial_max, growth_factor, "") { - progress_ = progress; -} - -Progress::Progress(int32_t initial_max, float growth_factor, const std::string& path) - : initial_max_(initial_max), - progress_(0), - max_(initial_max), - growth_factor_(growth_factor), - n_runs_(0), - average_max_(0), - path_(path) { - if (!path_.empty()) { - Load(); - } -} - -void Progress::Load() { - MYLOGD("Loading stats from %s\n", path_.c_str()); - std::string content; - if (!android::base::ReadFileToString(path_, &content)) { - MYLOGI("Could not read stats from %s; using max of %d\n", path_.c_str(), max_); - return; - } - if (content.empty()) { - MYLOGE("No stats (empty file) on %s; using max of %d\n", path_.c_str(), max_); - return; - } - std::vector<std::string> lines = android::base::Split(content, "\n"); - - if (lines.size() < 1) { - MYLOGE("Invalid stats on file %s: not enough lines (%d). Using max of %d\n", path_.c_str(), - (int)lines.size(), max_); - return; - } - char* ptr; - n_runs_ = strtol(lines[0].c_str(), &ptr, 10); - average_max_ = strtol(ptr, nullptr, 10); - if (n_runs_ <= 0 || average_max_ <= 0 || n_runs_ > STATS_MAX_N_RUNS || - average_max_ > STATS_MAX_AVERAGE) { - MYLOGE("Invalid stats line on file %s: %s\n", path_.c_str(), lines[0].c_str()); - initial_max_ = Progress::kDefaultMax; - } else { - initial_max_ = average_max_; - } - max_ = initial_max_; - - MYLOGI("Average max progress: %d in %d runs; estimated max: %d\n", average_max_, n_runs_, max_); -} - -void Progress::Save() { - int32_t total = n_runs_ * average_max_ + progress_; - int32_t runs = n_runs_ + 1; - int32_t average = floor(((float)total) / runs); - MYLOGI("Saving stats (total=%d, runs=%d, average=%d) on %s\n", total, runs, average, - path_.c_str()); - if (path_.empty()) { - return; - } - - std::string content = android::base::StringPrintf("%d %d\n", runs, average); - if (!android::base::WriteStringToFile(content, path_)) { - MYLOGE("Could not save stats on %s\n", path_.c_str()); - } -} - -int32_t Progress::Get() const { - return progress_; -} - -bool Progress::Inc(int32_t delta_sec) { - bool changed = false; - if (delta_sec >= 0) { - progress_ += delta_sec; - if (progress_ > max_) { - int32_t old_max = max_; - max_ = floor((float)progress_ * growth_factor_); - MYLOGD("Adjusting max progress from %d to %d\n", old_max, max_); - changed = true; - } - } - return changed; -} - -int32_t Progress::GetMax() const { - return max_; -} - -int32_t Progress::GetInitialMax() const { - return initial_max_; -} - -void Progress::Dump(int fd, const std::string& prefix) const { - const char* pr = prefix.c_str(); - dprintf(fd, "%sprogress: %d\n", pr, progress_); - dprintf(fd, "%smax: %d\n", pr, max_); - dprintf(fd, "%sinitial_max: %d\n", pr, initial_max_); - dprintf(fd, "%sgrowth_factor: %0.2f\n", pr, growth_factor_); - dprintf(fd, "%spath: %s\n", pr, path_.c_str()); - dprintf(fd, "%sn_runs: %d\n", pr, n_runs_); - 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); -} - -std::string Dumpstate::GetPath(const std::string& directory, const std::string& suffix) const { - return android::base::StringPrintf("%s/%s-%s%s", directory.c_str(), base_name_.c_str(), - name_.c_str(), suffix.c_str()); -} - -void Dumpstate::SetProgress(std::unique_ptr<Progress> progress) { - progress_ = std::move(progress); -} - -void for_each_userid(void (*func)(int), const char *header) { - std::string title = header == nullptr ? "for_each_userid" : android::base::StringPrintf( - "for_each_userid(%s)", header); - DurationReporter duration_reporter(title); - if (PropertiesHelper::IsDryRun()) return; - - DIR *d; - struct dirent *de; - - if (header) printf("\n------ %s ------\n", header); - func(0); - - if (!(d = opendir("/data/system/users"))) { - printf("Failed to open /data/system/users (%s)\n", strerror(errno)); - return; - } - - while ((de = readdir(d))) { - int userid; - if (de->d_type != DT_DIR || !(userid = atoi(de->d_name))) { - continue; - } - func(userid); - } - - closedir(d); -} - -static void __for_each_pid(void (*helper)(int, const char *, void *), const char *header, void *arg) { - DIR *d; - struct dirent *de; - - if (!(d = opendir("/proc"))) { - printf("Failed to open /proc (%s)\n", strerror(errno)); - return; - } - - if (header) printf("\n------ %s ------\n", header); - while ((de = readdir(d))) { - if (ds.IsUserConsentDenied()) { - MYLOGE( - "Returning early because user denied consent to share bugreport with calling app."); - closedir(d); - return; - } - int pid; - int fd; - char cmdpath[255]; - char cmdline[255]; - - if (!(pid = atoi(de->d_name))) { - continue; - } - - memset(cmdline, 0, sizeof(cmdline)); - - snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/cmdline", pid); - if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { - TEMP_FAILURE_RETRY(read(fd, cmdline, sizeof(cmdline) - 2)); - close(fd); - if (cmdline[0]) { - helper(pid, cmdline, arg); - continue; - } - } - - // if no cmdline, a kernel thread has comm - snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/comm", pid); - if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { - TEMP_FAILURE_RETRY(read(fd, cmdline + 1, sizeof(cmdline) - 4)); - close(fd); - if (cmdline[1]) { - cmdline[0] = '['; - size_t len = strcspn(cmdline, "\f\b\r\n"); - cmdline[len] = ']'; - cmdline[len+1] = '\0'; - } - } - if (!cmdline[0]) { - strcpy(cmdline, "N/A"); - } - helper(pid, cmdline, arg); - } - - closedir(d); -} - -static void for_each_pid_helper(int pid, const char *cmdline, void *arg) { - for_each_pid_func *func = (for_each_pid_func*) arg; - func(pid, cmdline); -} - -void for_each_pid(for_each_pid_func func, const char *header) { - std::string title = header == nullptr ? "for_each_pid" - : android::base::StringPrintf("for_each_pid(%s)", header); - DurationReporter duration_reporter(title); - if (PropertiesHelper::IsDryRun()) return; - - __for_each_pid(for_each_pid_helper, header, (void *) func); -} - -static void for_each_tid_helper(int pid, const char *cmdline, void *arg) { - DIR *d; - struct dirent *de; - char taskpath[255]; - for_each_tid_func *func = (for_each_tid_func *) arg; - - snprintf(taskpath, sizeof(taskpath), "/proc/%d/task", pid); - - if (!(d = opendir(taskpath))) { - printf("Failed to open %s (%s)\n", taskpath, strerror(errno)); - return; - } - - func(pid, pid, cmdline); - - while ((de = readdir(d))) { - if (ds.IsUserConsentDenied()) { - MYLOGE( - "Returning early because user denied consent to share bugreport with calling app."); - closedir(d); - return; - } - int tid; - int fd; - char commpath[255]; - char comm[255]; - - if (!(tid = atoi(de->d_name))) { - continue; - } - - if (tid == pid) - continue; - - snprintf(commpath, sizeof(commpath), "/proc/%d/comm", tid); - memset(comm, 0, sizeof(comm)); - if ((fd = TEMP_FAILURE_RETRY(open(commpath, O_RDONLY | O_CLOEXEC))) < 0) { - strcpy(comm, "N/A"); - } else { - char *c; - TEMP_FAILURE_RETRY(read(fd, comm, sizeof(comm) - 2)); - close(fd); - - c = strrchr(comm, '\n'); - if (c) { - *c = '\0'; - } - } - func(pid, tid, comm); - } - - closedir(d); -} - -void for_each_tid(for_each_tid_func func, const char *header) { - std::string title = header == nullptr ? "for_each_tid" - : android::base::StringPrintf("for_each_tid(%s)", header); - DurationReporter duration_reporter(title); - - if (PropertiesHelper::IsDryRun()) return; - - __for_each_pid(for_each_tid_helper, header, (void *) func); -} - -void show_wchan(int pid, int tid, const char *name) { - if (PropertiesHelper::IsDryRun()) return; - - char path[255]; - char buffer[255]; - int fd, ret, save_errno; - char name_buffer[255]; - - memset(buffer, 0, sizeof(buffer)); - - snprintf(path, sizeof(path), "/proc/%d/wchan", tid); - if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { - printf("Failed to open '%s' (%s)\n", path, strerror(errno)); - return; - } - - ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); - save_errno = errno; - close(fd); - - if (ret < 0) { - printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); - return; - } - - snprintf(name_buffer, sizeof(name_buffer), "%*s%s", - pid == tid ? 0 : 3, "", name); - - printf("%-7d %-32s %s\n", tid, name_buffer, buffer); - - return; -} - -// print time in centiseconds -static void snprcent(char *buffer, size_t len, size_t spc, - unsigned long long time) { - static long hz; // cache discovered hz - - if (hz <= 0) { - hz = sysconf(_SC_CLK_TCK); - if (hz <= 0) { - hz = 1000; - } - } - - // convert to centiseconds - time = (time * 100 + (hz / 2)) / hz; - - char str[16]; - - snprintf(str, sizeof(str), " %llu.%02u", - time / 100, (unsigned)(time % 100)); - size_t offset = strlen(buffer); - snprintf(buffer + offset, (len > offset) ? len - offset : 0, - "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); -} - -// print permille as a percent -static void snprdec(char *buffer, size_t len, size_t spc, unsigned permille) { - char str[16]; - - snprintf(str, sizeof(str), " %u.%u%%", permille / 10, permille % 10); - size_t offset = strlen(buffer); - snprintf(buffer + offset, (len > offset) ? len - offset : 0, - "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); -} - -void show_showtime(int pid, const char *name) { - if (PropertiesHelper::IsDryRun()) return; - - char path[255]; - char buffer[1023]; - int fd, ret, save_errno; - - memset(buffer, 0, sizeof(buffer)); - - snprintf(path, sizeof(path), "/proc/%d/stat", pid); - if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { - printf("Failed to open '%s' (%s)\n", path, strerror(errno)); - return; - } - - ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); - save_errno = errno; - close(fd); - - if (ret < 0) { - printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); - return; - } - - // field 14 is utime - // field 15 is stime - // field 42 is iotime - unsigned long long utime = 0, stime = 0, iotime = 0; - if (sscanf(buffer, - "%*u %*s %*s %*d %*d %*d %*d %*d %*d %*d %*d " - "%*d %*d %llu %llu %*d %*d %*d %*d %*d %*d " - "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " - "%*d %*d %*d %*d %*d %*d %*d %*d %*d %llu ", - &utime, &stime, &iotime) != 3) { - return; - } - - unsigned long long total = utime + stime; - if (!total) { - return; - } - - unsigned permille = (iotime * 1000 + (total / 2)) / total; - if (permille > 1000) { - permille = 1000; - } - - // try to beautify and stabilize columns at <80 characters - snprintf(buffer, sizeof(buffer), "%-6d%s", pid, name); - if ((name[0] != '[') || utime) { - snprcent(buffer, sizeof(buffer), 57, utime); - } - snprcent(buffer, sizeof(buffer), 65, stime); - if ((name[0] != '[') || iotime) { - snprcent(buffer, sizeof(buffer), 73, iotime); - } - if (iotime) { - snprdec(buffer, sizeof(buffer), 79, permille); - } - puts(buffer); // adds a trailing newline - - return; -} - -void do_dmesg() { - const char *title = "KERNEL LOG (dmesg)"; - DurationReporter duration_reporter(title); - printf("------ %s ------\n", title); - - if (PropertiesHelper::IsDryRun()) return; - - /* Get size of kernel buffer */ - int size = klogctl(KLOG_SIZE_BUFFER, nullptr, 0); - if (size <= 0) { - printf("Unexpected klogctl return value: %d\n\n", size); - return; - } - char *buf = (char *) malloc(size + 1); - if (buf == nullptr) { - printf("memory allocation failed\n\n"); - return; - } - int retval = klogctl(KLOG_READ_ALL, buf, size); - if (retval < 0) { - printf("klogctl failure\n\n"); - free(buf); - return; - } - buf[retval] = '\0'; - printf("%s\n\n", buf); - free(buf); - return; -} - -void do_showmap(int pid, const char *name) { - char title[255]; - char arg[255]; - - snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name); - snprintf(arg, sizeof(arg), "%d", pid); - RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT); -} - -int Dumpstate::DumpFile(const std::string& title, const std::string& path) { - DurationReporter duration_reporter(title); - - int status = DumpFileToFd(STDOUT_FILENO, title, path); - - UpdateProgress(WEIGHT_FILE); - - return status; -} - -int read_file_as_long(const char *path, long int *output) { - int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC)); - if (fd < 0) { - int err = errno; - MYLOGE("Error opening file descriptor for %s: %s\n", path, strerror(err)); - return -1; - } - char buffer[50]; - ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); - if (bytes_read == -1) { - MYLOGE("Error reading file %s: %s\n", path, strerror(errno)); - return -2; - } - if (bytes_read == 0) { - MYLOGE("File %s is empty\n", path); - return -3; - } - *output = atoi(buffer); - return 0; -} - -/* calls skip to gate calling dump_from_fd recursively - * in the specified directory. dump_from_fd defaults to - * dump_file_from_fd above when set to NULL. skip defaults - * to false when set to NULL. dump_from_fd will always be - * called with title NULL. - */ -int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path), - int (*dump_from_fd)(const char* title, const char* path, int fd)) { - DurationReporter duration_reporter(title); - DIR *dirp; - struct dirent *d; - char *newpath = nullptr; - const char *slash = "/"; - int retval = 0; - - if (!title.empty()) { - printf("------ %s (%s) ------\n", title.c_str(), dir); - } - if (PropertiesHelper::IsDryRun()) return 0; - - if (dir[strlen(dir) - 1] == '/') { - ++slash; - } - dirp = opendir(dir); - if (dirp == nullptr) { - retval = -errno; - MYLOGE("%s: %s\n", dir, strerror(errno)); - return retval; - } - - if (!dump_from_fd) { - dump_from_fd = dump_file_from_fd; - } - for (; ((d = readdir(dirp))); free(newpath), newpath = nullptr) { - if ((d->d_name[0] == '.') - && (((d->d_name[1] == '.') && (d->d_name[2] == '\0')) - || (d->d_name[1] == '\0'))) { - continue; - } - asprintf(&newpath, "%s%s%s%s", dir, slash, d->d_name, - (d->d_type == DT_DIR) ? "/" : ""); - if (!newpath) { - retval = -errno; - continue; - } - if (skip && (*skip)(newpath)) { - continue; - } - if (d->d_type == DT_DIR) { - int ret = dump_files("", newpath, skip, dump_from_fd); - if (ret < 0) { - retval = ret; - } - continue; - } - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC))); - if (fd.get() < 0) { - retval = -1; - printf("*** %s: %s\n", newpath, strerror(errno)); - continue; - } - (*dump_from_fd)(nullptr, newpath, fd.get()); - } - closedir(dirp); - if (!title.empty()) { - printf("\n"); - } - return retval; -} - -/* fd must have been opened with the flag O_NONBLOCK. With this flag set, - * it's possible to avoid issues where opening the file itself can get - * stuck. - */ -int dump_file_from_fd(const char *title, const char *path, int fd) { - if (PropertiesHelper::IsDryRun()) return 0; - - int flags = fcntl(fd, F_GETFL); - if (flags == -1) { - printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno)); - return -1; - } else if (!(flags & O_NONBLOCK)) { - printf("*** %s: fd must have O_NONBLOCK set.\n", path); - return -1; - } - return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun()); -} - -int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command, - const CommandOptions& options) { - DurationReporter duration_reporter(title); - - int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options); - - /* TODO: for now we're simplifying the progress calculation by using the - * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys, - * where its weight should be much higher proportionally to its timeout. - * Ideally, it should use a options.EstimatedDuration() instead...*/ - UpdateProgress(options.Timeout()); - - return status; -} - -void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args, - const CommandOptions& options, long dumpsysTimeoutMs) { - long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs(); - std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)}; - dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end()); - RunCommand(title, dumpsys, options); -} - -int open_socket(const char *service) { - int s = android_get_control_socket(service); - if (s < 0) { - MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno)); - return -1; - } - fcntl(s, F_SETFD, FD_CLOEXEC); - - // Set backlog to 0 to make sure that queue size will be minimum. - // In Linux, because the minimum queue will be 1, connect() will be blocked - // if the other clients already called connect() and the connection request was not accepted. - if (listen(s, 0) < 0) { - MYLOGE("listen(control socket): %s\n", strerror(errno)); - return -1; - } - - struct sockaddr addr; - socklen_t alen = sizeof(addr); - int fd = accept(s, &addr, &alen); - - // Close socket just after accept(), to make sure that connect() by client will get error - // when the socket is used by the other services. - // There is still a race condition possibility between accept and close, but there is no way - // to close-on-accept atomically. - // See detail; b/123306389#comment25 - close(s); - - if (fd < 0) { - MYLOGE("accept(control socket): %s\n", strerror(errno)); - return -1; - } - - return fd; -} - -/* redirect output to a service control socket */ -bool redirect_to_socket(FILE* redirect, const char* service) { - int fd = open_socket(service); - if (fd == -1) { - return false; - } - fflush(redirect); - // TODO: handle dup2 failure - TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); - close(fd); - return true; -} - -// TODO: should call is_valid_output_file and/or be merged into it. -void create_parent_dirs(const char *path) { - char *chp = const_cast<char *> (path); - - /* skip initial slash */ - if (chp[0] == '/') - chp++; - - /* create leading directories, if necessary */ - struct stat dir_stat; - while (chp && chp[0]) { - chp = strchr(chp, '/'); - if (chp) { - *chp = 0; - if (stat(path, &dir_stat) == -1 || !S_ISDIR(dir_stat.st_mode)) { - MYLOGI("Creating directory %s\n", path); - if (mkdir(path, 0770)) { /* drwxrwx--- */ - MYLOGE("Unable to create directory %s: %s\n", path, strerror(errno)); - } else if (chown(path, AID_SHELL, AID_SHELL)) { - MYLOGE("Unable to change ownership of dir %s: %s\n", path, strerror(errno)); - } - } - *chp++ = '/'; - } - } -} - -bool _redirect_to_file(FILE* redirect, char* path, int truncate_flag) { - create_parent_dirs(path); - - int fd = TEMP_FAILURE_RETRY(open(path, - O_WRONLY | O_CREAT | truncate_flag | O_CLOEXEC | O_NOFOLLOW, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); - if (fd < 0) { - MYLOGE("%s: %s\n", path, strerror(errno)); - return false; - } - - TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); - close(fd); - return true; -} - -bool redirect_to_file(FILE* redirect, char* path) { - return _redirect_to_file(redirect, path, O_TRUNC); -} - -bool redirect_to_existing_file(FILE* redirect, char* path) { - return _redirect_to_file(redirect, path, O_APPEND); -} - -void dump_route_tables() { - DurationReporter duration_reporter("DUMP ROUTE TABLES"); - if (PropertiesHelper::IsDryRun()) return; - const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables"; - ds.DumpFile("RT_TABLES", RT_TABLES_PATH); - FILE* fp = fopen(RT_TABLES_PATH, "re"); - if (!fp) { - printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno)); - return; - } - char table[16]; - // Each line has an integer (the table number), a space, and a string (the table name). We only - // need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name. - // Add a fixed max limit so this doesn't go awry. - for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) { - RunCommand("ROUTE TABLE IPv4", {"ip", "-4", "route", "show", "table", table}); - RunCommand("ROUTE TABLE IPv6", {"ip", "-6", "route", "show", "table", table}); - } - fclose(fp); -} - -// TODO: make this function thread safe if sections are generated in parallel. -void Dumpstate::UpdateProgress(int32_t delta_sec) { - if (progress_ == nullptr) { - MYLOGE("UpdateProgress: progress_ not set\n"); - return; - } - - // Always update progess so stats can be tuned... - bool max_changed = progress_->Inc(delta_sec); - - // ...but only notifiy listeners when necessary. - if (!options_->do_progress_updates) return; - - int progress = progress_->Get(); - int max = progress_->GetMax(); - - // adjusts max on the fly - if (max_changed && listener_ != nullptr) { - listener_->onMaxProgressUpdated(max); - } - - int32_t last_update_delta = progress - last_updated_progress_; - if (last_updated_progress_ > 0 && last_update_delta < update_progress_threshold_) { - return; - } - last_updated_progress_ = progress; - - if (control_socket_fd_ >= 0) { - dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress, max); - fsync(control_socket_fd_); - } - - int percent = 100 * progress / max; - if (listener_ != nullptr) { - if (percent % 5 == 0) { - // We don't want to spam logcat, so only log multiples of 5. - MYLOGD("Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), progress, max, - percent); - } else { - // stderr is ignored on normal invocations, but useful when calling - // /system/bin/dumpstate directly for debuggging. - fprintf(stderr, "Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), - progress, max, percent); - } - // TODO(b/111441001): Remove in favor of onProgress - listener_->onProgressUpdated(progress); - - listener_->onProgress(percent); - } -} - -void Dumpstate::TakeScreenshot(const std::string& path) { - const std::string& real_path = path.empty() ? screenshot_path_ : path; - int status = - RunCommand("", {"/system/bin/screencap", "-p", real_path}, - CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); - if (status == 0) { - MYLOGD("Screenshot saved on %s\n", real_path.c_str()); - } else { - MYLOGE("Failed to take screenshot on %s\n", real_path.c_str()); - } -} - -bool is_dir(const char* pathname) { - struct stat info; - if (stat(pathname, &info) == -1) { - return false; - } - return S_ISDIR(info.st_mode); -} - -time_t get_mtime(int fd, time_t default_mtime) { - struct stat info; - if (fstat(fd, &info) == -1) { - return default_mtime; - } - return info.st_mtime; -} - -void dump_emmc_ecsd(const char *ext_csd_path) { - // List of interesting offsets - struct hex { - char str[2]; - }; - static const size_t EXT_CSD_REV = 192 * sizeof(hex); - static const size_t EXT_PRE_EOL_INFO = 267 * sizeof(hex); - static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_A = 268 * sizeof(hex); - static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_B = 269 * sizeof(hex); - - std::string buffer; - if (!android::base::ReadFileToString(ext_csd_path, &buffer)) { - return; - } - - printf("------ %s Extended CSD ------\n", ext_csd_path); - - if (buffer.length() < (EXT_CSD_REV + sizeof(hex))) { - printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length()); - return; - } - - int ext_csd_rev = 0; - std::string sub = buffer.substr(EXT_CSD_REV, sizeof(hex)); - if (sscanf(sub.c_str(), "%2x", &ext_csd_rev) != 1) { - printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n", ext_csd_path, sub.c_str()); - return; - } - - static const char *ver_str[] = { - "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0" - }; - printf("rev 1.%d (MMC %s)\n", ext_csd_rev, - (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? ver_str[ext_csd_rev] - : "Unknown"); - if (ext_csd_rev < 7) { - printf("\n"); - return; - } - - if (buffer.length() < (EXT_PRE_EOL_INFO + sizeof(hex))) { - printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length()); - return; - } - - int ext_pre_eol_info = 0; - sub = buffer.substr(EXT_PRE_EOL_INFO, sizeof(hex)); - if (sscanf(sub.c_str(), "%2x", &ext_pre_eol_info) != 1) { - printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n", ext_csd_path, sub.c_str()); - return; - } - - static const char *eol_str[] = { - "Undefined", - "Normal", - "Warning (consumed 80% of reserve)", - "Urgent (consumed 90% of reserve)" - }; - printf( - "PRE_EOL_INFO %d (MMC %s)\n", ext_pre_eol_info, - eol_str[(ext_pre_eol_info < (int)(sizeof(eol_str) / sizeof(eol_str[0]))) ? ext_pre_eol_info - : 0]); - - for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A; - lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B; - lifetime += sizeof(hex)) { - int ext_device_life_time_est; - static const char *est_str[] = { - "Undefined", - "0-10% of device lifetime used", - "10-20% of device lifetime used", - "20-30% of device lifetime used", - "30-40% of device lifetime used", - "40-50% of device lifetime used", - "50-60% of device lifetime used", - "60-70% of device lifetime used", - "70-80% of device lifetime used", - "80-90% of device lifetime used", - "90-100% of device lifetime used", - "Exceeded the maximum estimated device lifetime", - }; - - if (buffer.length() < (lifetime + sizeof(hex))) { - printf("*** %s: truncated content %zu\n", ext_csd_path, buffer.length()); - break; - } - - ext_device_life_time_est = 0; - sub = buffer.substr(lifetime, sizeof(hex)); - if (sscanf(sub.c_str(), "%2x", &ext_device_life_time_est) != 1) { - printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n", ext_csd_path, - (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A', - sub.c_str()); - continue; - } - printf("DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n", - (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A', - ext_device_life_time_est, - est_str[(ext_device_life_time_est < (int)(sizeof(est_str) / sizeof(est_str[0]))) - ? ext_device_life_time_est - : 0]); - } - - printf("\n"); -} diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 73780eccbf..0212bc5564 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -275,7 +275,7 @@ protected: writer.StartEntry("primary.prof", ZipWriter::kCompress); writer.FinishEntry(); writer.Finish(); - close(fd); + fclose(file); } // Create the app user data. diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp index 9513ec1c23..a5b1ac5c5f 100644 --- a/cmds/service/Android.bp +++ b/cmds/service/Android.bp @@ -4,6 +4,7 @@ cc_binary { srcs: ["service.cpp"], shared_libs: [ + "libcutils", "libutils", "libbinder", ], @@ -22,6 +23,7 @@ cc_binary { srcs: ["service.cpp"], shared_libs: [ + "libcutils", "libutils", "libbinder", ], diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index 8a33dd227a..18b6b58a9e 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp @@ -18,13 +18,18 @@ #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <binder/TextOutput.h> +#include <cutils/ashmem.h> #include <getopt.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> +#include <sys/mman.h> #include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> using namespace android; @@ -187,6 +192,57 @@ int main(int argc, char* const argv[]) } else if (strcmp(argv[optind], "null") == 0) { optind++; data.writeStrongBinder(nullptr); + } else if (strcmp(argv[optind], "fd") == 0) { + optind++; + if (optind >= argc) { + aerr << "service: no path supplied for 'fd'" << endl; + wantsUsage = true; + result = 10; + break; + } + const char *path = argv[optind++]; + int fd = open(path, O_RDONLY); + if (fd < 0) { + aerr << "service: could not open '" << path << "'" << endl; + wantsUsage = true; + result = 10; + break; + } + data.writeFileDescriptor(fd, true /* take ownership */); + } else if (strcmp(argv[optind], "afd") == 0) { + optind++; + if (optind >= argc) { + aerr << "service: no path supplied for 'afd'" << endl; + wantsUsage = true; + result = 10; + break; + } + const char *path = argv[optind++]; + 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; + wantsUsage = true; + result = 10; + break; + } + 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); + 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; + wantsUsage = true; + result = 10; + break; + } + data.writeFileDescriptor( + atoi(argv[optind++]), true /* take ownership */); + } else if (strcmp(argv[optind], "intent") == 0) { char* action = nullptr; @@ -232,7 +288,6 @@ int main(int argc, char* const argv[]) else if (strcmp(key, "categories") == 0) { char* context2 = nullptr; - int categoryCount = 0; categories[categoryCount] = strtok_r(value, ",", &context2); while (categories[categoryCount] != nullptr) @@ -301,13 +356,19 @@ int main(int argc, char* const argv[]) 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 ] ...\n" + " service 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" - " s16: Write the UTF-16 string STR 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" // " action=STR data=STR type=STR launchFlags=INT component=STR categories=STR[,STR,...]\n"; return result; diff --git a/data/etc/android.hardware.se.omapi.ese.xml b/data/etc/android.hardware.se.omapi.ese.xml new file mode 100644 index 0000000000..3b1d81cd87 --- /dev/null +++ b/data/etc/android.hardware.se.omapi.ese.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This feature indicates that the device supports The device supports + Open Mobile API capable ESE-based secure elements--> +<permissions> + <feature name="android.hardware.se.omapi.ese" /> +</permissions> diff --git a/data/etc/android.hardware.se.omapi.sd.xml b/data/etc/android.hardware.se.omapi.sd.xml new file mode 100644 index 0000000000..8fc2869d23 --- /dev/null +++ b/data/etc/android.hardware.se.omapi.sd.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This feature indicates that the device supports The device supports + Open Mobile API capable SD-based secure elements--> +<permissions> + <feature name="android.hardware.se.omapi.sd" /> +</permissions> diff --git a/data/etc/android.hardware.se.omapi.uicc.xml b/data/etc/android.hardware.se.omapi.uicc.xml new file mode 100644 index 0000000000..9c6f143990 --- /dev/null +++ b/data/etc/android.hardware.se.omapi.uicc.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This feature indicates that the device supports The device supports + Open Mobile API capable UICC-based secure elements--> +<permissions> + <feature name="android.hardware.se.omapi.uicc" /> +</permissions> diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index c9a5af32ea..4222e36dee 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -70,6 +70,7 @@ cc_library_shared { "ProcessInfoService.cpp", "ProcessState.cpp", "Static.cpp", + "Stability.cpp", "Status.cpp", "TextOutput.cpp", "IpPrefix.cpp", diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 967ffd59ef..9e55c2c8c3 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -17,15 +17,12 @@ #include <binder/Binder.h> #include <atomic> +#include <utils/misc.h> #include <binder/BpBinder.h> #include <binder/IInterface.h> -#include <binder/IPCThreadState.h> #include <binder/IResultReceiver.h> #include <binder/IShellCallback.h> #include <binder/Parcel.h> -#include <cutils/android_filesystem_config.h> -#include <cutils/compiler.h> -#include <utils/misc.h> #include <stdio.h> @@ -128,19 +125,6 @@ status_t BBinder::transact( { data.setDataPosition(0); - // Shell command transaction is conventionally implemented by - // overriding onTransact by copy/pasting the parceling code from - // this file. So, we must check permissions for it before we call - // onTransact. This check is here because shell APIs aren't - // guaranteed to be stable, and so they should only be used by - // developers. - if (CC_UNLIKELY(code == SHELL_COMMAND_TRANSACTION)) { - uid_t uid = IPCThreadState::self()->getCallingUid(); - if (uid != AID_SHELL && uid != AID_ROOT) { - return PERMISSION_DENIED; - } - } - status_t err = NO_ERROR; switch (code) { case PING_TRANSACTION: @@ -151,6 +135,7 @@ status_t BBinder::transact( break; } + // In case this is being transacted on in the same process. if (reply != nullptr) { reply->setDataPosition(0); } diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 5ceb218b8b..425ece384b 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -21,6 +21,7 @@ #include <binder/IPCThreadState.h> #include <binder/IResultReceiver.h> +#include <binder/Stability.h> #include <cutils/compiler.h> #include <utils/Log.h> @@ -213,9 +214,22 @@ status_t BpBinder::transact( { // Once a binder has died, it will never come back to life. if (mAlive) { + // user transactions require a given stability level + // Cannot add requirement w/o SM update + // if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) { + // using android::internal::Stability; + + // auto stability = Stability::get(this); + + // if (CC_UNLIKELY(!Stability::check(stability, Stability::kLocalStability))) { + // return BAD_TYPE; + // } + // } + status_t status = IPCThreadState::self()->transact( mHandle, code, data, reply, flags); if (status == DEAD_OBJECT) mAlive = 0; + return status; } diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 3b889fbe20..4356706b7e 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -421,7 +421,7 @@ void IPCThreadState::clearCaller() void IPCThreadState::flushCommands() { - if (mProcess->mDriverFD <= 0) + if (mProcess->mDriverFD < 0) return; talkWithDriver(false); // The flush could have caused post-write refcount decrements to have @@ -574,7 +574,7 @@ void IPCThreadState::joinThreadPool(bool isMain) int IPCThreadState::setupPolling(int* fd) { - if (mProcess->mDriverFD <= 0) { + if (mProcess->mDriverFD < 0) { return -EBADF; } @@ -878,7 +878,7 @@ finish: status_t IPCThreadState::talkWithDriver(bool doReceive) { - if (mProcess->mDriverFD <= 0) { + if (mProcess->mDriverFD < 0) { return -EBADF; } @@ -936,7 +936,7 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) #else err = INVALID_OPERATION; #endif - if (mProcess->mDriverFD <= 0) { + if (mProcess->mDriverFD < 0) { err = -EBADF; } IF_LOG_COMMANDS() { @@ -1246,7 +1246,7 @@ void IPCThreadState::threadDestructor(void *st) if (self) { self->flushCommands(); #if defined(__ANDROID__) - if (self->mProcess->mDriverFD > 0) { + if (self->mProcess->mDriverFD >= 0) { ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); } #endif diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index a1ddec8920..c75f0365f5 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -35,6 +35,7 @@ #include <binder/IPCThreadState.h> #include <binder/Parcel.h> #include <binder/ProcessState.h> +#include <binder/Stability.h> #include <binder/Status.h> #include <binder/TextOutput.h> @@ -102,10 +103,6 @@ static void acquire_object(const sp<ProcessState>& proc, reinterpret_cast<IBinder*>(obj.cookie)->incStrong(who); } return; - case BINDER_TYPE_WEAK_BINDER: - if (obj.binder) - reinterpret_cast<RefBase::weakref_type*>(obj.binder)->incWeak(who); - return; case BINDER_TYPE_HANDLE: { const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle); if (b != nullptr) { @@ -114,11 +111,6 @@ static void acquire_object(const sp<ProcessState>& proc, } return; } - case BINDER_TYPE_WEAK_HANDLE: { - const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle); - if (b != nullptr) b.get_refs()->incWeak(who); - return; - } case BINDER_TYPE_FD: { if ((obj.cookie != 0) && (outAshmemSize != nullptr) && ashmem_valid(obj.handle)) { // If we own an ashmem fd, keep track of how much memory it refers to. @@ -144,10 +136,6 @@ static void release_object(const sp<ProcessState>& proc, reinterpret_cast<IBinder*>(obj.cookie)->decStrong(who); } return; - case BINDER_TYPE_WEAK_BINDER: - if (obj.binder) - reinterpret_cast<RefBase::weakref_type*>(obj.binder)->decWeak(who); - return; case BINDER_TYPE_HANDLE: { const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle); if (b != nullptr) { @@ -156,11 +144,6 @@ static void release_object(const sp<ProcessState>& proc, } return; } - case BINDER_TYPE_WEAK_HANDLE: { - const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle); - if (b != nullptr) b.get_refs()->decWeak(who); - return; - } case BINDER_TYPE_FD: { if (obj.cookie != 0) { // owned if ((outAshmemSize != nullptr) && ashmem_valid(obj.handle)) { @@ -182,14 +165,34 @@ static void release_object(const sp<ProcessState>& proc, ALOGE("Invalid object type 0x%08x", obj.hdr.type); } -inline static status_t finish_flatten_binder( - const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out) +status_t Parcel::finishFlattenBinder( + const sp<IBinder>& /*binder*/, const flat_binder_object& flat) { - return out->writeObject(flat, false); + status_t status = writeObject(flat, false); + if (status != OK) return status; + + // internal::Stability::tryMarkCompilationUnit(binder.get()); + // Cannot change wire protocol w/o SM update + // return writeInt32(internal::Stability::get(binder.get())); + return OK; } -static status_t flatten_binder(const sp<ProcessState>& /*proc*/, - const sp<IBinder>& binder, Parcel* out) +status_t Parcel::finishUnflattenBinder( + const sp<IBinder>& binder, sp<IBinder>* out) const +{ + // int32_t stability; + // Cannot change wire protocol w/o SM update + // status_t status = readInt32(&stability); + // if (status != OK) return status; + + // status = internal::Stability::set(binder.get(), stability, true /*log*/); + // if (status != OK) return status; + + *out = binder; + return OK; +} + +status_t Parcel::flattenBinder(const sp<IBinder>& binder) { flat_binder_object obj; @@ -227,108 +230,24 @@ static status_t flatten_binder(const sp<ProcessState>& /*proc*/, obj.cookie = 0; } - return finish_flatten_binder(binder, obj, out); -} - -static status_t flatten_binder(const sp<ProcessState>& /*proc*/, - const wp<IBinder>& binder, Parcel* out) -{ - flat_binder_object obj; - - obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - if (binder != nullptr) { - sp<IBinder> real = binder.promote(); - if (real != nullptr) { - IBinder *local = real->localBinder(); - if (!local) { - BpBinder *proxy = real->remoteBinder(); - if (proxy == nullptr) { - ALOGE("null proxy"); - } - const int32_t handle = proxy ? proxy->handle() : 0; - obj.hdr.type = BINDER_TYPE_WEAK_HANDLE; - obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ - obj.handle = handle; - obj.cookie = 0; - } else { - obj.hdr.type = BINDER_TYPE_WEAK_BINDER; - obj.binder = reinterpret_cast<uintptr_t>(binder.get_refs()); - obj.cookie = reinterpret_cast<uintptr_t>(binder.unsafe_get()); - } - return finish_flatten_binder(real, obj, out); - } - - // XXX How to deal? In order to flatten the given binder, - // we need to probe it for information, which requires a primary - // reference... but we don't have one. - // - // The OpenBinder implementation uses a dynamic_cast<> here, - // but we can't do that with the different reference counting - // implementation we are using. - ALOGE("Unable to unflatten Binder weak reference!"); - obj.hdr.type = BINDER_TYPE_BINDER; - obj.binder = 0; - obj.cookie = 0; - return finish_flatten_binder(nullptr, obj, out); - - } else { - obj.hdr.type = BINDER_TYPE_BINDER; - obj.binder = 0; - obj.cookie = 0; - return finish_flatten_binder(nullptr, obj, out); - } -} - -inline static status_t finish_unflatten_binder( - BpBinder* /*proxy*/, const flat_binder_object& /*flat*/, - const Parcel& /*in*/) -{ - return NO_ERROR; + return finishFlattenBinder(binder, obj); } -static status_t unflatten_binder(const sp<ProcessState>& proc, - const Parcel& in, sp<IBinder>* out) +status_t Parcel::unflattenBinder(sp<IBinder>* out) const { - const flat_binder_object* flat = in.readObject(false); + const flat_binder_object* flat = readObject(false); if (flat) { switch (flat->hdr.type) { - case BINDER_TYPE_BINDER: - *out = reinterpret_cast<IBinder*>(flat->cookie); - return finish_unflatten_binder(nullptr, *flat, in); - case BINDER_TYPE_HANDLE: - *out = proc->getStrongProxyForHandle(flat->handle); - return finish_unflatten_binder( - static_cast<BpBinder*>(out->get()), *flat, in); - } - } - return BAD_TYPE; -} - -static status_t unflatten_binder(const sp<ProcessState>& proc, - const Parcel& in, wp<IBinder>* out) -{ - const flat_binder_object* flat = in.readObject(false); - - if (flat) { - switch (flat->hdr.type) { - case BINDER_TYPE_BINDER: - *out = reinterpret_cast<IBinder*>(flat->cookie); - return finish_unflatten_binder(nullptr, *flat, in); - case BINDER_TYPE_WEAK_BINDER: - if (flat->binder != 0) { - out->set_object_and_refs( - reinterpret_cast<IBinder*>(flat->cookie), - reinterpret_cast<RefBase::weakref_type*>(flat->binder)); - } else { - *out = nullptr; - } - return finish_unflatten_binder(nullptr, *flat, in); - case BINDER_TYPE_HANDLE: - case BINDER_TYPE_WEAK_HANDLE: - *out = proc->getWeakProxyForHandle(flat->handle); - return finish_unflatten_binder( - static_cast<BpBinder*>(out->unsafe_get()), *flat, in); + case BINDER_TYPE_BINDER: { + sp<IBinder> binder = reinterpret_cast<IBinder*>(flat->cookie); + return finishUnflattenBinder(binder, out); + } + case BINDER_TYPE_HANDLE: { + sp<IBinder> binder = + ProcessState::self()->getStrongProxyForHandle(flat->handle); + return finishUnflattenBinder(binder, out); + } } } return BAD_TYPE; @@ -1061,7 +980,7 @@ status_t Parcel::writeString16(const char16_t* str, size_t len) status_t Parcel::writeStrongBinder(const sp<IBinder>& val) { - return flatten_binder(ProcessState::self(), val, this); + return flattenBinder(val); } status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) @@ -1082,11 +1001,6 @@ status_t Parcel::readStrongBinderVector(std::vector<sp<IBinder>>* val) const { return readTypedVector(val, &Parcel::readStrongBinder); } -status_t Parcel::writeWeakBinder(const wp<IBinder>& val) -{ - return flatten_binder(ProcessState::self(), val, this); -} - status_t Parcel::writeRawNullableParcelable(const Parcelable* parcelable) { if (!parcelable) { return writeInt32(0); @@ -2004,7 +1918,7 @@ status_t Parcel::readStrongBinder(sp<IBinder>* val) const status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const { - return unflatten_binder(ProcessState::self(), *this, val); + return unflattenBinder(val); } sp<IBinder> Parcel::readStrongBinder() const @@ -2017,13 +1931,6 @@ sp<IBinder> Parcel::readStrongBinder() const return val; } -wp<IBinder> Parcel::readWeakBinder() const -{ - wp<IBinder> val; - unflatten_binder(ProcessState::self(), *this, &val); - return val; -} - status_t Parcel::readParcelable(Parcelable* parcelable) const { int32_t have_parcelable = 0; status_t status = readInt32(&have_parcelable); diff --git a/libs/binder/include/private/binder/ParcelValTypes.h b/libs/binder/ParcelValTypes.h index 666d22a57f..666d22a57f 100644 --- a/libs/binder/include/private/binder/ParcelValTypes.h +++ b/libs/binder/ParcelValTypes.h diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp index c0aec0a979..97a6c94635 100644 --- a/libs/binder/PersistableBundle.cpp +++ b/libs/binder/PersistableBundle.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "PersistableBundle" #include <binder/PersistableBundle.h> -#include <private/binder/ParcelValTypes.h> #include <limits> @@ -26,6 +25,8 @@ #include <log/log.h> #include <utils/Errors.h> +#include "ParcelValTypes.h" + using android::BAD_TYPE; using android::BAD_VALUE; using android::NO_ERROR; diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index a07b3a043b..07db50f7b3 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -21,6 +21,7 @@ #include <binder/BpBinder.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <binder/Stability.h> #include <cutils/atomic.h> #include <utils/Log.h> #include <utils/String8.h> @@ -109,7 +110,13 @@ sp<ProcessState> ProcessState::selfOrNull() sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/) { - return getStrongProxyForHandle(0); + sp<IBinder> context = getStrongProxyForHandle(0); + + // The root object is special since we get it directly from the driver, it is never + // written by Parcell::writeStrongBinder. + internal::Stability::tryMarkCompilationUnit(context.get()); + + return context; } void ProcessState::startThreadPool() @@ -210,8 +217,12 @@ sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) if (e != nullptr) { // We need to create a new BpBinder if there isn't currently one, OR we - // are unable to acquire a weak reference on this current one. See comment - // in getWeakProxyForHandle() for more info about this. + // are unable to acquire a weak reference on this current one. The + // attemptIncWeak() is safe because we know the BpBinder destructor will always + // call expungeHandle(), which acquires the same lock we are holding now. + // We need to do this because there is a race condition between someone + // releasing a reference on this BpBinder, and a new reference on its handle + // arriving from the driver. IBinder* b = e->binder; if (b == nullptr || !e->refs->attemptIncWeak(this)) { if (handle == 0) { @@ -257,37 +268,6 @@ sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) return result; } -wp<IBinder> ProcessState::getWeakProxyForHandle(int32_t handle) -{ - wp<IBinder> result; - - AutoMutex _l(mLock); - - handle_entry* e = lookupHandleLocked(handle); - - if (e != nullptr) { - // We need to create a new BpBinder if there isn't currently one, OR we - // are unable to acquire a weak reference on this current one. The - // attemptIncWeak() is safe because we know the BpBinder destructor will always - // call expungeHandle(), which acquires the same lock we are holding now. - // We need to do this because there is a race condition between someone - // releasing a reference on this BpBinder, and a new reference on its handle - // arriving from the driver. - IBinder* b = e->binder; - if (b == nullptr || !e->refs->attemptIncWeak(this)) { - b = BpBinder::create(handle); - result = b; - e->binder = b; - if (b) e->refs = b->getWeakRefs(); - } else { - result = b; - e->refs->decWeak(this); - } - } - - return result; -} - void ProcessState::expungeHandle(int32_t handle, IBinder* binder) { AutoMutex _l(mLock); diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp new file mode 100644 index 0000000000..b6f10c8759 --- /dev/null +++ b/libs/binder/Stability.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <binder/Stability.h> + +namespace android { +namespace internal { + +void Stability::markCompilationUnit(IBinder* binder) { + status_t result = set(binder, kLocalStability, true /*log*/); + LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); +} + +void Stability::markVintf(IBinder* binder) { + status_t result = set(binder, Level::VINTF, true /*log*/); + LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); +} + +void Stability::debugLogStability(const std::string& tag, const sp<IBinder>& binder) { + ALOGE("%s: stability is %s", tag.c_str(), stabilityString(get(binder.get())).c_str()); +} + +void Stability::markVndk(IBinder* binder) { + status_t result = set(binder, Level::VENDOR, true /*log*/); + LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); +} + +void Stability::tryMarkCompilationUnit(IBinder* binder) { + (void) set(binder, kLocalStability, false /*log*/); +} + +status_t Stability::set(IBinder* binder, int32_t stability, bool log) { + Level currentStability = get(binder); + + // null binder is always written w/ 'UNDECLARED' stability + if (binder == nullptr) { + if (stability == UNDECLARED) { + return OK; + } else { + if (log) { + ALOGE("Null binder written with stability %s.", + stabilityString(stability).c_str()); + } + return BAD_TYPE; + } + } + + if (!isDeclaredStability(stability)) { + if (log) { + ALOGE("Can only set known stability, not %d.", stability); + } + return BAD_TYPE; + } + + if (currentStability != Level::UNDECLARED && currentStability != stability) { + if (log) { + ALOGE("Interface being set with %s but it is already marked as %s.", + stabilityString(stability).c_str(), stabilityString(currentStability).c_str()); + } + return BAD_TYPE; + } + + if (currentStability == stability) return OK; + + binder->attachObject( + reinterpret_cast<void*>(&Stability::get), + reinterpret_cast<void*>(stability), + nullptr /*cleanupCookie*/, + nullptr /*cleanup function*/); + + return OK; +} + +Stability::Level Stability::get(IBinder* binder) { + if (binder == nullptr) return UNDECLARED; + + return static_cast<Level>(reinterpret_cast<intptr_t>( + binder->findObject(reinterpret_cast<void*>(&Stability::get)))); +} + +bool Stability::check(int32_t provided, Level required) { + bool stable = (provided & required) == required; + + if (!isDeclaredStability(provided) && provided != UNDECLARED) { + ALOGE("Unknown stability when checking interface stability %d.", provided); + + stable = false; + } + + if (!stable) { + ALOGE("Cannot do a user transaction on a %s binder in a %s context.", + stabilityString(provided).c_str(), + stabilityString(required).c_str()); + } + + return stable; +} + +bool Stability::isDeclaredStability(int32_t stability) { + return stability == VENDOR || stability == SYSTEM || stability == VINTF; +} + +std::string Stability::stabilityString(int32_t stability) { + switch (stability) { + case Level::UNDECLARED: return "undeclared stability"; + case Level::VENDOR: return "vendor stability"; + case Level::SYSTEM: return "system stability"; + case Level::VINTF: return "vintf stability"; + } + return "unknown stability " + std::to_string(stability); +} + +} // namespace internal +} // namespace stability
\ No newline at end of file diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h index cf3ef84caa..dec75f5a64 100644 --- a/libs/binder/include/binder/Binder.h +++ b/libs/binder/include/binder/Binder.h @@ -38,7 +38,7 @@ public: virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0); + uint32_t flags = 0) final; // NOLINTNEXTLINE(google-default-arguments) virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, @@ -54,9 +54,9 @@ public: virtual void attachObject( const void* objectID, void* object, void* cleanupCookie, - object_cleanup_func func); - virtual void* findObject(const void* objectID) const; - virtual void detachObject(const void* objectID); + object_cleanup_func func) final; + virtual void* findObject(const void* objectID) const final; + virtual void detachObject(const void* objectID) final; virtual BBinder* localBinder(); diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index b3a1d0b7e9..28599f4fc2 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -45,7 +45,7 @@ public: virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0); + uint32_t flags = 0) final; // NOLINTNEXTLINE(google-default-arguments) virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, @@ -61,9 +61,9 @@ public: virtual void attachObject( const void* objectID, void* object, void* cleanupCookie, - object_cleanup_func func); - virtual void* findObject(const void* objectID) const; - virtual void detachObject(const void* objectID); + object_cleanup_func func) final; + virtual void* findObject(const void* objectID) const final; + virtual void detachObject(const void* objectID) final; virtual BpBinder* remoteBinder(); diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 117b90a03d..c8f82a3e09 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -67,7 +67,7 @@ public: status_t setDataSize(size_t size); void setDataPosition(size_t pos) const; status_t setDataCapacity(size_t size); - + status_t setData(const uint8_t* buffer, size_t len); status_t appendFrom(const Parcel *parcel, @@ -117,7 +117,6 @@ public: status_t writeString16(const std::unique_ptr<String16>& str); status_t writeString16(const char16_t* str, size_t len); status_t writeStrongBinder(const sp<IBinder>& val); - status_t writeWeakBinder(const wp<IBinder>& val); status_t writeInt32Array(size_t len, const int32_t *val); status_t writeByteArray(size_t len, const uint8_t *val); status_t writeBool(bool val); @@ -271,7 +270,6 @@ public: sp<IBinder> readStrongBinder() const; status_t readStrongBinder(sp<IBinder>* val) const; status_t readNullableStrongBinder(sp<IBinder>* val) const; - wp<IBinder> readWeakBinder() const; template<typename T> status_t readParcelableVector( @@ -410,7 +408,13 @@ private: void initState(); void scanForFds() const; status_t validateReadData(size_t len) const; - + + status_t finishFlattenBinder(const sp<IBinder>& binder, + const flat_binder_object& flat); + status_t finishUnflattenBinder(const sp<IBinder>& binder, sp<IBinder>* out) const; + status_t flattenBinder(const sp<IBinder>& binder); + status_t unflattenBinder(sp<IBinder>* out) const; + template<class T> status_t readAligned(T *pArg) const; diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index 3af9eed9c6..8339976567 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -58,7 +58,6 @@ public: void* userData); sp<IBinder> getStrongProxyForHandle(int32_t handle); - wp<IBinder> getWeakProxyForHandle(int32_t handle); void expungeHandle(int32_t handle, IBinder* binder); void spawnPooledThread(bool isMain); diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h new file mode 100644 index 0000000000..f8240e4c72 --- /dev/null +++ b/libs/binder/include/binder/Stability.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IBinder.h> +#include <string> + +namespace android { + +class BpBinder; +class ProcessState; + +namespace internal { + +// WARNING: These APIs are only ever expected to be called by auto-generated code. +// Instead of calling them, you should set the stability of a .aidl interface +class Stability final { +public: + // WARNING: This is only ever expected to be called by auto-generated code. You likely want to + // change or modify the stability class of the interface you are using. + // This must be called as soon as the binder in question is constructed. No thread safety + // is provided. + // E.g. stability is according to libbinder compilation unit + static void markCompilationUnit(IBinder* binder); + // WARNING: This is only ever expected to be called by auto-generated code. You likely want to + // change or modify the stability class of the interface you are using. + // This must be called as soon as the binder in question is constructed. No thread safety + // is provided. + // E.g. stability is according to libbinder_ndk or Java SDK AND the interface + // expressed here is guaranteed to be stable for multiple years (Stable AIDL) + static void markVintf(IBinder* binder); + + // WARNING: for debugging only + static void debugLogStability(const std::string& tag, const sp<IBinder>& binder); + + // WARNING: This is only ever expected to be called by auto-generated code or tests. + // You likely want to change or modify the stability of the interface you are using. + // This must be called as soon as the binder in question is constructed. No thread safety + // is provided. + // E.g. stability is according to libbinder_ndk or Java SDK AND the interface + // expressed here is guaranteed to be stable for multiple years (Stable AIDL) + // If this is called when __ANDROID_VNDK__ is not defined, then it is UB and will likely + // break the device during GSI or other tests. + static void markVndk(IBinder* binder); + +private: + // Parcel needs to read/write stability level in an unstable format. + friend ::android::Parcel; + + // only expose internal APIs inside of libbinder, for checking stability + friend ::android::BpBinder; + + // so that it can mark the context object (only the root object doesn't go + // through Parcel) + friend ::android::ProcessState; + + static void tryMarkCompilationUnit(IBinder* binder); + + enum Level : int32_t { + UNDECLARED = 0, + + VENDOR = 0b000011, + SYSTEM = 0b001100, + VINTF = 0b111111, + }; + +#ifdef __ANDROID_VNDK__ + static constexpr Level kLocalStability = Level::VENDOR; +#else + static constexpr Level kLocalStability = Level::SYSTEM; +#endif + + // applies stability to binder if stability level is known + __attribute__((warn_unused_result)) + static status_t set(IBinder* binder, int32_t stability, bool log); + + static Level get(IBinder* binder); + + static bool check(int32_t provided, Level required); + + static bool isDeclaredStability(int32_t stability); + static std::string stabilityString(int32_t stability); + + Stability(); +}; + +} // namespace internal +} // namespace android diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 21bef2e930..734a9287e1 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -14,13 +14,12 @@ * limitations under the License. */ -cc_library { +cc_library_shared { name: "libbinder_ndk", - vendor_available: true, export_include_dirs: [ "include_ndk", - "include_apex", + "include_platform", ], cflags: [ @@ -34,6 +33,7 @@ cc_library { "ibinder_jni.cpp", "parcel.cpp", "process.cpp", + "stability.cpp", "status.cpp", "service_manager.cpp", ], @@ -55,7 +55,7 @@ cc_library { version_script: "libbinder_ndk.map.txt", stubs: { symbol_file: "libbinder_ndk.map.txt", - versions: ["29"], + versions: ["29", "30"], }, } @@ -74,3 +74,12 @@ ndk_library { symbol_file: "libbinder_ndk.map.txt", first_version: "29", } + +llndk_library { + name: "libbinder_ndk", + symbol_file: "libbinder_ndk.map.txt", + export_include_dirs: [ + "include_ndk", + "include_platform", + ], +} diff --git a/libs/binder/ndk/include_apex/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 055c79bca1..055c79bca1 100644 --- a/libs/binder/ndk/include_apex/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h diff --git a/libs/binder/ndk/include_apex/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h index 69e6387bcb..fdefbb4b8a 100644 --- a/libs/binder/ndk/include_apex/android/binder_process.h +++ b/libs/binder/ndk/include_platform/android/binder_process.h @@ -27,7 +27,7 @@ __BEGIN_DECLS void ABinderProcess_startThreadPool(); /** * This sets the maximum number of threads that can be started in the threadpool. By default, after - * startThreadPool is called, this is one. If it is called additional times, it will only prevent + * startThreadPool is called, this is 15. If it is called additional times, it will only prevent * the kernel from starting new threads and will not delete already existing threads. */ bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads); diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h new file mode 100644 index 0000000000..e6aeb04e6f --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_ibinder.h> + +__BEGIN_DECLS + +#ifdef __ANDROID_VNDK__ + +/** + * This interface has the stability of the vendor image. + */ +void AIBinder_markVendorStability(AIBinder* binder); + +static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) { + AIBinder_markVendorStability(binder); +} + +#else // ndef defined __ANDROID_VNDK__ + +/** + * This interface has the stability of the system image. + */ +void AIBinder_markSystemStability(AIBinder* binder); + +static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) { + AIBinder_markSystemStability(binder); +} + +#endif // ifdef __ANDROID_VNDK__ + +/** + * This interface has system<->vendor stability + */ +void AIBinder_markVintfStability(AIBinder* binder); + +__END_DECLS diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 7e6581736f..feedde6b86 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -89,12 +89,21 @@ LIBBINDER_NDK { # introduced=29 AStatus_getStatus; AStatus_isOk; AStatus_newOk; - ABinderProcess_joinThreadPool; # apex - ABinderProcess_setThreadPoolMaxThreadCount; # apex - ABinderProcess_startThreadPool; # apex - AServiceManager_addService; # apex - AServiceManager_checkService; # apex - AServiceManager_getService; # apex + ABinderProcess_joinThreadPool; # apex vndk + ABinderProcess_setThreadPoolMaxThreadCount; # apex vndk + ABinderProcess_startThreadPool; # apex vndk + AServiceManager_addService; # apex vndk + AServiceManager_checkService; # apex vndk + AServiceManager_getService; # apex vndk + local: + *; +}; + +LIBBINDER_NDK30 { # introduced=30 + global: + AIBinder_markSystemStability; # apex + AIBinder_markVendorStability; # vndk + AIBinder_markVintfStability; # apex vndk local: *; }; diff --git a/libs/binder/ndk/scripts/init_map.sh b/libs/binder/ndk/scripts/init_map.sh deleted file mode 100755 index 3529b725ce..0000000000 --- a/libs/binder/ndk/scripts/init_map.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# Simple helper for ease of development until this API is frozen. - -echo "LIBBINDER_NDK { # introduced=29" -echo " global:" -{ - grep -oP "AIBinder_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_ibinder.h; - grep -oP "AIBinder_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_ibinder_jni.h; - grep -oP "AParcel_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_parcel.h; - grep -oP "AStatus_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_status.h; -} | sort | uniq | awk '{ print " " $0 ";"; }' -{ - grep -oP "AServiceManager_[a-zA-Z0-9_]+(?=\()" include_apex/android/binder_manager.h; - grep -oP "ABinderProcess_[a-zA-Z0-9_]+(?=\()" include_apex/android/binder_process.h; -} | sort | uniq | awk '{ print " " $0 "; # apex"; }' -echo " local:" -echo " *;" -echo "};" diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp new file mode 100644 index 0000000000..a5b3ecea4a --- /dev/null +++ b/libs/binder/ndk/stability.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_stability.h> + +#include <binder/Stability.h> +#include "ibinder_internal.h" + +#include <log/log.h> + +using ::android::internal::Stability; + +#ifdef __ANDROID_VNDK__ +#error libbinder_ndk should only be built in a system context +#endif + +#ifdef __ANDROID_NDK__ +#error libbinder_ndk should only be built in a system context +#endif + +// explicit extern because symbol is only declared in header when __ANDROID_VNDK__ +extern "C" void AIBinder_markVendorStability(AIBinder* binder) { + Stability::markVndk(binder->getBinder().get()); +} + +void AIBinder_markSystemStability(AIBinder* binder) { + Stability::markCompilationUnit(binder->getBinder().get()); +} + +void AIBinder_markVintfStability(AIBinder* binder) { + Stability::markVintf(binder->getBinder().get()); +} diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp index 8cd4e033df..bb1fe2f8e3 100644 --- a/libs/binder/ndk/test/Android.bp +++ b/libs/binder/ndk/test/Android.bp @@ -44,10 +44,10 @@ cc_defaults { "libandroid_runtime_lazy", "libbase", "libbinder", + "libbinder_ndk", "libutils", ], static_libs: [ - "libbinder_ndk", "test_libbinder_ndk_library", ], } diff --git a/libs/binder/ndk/update.sh b/libs/binder/ndk/update.sh index 9a4577ffff..1eba892021 100755 --- a/libs/binder/ndk/update.sh +++ b/libs/binder/ndk/update.sh @@ -20,4 +20,3 @@ set -ex # This script makes sure that the source code is in sync with the various scripts ./scripts/gen_parcel_helper.py ./scripts/format.sh -./scripts/init_map.sh > libbinder_ndk.map.txt diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index edbe05ee62..bc457ce30c 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -19,8 +19,6 @@ cc_defaults { cflags: [ "-Wall", "-Werror", - "-Wno-unused-private-field", - "-Wno-unused-variable", ], } @@ -139,3 +137,31 @@ cc_test { ], test_suites: ["device-tests"], } + +aidl_interface { + name: "binderStabilityTestIface", + srcs: [ + "IBinderStabilityTest.aidl", + ], +} + +cc_test { + name: "binderStabilityTest", + defaults: ["binder_test_defaults"], + srcs: [ + "binderStabilityTest.cpp", + ], + + shared_libs: [ + "libbinder_ndk", + "libbinder", + "liblog", + "libutils", + ], + static_libs: [ + "binderStabilityTestIface-cpp", + "binderStabilityTestIface-ndk_platform", + ], + + test_suites: ["device-tests"], +} diff --git a/libs/binder/tests/IBinderStabilityTest.aidl b/libs/binder/tests/IBinderStabilityTest.aidl new file mode 100644 index 0000000000..36e1c2cbc4 --- /dev/null +++ b/libs/binder/tests/IBinderStabilityTest.aidl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! +// THIS IS ONLY FOR TESTING! +interface IBinderStabilityTest { + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + void sendBinder(IBinder binder); + + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + void sendAndCallBinder(IBinder binder); + + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + IBinder returnNoStabilityBinder(); + + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + IBinder returnLocalStabilityBinder(); + + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + IBinder returnVintfStabilityBinder(); + + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + IBinder returnVendorStabilityBinder(); +} +// DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! +// THIS IS ONLY FOR TESTING! +// Construct and return a binder with a specific stability diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp index 77ebac8f5a..f3ed6a613c 100644 --- a/libs/binder/tests/binderDriverInterfaceTest.cpp +++ b/libs/binder/tests/binderDriverInterfaceTest.cpp @@ -230,7 +230,6 @@ TEST_F(BinderDriverInterfaceTest, IncRefsAcquireReleaseDecRefs) { } TEST_F(BinderDriverInterfaceTest, Transaction) { - binder_uintptr_t cookie = 1234; struct { uint32_t cmd1; struct binder_transaction_data arg1; diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index cf83faa8cf..4f0c969e91 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -66,7 +66,6 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION, - BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION, BINDER_LIB_TEST_EXIT_TRANSACTION, BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION, BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, @@ -767,22 +766,6 @@ TEST_F(BinderLibTest, PromoteLocal) { EXPECT_TRUE(strong_from_weak == nullptr); } -TEST_F(BinderLibTest, PromoteRemote) { - int ret; - Parcel data, reply; - sp<IBinder> strong = new BBinder(); - sp<IBinder> server = addServer(); - - ASSERT_TRUE(server != nullptr); - ASSERT_TRUE(strong != nullptr); - - ret = data.writeWeakBinder(strong); - EXPECT_EQ(NO_ERROR, ret); - - ret = server->transact(BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION, data, &reply); - EXPECT_GE(ret, 0); -} - TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) { status_t ret; Parcel data, reply; @@ -808,7 +791,6 @@ TEST_F(BinderLibTest, FreedBinder) { wp<IBinder> keepFreedBinder; { Parcel data, reply; - data.writeBool(false); /* request weak reference */ ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply); ASSERT_EQ(NO_ERROR, ret); struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data()); @@ -817,8 +799,9 @@ TEST_F(BinderLibTest, FreedBinder) { * delete its reference to it - otherwise the transaction * fails regardless of whether the driver is fixed. */ - keepFreedBinder = reply.readWeakBinder(); + keepFreedBinder = reply.readStrongBinder(); } + IPCThreadState::self()->flushCommands(); { Parcel data, reply; data.writeStrongBinder(server); @@ -954,7 +937,6 @@ class BinderLibTestService : public BBinder case BINDER_LIB_TEST_ADD_POLL_SERVER: case BINDER_LIB_TEST_ADD_SERVER: { int ret; - uint8_t buf[1] = { 0 }; int serverid; if (m_id != 0) { @@ -1152,29 +1134,6 @@ class BinderLibTestService : public BBinder if (ret != size) return UNKNOWN_ERROR; return NO_ERROR; } - case BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION: { - int ret; - wp<IBinder> weak; - sp<IBinder> strong; - Parcel data2, reply2; - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> server = sm->getService(binderLibTestServiceName); - - weak = data.readWeakBinder(); - if (weak == nullptr) { - return BAD_VALUE; - } - strong = weak.promote(); - - ret = server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data2, &reply2); - if (ret != NO_ERROR) - exit(EXIT_FAILURE); - - if (strong == nullptr) { - reply->setError(1); - } - return NO_ERROR; - } case BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION: alarm(10); return NO_ERROR; @@ -1183,13 +1142,8 @@ class BinderLibTestService : public BBinder ; exit(EXIT_SUCCESS); case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: { - bool strongRef = data.readBool(); sp<IBinder> binder = new BBinder(); - if (strongRef) { - reply->writeStrongBinder(binder); - } else { - reply->writeWeakBinder(binder); - } + reply->writeStrongBinder(binder); return NO_ERROR; } default: @@ -1204,7 +1158,6 @@ class BinderLibTestService : public BBinder bool m_serverStartRequested; sp<IBinder> m_serverStarted; sp<IBinder> m_strongRef; - bool m_callbackPending; sp<IBinder> m_callback; }; @@ -1266,7 +1219,7 @@ int run_server(int index, int readypipefd, bool usePoll) * We simulate a single-threaded process using the binder poll * interface; besides handling binder commands, it can also * issue outgoing transactions, by storing a callback in - * m_callback and setting m_callbackPending. + * m_callback. * * processPendingCall() will then issue that transaction. */ @@ -1293,8 +1246,6 @@ int run_server(int index, int readypipefd, bool usePoll) } int main(int argc, char **argv) { - int ret; - if (argc == 4 && !strcmp(argv[1], "--servername")) { binderservername = argv[2]; } else { diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index 6a16e2496d..ce026e9229 100644 --- a/libs/binder/tests/binderSafeInterfaceTest.cpp +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp @@ -75,7 +75,7 @@ public: private: int32_t mValue = 0; - uint8_t mPadding[4] = {}; // Avoids a warning from -Wpadded + __attribute__((unused)) uint8_t mPadding[4] = {}; // Avoids a warning from -Wpadded }; struct TestFlattenable : Flattenable<TestFlattenable> { @@ -727,6 +727,7 @@ TEST_F(SafeInterfaceTest, TestIncremementParcelableVector) { const std::vector<TestParcelable> a{TestParcelable{1}, TestParcelable{2}}; std::vector<TestParcelable> aPlusOne; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a.size(), aPlusOne.size()); for (size_t i = 0; i < a.size(); ++i) { ASSERT_EQ(a[i].getValue() + 1, aPlusOne[i].getValue()); diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp new file mode 100644 index 0000000000..0336b9e3ab --- /dev/null +++ b/libs/binder/tests/binderStabilityTest.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_manager.h> +#include <android/binder_stability.h> +#include <binder/Binder.h> +#include <binder/IBinder.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/Stability.h> +#include <gtest/gtest.h> + +#include <sys/prctl.h> + +#include "aidl/BnBinderStabilityTest.h" +#include "BnBinderStabilityTest.h" + +using namespace android; +using namespace ndk; +using android::binder::Status; +using android::internal::Stability; // for testing only! + +const String16 kSystemStabilityServer = String16("binder_stability_test_service_system"); + +// This is handwritten so that we can test different stability levels w/o having the AIDL +// compiler assign them. Hand-writing binder interfaces is considered a bad practice +// sanity reasons. YOU SHOULD DEFINE AN AIDL INTERFACE INSTEAD! +class BadStableBinder : public BBinder { +public: + static constexpr uint32_t USER_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION; + static String16 kDescriptor; + + bool gotUserTransaction = false; + + static status_t doUserTransaction(const sp<IBinder>& binder) { + Parcel data, reply; + data.writeInterfaceToken(kDescriptor); + return binder->transact(USER_TRANSACTION, data, &reply, 0/*flags*/); + } + + status_t onTransact(uint32_t code, + const Parcel& data, Parcel* reply, uint32_t flags) override { + if (code == USER_TRANSACTION) { + // not interested in this kind of stability. Make sure + // we have a test failure + LOG_ALWAYS_FATAL_IF(!data.enforceInterface(kDescriptor)); + + gotUserTransaction = true; + + ALOGE("binder stability: Got user transaction"); + return OK; + } + return BBinder::onTransact(code, data, reply, flags); + } + + static sp<BadStableBinder> undef() { + sp<BadStableBinder> iface = new BadStableBinder(); + return iface; + } + + static sp<BadStableBinder> system() { + sp<BadStableBinder> iface = new BadStableBinder(); + Stability::markCompilationUnit(iface.get()); // <- for test only + return iface; + } + + static sp<BadStableBinder> vintf() { + sp<BadStableBinder> iface = new BadStableBinder(); + Stability::markVintf(iface.get()); // <- for test only + return iface; + } + + static sp<BadStableBinder> vendor() { + sp<BadStableBinder> iface = new BadStableBinder(); + Stability::markVndk(iface.get()); // <- for test only + return iface; + } +}; +String16 BadStableBinder::kDescriptor = String16("BadStableBinder.test"); + +// NO! NO! NO! Do not even think of doing something like this! +// This is for testing! If a class like this was actually used in production, +// it would ruin everything! +class MyBinderStabilityTest : public BnBinderStabilityTest { +public: + Status sendBinder(const sp<IBinder>& /*binder*/) override { + return Status::ok(); + } + Status sendAndCallBinder(const sp<IBinder>& binder) override { + Stability::debugLogStability("sendAndCallBinder got binder", binder); + return Status::fromExceptionCode(BadStableBinder::doUserTransaction(binder)); + } + Status returnNoStabilityBinder(sp<IBinder>* _aidl_return) override { + *_aidl_return = BadStableBinder::undef(); + return Status::ok(); + } + Status returnLocalStabilityBinder(sp<IBinder>* _aidl_return) override { + *_aidl_return = BadStableBinder::system(); + return Status::ok(); + } + Status returnVintfStabilityBinder(sp<IBinder>* _aidl_return) override { + *_aidl_return = BadStableBinder::vintf(); + return Status::ok(); + } + Status returnVendorStabilityBinder(sp<IBinder>* _aidl_return) override { + *_aidl_return = BadStableBinder::vendor(); + return Status::ok(); + } +}; + +TEST(BinderStability, CantCallVendorBinderInSystemContext) { + sp<IBinder> serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer); + auto server = interface_cast<IBinderStabilityTest>(serverBinder); + + ASSERT_NE(nullptr, server.get()); + ASSERT_NE(nullptr, IInterface::asBinder(server)->remoteBinder()); + + EXPECT_TRUE(server->sendBinder(BadStableBinder::undef()).isOk()); + EXPECT_TRUE(server->sendBinder(BadStableBinder::system()).isOk()); + EXPECT_TRUE(server->sendBinder(BadStableBinder::vintf()).isOk()); + EXPECT_TRUE(server->sendBinder(BadStableBinder::vendor()).isOk()); + + { + sp<BadStableBinder> binder = BadStableBinder::undef(); + EXPECT_TRUE(server->sendAndCallBinder(binder).isOk()); + EXPECT_TRUE(binder->gotUserTransaction); + } + { + sp<BadStableBinder> binder = BadStableBinder::system(); + EXPECT_TRUE(server->sendAndCallBinder(binder).isOk()); + EXPECT_TRUE(binder->gotUserTransaction); + } + { + sp<BadStableBinder> binder = BadStableBinder::vintf(); + EXPECT_TRUE(server->sendAndCallBinder(binder).isOk()); + EXPECT_TRUE(binder->gotUserTransaction); + } + { + // !!! user-defined transaction may not be stable for remote server !!! + // !!! so, it does not work !!! + sp<BadStableBinder> binder = BadStableBinder::vendor(); + EXPECT_EQ(BAD_TYPE, server->sendAndCallBinder(binder).exceptionCode()); + EXPECT_FALSE(binder->gotUserTransaction); + } + + sp<IBinder> out; + EXPECT_TRUE(server->returnNoStabilityBinder(&out).isOk()); + ASSERT_NE(nullptr, out.get()); + EXPECT_EQ(OK, out->pingBinder()); + EXPECT_EQ(OK, BadStableBinder::doUserTransaction(out)); + + EXPECT_TRUE(server->returnLocalStabilityBinder(&out).isOk()); + ASSERT_NE(nullptr, out.get()); + EXPECT_EQ(OK, out->pingBinder()); + EXPECT_EQ(OK, BadStableBinder::doUserTransaction(out)); + + EXPECT_TRUE(server->returnVintfStabilityBinder(&out).isOk()); + ASSERT_NE(nullptr, out.get()); + EXPECT_EQ(OK, out->pingBinder()); + EXPECT_EQ(OK, BadStableBinder::doUserTransaction(out)); + + EXPECT_TRUE(server->returnVendorStabilityBinder(&out).isOk()); + ASSERT_NE(nullptr, out.get()); + + // !!! libbinder-defined transaction works !!! + EXPECT_EQ(OK, out->pingBinder()); + + // !!! user-defined transaction may not be stable !!! + // !!! so, it does not work !!! + EXPECT_EQ(BAD_TYPE, BadStableBinder::doUserTransaction(out)); +} + +// This is handwritten so that we can test different stability levels w/o having the AIDL +// compiler assign them. Hand-writing binder interfaces is considered a bad practice +// sanity reasons. YOU SHOULD DEFINE AN AIDL INTERFACE INSTEAD! + +struct NdkBinderStable_DataClass { + bool gotUserTransaction = false; +}; +void* NdkBadStableBinder_Class_onCreate(void* args) { + LOG_ALWAYS_FATAL_IF(args != nullptr, "Takes no args"); + return static_cast<void*>(new NdkBinderStable_DataClass); +} +void NdkBadStableBinder_Class_onDestroy(void* userData) { + delete static_cast<NdkBinderStable_DataClass*>(userData); +} +NdkBinderStable_DataClass* NdkBadStableBinder_getUserData(AIBinder* binder) { + LOG_ALWAYS_FATAL_IF(binder == nullptr); + void* userData = AIBinder_getUserData(binder); + LOG_ALWAYS_FATAL_IF(userData == nullptr, "null data - binder is remote?"); + + return static_cast<NdkBinderStable_DataClass*>(userData); +} +binder_status_t NdkBadStableBinder_Class_onTransact( + AIBinder* binder, transaction_code_t code, const AParcel* /*in*/, AParcel* /*out*/) { + + if (code == BadStableBinder::USER_TRANSACTION) { + ALOGE("ndk binder stability: Got user transaction"); + NdkBadStableBinder_getUserData(binder)->gotUserTransaction = true; + return STATUS_OK; + } + + return STATUS_UNKNOWN_TRANSACTION; +} + +static AIBinder_Class* kNdkBadStableBinder = + AIBinder_Class_define(String8(BadStableBinder::kDescriptor).c_str(), + NdkBadStableBinder_Class_onCreate, + NdkBadStableBinder_Class_onDestroy, + NdkBadStableBinder_Class_onTransact); + +// for testing only to get around __ANDROID_VNDK__ guard. +extern "C" void AIBinder_markVendorStability(AIBinder* binder); // <- BAD DO NOT COPY + +TEST(BinderStability, NdkCantCallVendorBinderInSystemContext) { + SpAIBinder binder = SpAIBinder(AServiceManager_getService( + String8(kSystemStabilityServer).c_str())); + + std::shared_ptr<aidl::IBinderStabilityTest> remoteServer = + aidl::IBinderStabilityTest::fromBinder(binder); + + ASSERT_NE(nullptr, remoteServer.get()); + + SpAIBinder comp = SpAIBinder(AIBinder_new(kNdkBadStableBinder, nullptr /*args*/)); + EXPECT_TRUE(remoteServer->sendBinder(comp).isOk()); + EXPECT_TRUE(remoteServer->sendAndCallBinder(comp).isOk()); + EXPECT_TRUE(NdkBadStableBinder_getUserData(comp.get())->gotUserTransaction); + + SpAIBinder vendor = SpAIBinder(AIBinder_new(kNdkBadStableBinder, nullptr /*args*/)); + AIBinder_markVendorStability(vendor.get()); + EXPECT_TRUE(remoteServer->sendBinder(vendor).isOk()); + EXPECT_FALSE(remoteServer->sendAndCallBinder(vendor).isOk()); + EXPECT_FALSE(NdkBadStableBinder_getUserData(vendor.get())->gotUserTransaction); +} + +class MarksStabilityInConstructor : public BBinder { +public: + static bool gDestructed; + + MarksStabilityInConstructor() { + Stability::markCompilationUnit(this); + } + ~MarksStabilityInConstructor() { + gDestructed = true; + } +}; +bool MarksStabilityInConstructor::gDestructed = false; + +TEST(BinderStability, MarkingObjectNoDestructTest) { + ASSERT_FALSE(MarksStabilityInConstructor::gDestructed); + + // best practice is to put this directly in an sp, but for this test, we + // want to explicitly check what happens before that happens + MarksStabilityInConstructor* binder = new MarksStabilityInConstructor(); + ASSERT_FALSE(MarksStabilityInConstructor::gDestructed); + + sp<MarksStabilityInConstructor> binderSp = binder; + ASSERT_FALSE(MarksStabilityInConstructor::gDestructed); + + binderSp = nullptr; + ASSERT_TRUE(MarksStabilityInConstructor::gDestructed); +} + +TEST(BinderStability, RemarkDies) { + ASSERT_DEATH({ + sp<IBinder> binder = new BBinder(); + Stability::markCompilationUnit(binder.get()); // <-- only called for tests + Stability::markVndk(binder.get()); // <-- only called for tests + }, "Should only mark known object."); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + if (fork() == 0) { + // child process + prctl(PR_SET_PDEATHSIG, SIGHUP); + + sp<IBinder> server = new MyBinderStabilityTest; + android::defaultServiceManager()->addService(kSystemStabilityServer, server); + + IPCThreadState::self()->joinThreadPool(true); + exit(1); // should not reach + } + + // This is not racey. Just giving these services some time to register before we call + // getService which sleeps for much longer... + usleep(10000); + + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp index ec9534abac..ab4c56a6af 100644 --- a/libs/binder/tests/schd-dbg.cpp +++ b/libs/binder/tests/schd-dbg.cpp @@ -290,6 +290,7 @@ static void* thread_start(void* p) { sta = tickNow(); status_t ret = workers[target]->transact(BINDER_NOP, data, &reply); + ASSERT(ret == NO_ERROR); end = tickNow(); results_fifo->add_time(tickNano(sta, end)); diff --git a/services/nativeperms/.clang-format b/services/nativeperms/.clang-format deleted file mode 100644 index 6006e6f4fd..0000000000 --- a/services/nativeperms/.clang-format +++ /dev/null @@ -1,13 +0,0 @@ -BasedOnStyle: Google -AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: true -AllowShortLoopsOnASingleLine: true -BinPackArguments: true -BinPackParameters: true -ColumnLimit: 80 -CommentPragmas: NOLINT:.* -ContinuationIndentWidth: 8 -DerivePointerAlignment: false -IndentWidth: 4 -PointerAlignment: Left -TabWidth: 4 diff --git a/services/nativeperms/Android.bp b/services/nativeperms/Android.bp deleted file mode 100644 index cbc7d665e3..0000000000 --- a/services/nativeperms/Android.bp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -cc_binary { - name: "nativeperms", - srcs: [ - "nativeperms.cpp", - "android/os/IPermissionController.aidl", - ], - cflags: [ - "-Wall", - "-Werror", - ], - shared_libs: [ - "libbinder", - "libbrillo", - "libbrillo-binder", - "libchrome", - "libutils", - ], - init_rc: ["nativeperms.rc"], -} diff --git a/services/nativeperms/android/os/IPermissionController.aidl b/services/nativeperms/android/os/IPermissionController.aidl deleted file mode 100644 index 89db85ca0a..0000000000 --- a/services/nativeperms/android/os/IPermissionController.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* //device/java/android/android/os/IPowerManager.aidl -** -** Copyright 2007, 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 */ -interface IPermissionController { - boolean checkPermission(String permission, int pid, int uid); - String[] getPackagesForUid(int uid); - boolean isRuntimePermission(String permission); -} diff --git a/services/nativeperms/android/os/README b/services/nativeperms/android/os/README deleted file mode 100644 index e4144995b0..0000000000 --- a/services/nativeperms/android/os/README +++ /dev/null @@ -1,4 +0,0 @@ -IPermissionController.aidl in this directory is a verbatim copy of -https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/IPermissionController.aidl, -because some Brillo manifests do not currently include the frameworks/base repo. -TODO(jorgelo): Figure out a way to use the .aidl file in frameworks/base. diff --git a/services/nativeperms/nativeperms.cpp b/services/nativeperms/nativeperms.cpp deleted file mode 100644 index 7f03bedb29..0000000000 --- a/services/nativeperms/nativeperms.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2016 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 <base/at_exit.h> -#include <base/logging.h> -#include <base/message_loop/message_loop.h> -#include <binder/IServiceManager.h> -#include <binder/Status.h> -#include <brillo/binder_watcher.h> -#include <brillo/message_loops/base_message_loop.h> -#include <brillo/syslog_logging.h> -#include <utils/String16.h> - -#include "android/os/BnPermissionController.h" - -namespace { -static android::String16 serviceName("permission"); -} - -namespace android { - -class PermissionService : public android::os::BnPermissionController { - public: - ::android::binder::Status checkPermission( - const ::android::String16& permission, int32_t pid, int32_t uid, - bool* _aidl_return) { - (void)permission; - (void)pid; - (void)uid; - *_aidl_return = true; - return binder::Status::ok(); - } - - ::android::binder::Status getPackagesForUid( - int32_t uid, ::std::vector<::android::String16>* _aidl_return) { - (void)uid; - // Brillo doesn't currently have installable packages. - if (_aidl_return) { - _aidl_return->clear(); - } - return binder::Status::ok(); - } - - ::android::binder::Status isRuntimePermission( - const ::android::String16& permission, bool* _aidl_return) { - (void)permission; - // Brillo doesn't currently have runtime permissions. - *_aidl_return = false; - return binder::Status::ok(); - } -}; - -} // namespace android - -int main() { - base::AtExitManager atExitManager; - brillo::InitLog(brillo::kLogToSyslog); - // Register the service with servicemanager. - android::status_t status = android::defaultServiceManager()->addService( - serviceName, new android::PermissionService()); - CHECK(status == android::OK) << "Failed to get IPermissionController " - "binder from servicemanager."; - - // Create a message loop. - base::MessageLoopForIO messageLoopForIo; - brillo::BaseMessageLoop messageLoop{&messageLoopForIo}; - - // Initialize a binder watcher. - brillo::BinderWatcher watcher(&messageLoop); - watcher.Init(); - - // Run the message loop. - messageLoop.Run(); -} diff --git a/services/nativeperms/nativeperms.rc b/services/nativeperms/nativeperms.rc deleted file mode 100644 index 704c0a2acc..0000000000 --- a/services/nativeperms/nativeperms.rc +++ /dev/null @@ -1,4 +0,0 @@ -service nativeperms /system/bin/nativeperms - class main - user system - group system |