diff options
27 files changed, 912 insertions, 167 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 4d98520e01..310b5cecf0 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -135,6 +135,11 @@ typedef Dumpstate::ConsentCallback::ConsentResult UserConsentResult; static char cmdline_buf[16384] = "(unknown)"; static const char *dump_traces_path = nullptr; static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000; +// Because telephony reports are significantly faster to collect (< 10 seconds vs. > 2 minutes), +// it's often the case that they time out far too quickly for consent with such a hefty dialog for +// the user to read. For telephony reports only, we increase the default timeout to 2 minutes to +// roughly match full reports' durations. +static const uint64_t TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS = 2 * 60 * 1000; // TODO: variables and functions below should be part of dumpstate object @@ -149,6 +154,7 @@ void add_mountinfo(); #define RECOVERY_DATA_DIR "/data/misc/recovery" #define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log" #define LOGPERSIST_DATA_DIR "/data/misc/logd" +#define PREREBOOT_DATA_DIR "/data/misc/prereboot" #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" #define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref" #define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat" @@ -890,6 +896,14 @@ static void DoSystemLogcat(time_t since) { CommandOptions::WithTimeoutInMs(timeout_ms).Build()); } +static void DoRadioLogcat() { + unsigned long timeout_ms = logcat_timeout({"radio"}); + RunCommand( + "RADIO LOG", + {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); +} + static void DoLogcat() { unsigned long timeout_ms; // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags"); @@ -908,11 +922,7 @@ static void DoLogcat() { "STATS LOG", {"logcat", "-b", "stats", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); - timeout_ms = logcat_timeout({"radio"}); - RunCommand( - "RADIO LOG", - {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, - CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); + DoRadioLogcat(); RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"}); @@ -1413,11 +1423,14 @@ static Dumpstate::RunStatus dumpstate() { RunCommand("FILESYSTEMS & FREE SPACE", {"df"}); /* Binder state is expensive to look at as it uses a lot of memory. */ - DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log"); - DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log"); - DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions"); - DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats"); - DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state"); + std::string binder_logs_dir = access("/dev/binderfs/binder_logs", R_OK) ? + "/sys/kernel/debug/binder" : "/dev/binderfs/binder_logs"; + + DumpFile("BINDER FAILED TRANSACTION LOG", binder_logs_dir + "/failed_transaction_log"); + DumpFile("BINDER TRANSACTION LOG", binder_logs_dir + "/transaction_log"); + DumpFile("BINDER TRANSACTIONS", binder_logs_dir + "/transactions"); + DumpFile("BINDER STATS", binder_logs_dir + "/stats"); + DumpFile("BINDER STATE", binder_logs_dir + "/state"); /* Add window and surface trace files. */ if (!PropertiesHelper::IsUserBuild()) { @@ -1561,6 +1574,7 @@ static Dumpstate::RunStatus DumpstateDefault() { ds.AddDir(PROFILE_DATA_DIR_CUR, true); ds.AddDir(PROFILE_DATA_DIR_REF, true); } + ds.AddDir(PREREBOOT_DATA_DIR, false); add_mountinfo(); DumpIpTablesAsRoot(); DumpDynamicPartitionInfo(); @@ -1599,8 +1613,10 @@ static Dumpstate::RunStatus DumpstateDefault() { return status; } -// This method collects common dumpsys for telephony and wifi -static void DumpstateRadioCommon() { +// This method collects common dumpsys for telephony and wifi. Typically, wifi +// reports are fine to include all information, but telephony reports on user +// builds need to strip some content (see DumpstateTelephonyOnly). +static void DumpstateRadioCommon(bool include_sensitive_info = true) { DumpIpTablesAsRoot(); ds.AddDir(LOGPERSIST_DATA_DIR, false); @@ -1609,26 +1625,51 @@ static void DumpstateRadioCommon() { return; } - do_dmesg(); - DoLogcat(); + // We need to be picky about some stuff for telephony reports on user builds. + if (!include_sensitive_info) { + // Only dump the radio log buffer (other buffers and dumps contain too much unrelated info). + DoRadioLogcat(); + } else { + // Contains various system properties and process startup info. + do_dmesg(); + // Logs other than the radio buffer may contain package/component names and potential PII. + DoLogcat(); + // Too broad for connectivity problems. + DoKmsg(); + // Contains unrelated hardware info (camera, NFC, biometrics, ...). + DumpHals(); + } + DumpPacketStats(); - DoKmsg(); DumpIpAddrAndRules(); dump_route_tables(); - DumpHals(); - RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}, CommandOptions::WithTimeout(10).Build()); } -// This method collects dumpsys for telephony debugging only +// We use "telephony" here for legacy reasons, though this now really means "connectivity" (cellular +// + wifi + networking). This method collects dumpsys for connectivity debugging only. General rules +// for what can be included on user builds: all reported information MUST directly relate to +// connectivity debugging or customer support and MUST NOT contain unrelated personally identifiable +// information. This information MUST NOT identify user-installed packages (UIDs are OK, package +// names are not), and MUST NOT contain logs of user application traffic. +// TODO(b/148168577) rename this and other related fields/methods to "connectivity" instead. static void DumpstateTelephonyOnly() { DurationReporter duration_reporter("DUMPSTATE"); + const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build(); - DumpstateRadioCommon(); + const bool include_sensitive_info = !PropertiesHelper::IsUserBuild(); - RunCommand("SYSTEM PROPERTIES", {"getprop"}); + DumpstateRadioCommon(include_sensitive_info); + + if (include_sensitive_info) { + // Contains too much unrelated PII, and given the unstructured nature of sysprops, we can't + // really cherrypick all of the connectivity-related ones. Apps generally have no business + // reading these anyway, and there should be APIs to supply the info in a more app-friendly + // way. + RunCommand("SYSTEM PROPERTIES", {"getprop"}); + } printf("========================================================\n"); printf("== Android Framework Services\n"); @@ -1636,15 +1677,28 @@ static void DumpstateTelephonyOnly() { RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); - RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(), - SEC_TO_MSEC(10)); - RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + // TODO(b/146521742) build out an argument to include bound services here for user builds RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); - RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(), + RunDumpsys("DUMPSYS", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"network_management"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + if (include_sensitive_info) { + // Contains raw IP addresses, omit from reports on user builds. + RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + // Contains raw destination IP/MAC addresses, omit from reports on user builds. + RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + // Contains package/component names, omit from reports on user builds. + RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + // Contains package names, but should be relatively simple to remove them (also contains + // UIDs already), omit from reports on user builds. + RunDumpsys("BATTERYSTATS", {"deviceidle"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + } printf("========================================================\n"); printf("== Running Application Services\n"); @@ -1652,18 +1706,24 @@ static void DumpstateTelephonyOnly() { RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"}); - printf("========================================================\n"); - printf("== Running Application Services (non-platform)\n"); - printf("========================================================\n"); + if (include_sensitive_info) { + printf("========================================================\n"); + printf("== Running Application Services (non-platform)\n"); + printf("========================================================\n"); - RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, - DUMPSYS_COMPONENTS_OPTIONS); + // Contains package/component names and potential PII, omit from reports on user builds. + // To get dumps of the active CarrierService(s) on user builds, we supply an argument to the + // carrier_config dumpsys instead. + RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, + DUMPSYS_COMPONENTS_OPTIONS); - printf("========================================================\n"); - printf("== Checkins\n"); - printf("========================================================\n"); + printf("========================================================\n"); + printf("== Checkins\n"); + printf("========================================================\n"); - RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); + // Contains package/component names, omit from reports on user builds. + RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); + } printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); @@ -2275,6 +2335,7 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt break; case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: options->telephony_only = true; + options->do_progress_updates = true; options->do_fb = false; options->do_broadcast = true; break; @@ -2825,8 +2886,13 @@ Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() { if (consent_result == UserConsentResult::UNAVAILABLE) { // User has not responded yet. uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs(); - if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) { - uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000; + // Telephony is a fast report type, particularly on user builds where information may be + // more aggressively limited. To give the user time to read the consent dialog, increase the + // timeout. + uint64_t timeout_ms = options_->telephony_only ? TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS + : USER_CONSENT_TIMEOUT_MS; + if (elapsed_ms < timeout_ms) { + uint delay_seconds = (timeout_ms - elapsed_ms) / 1000; MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds); sleep(delay_seconds); } diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index cff1d439d9..99d482f034 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -372,12 +372,12 @@ TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { EXPECT_TRUE(options_.do_broadcast); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.telephony_only); + EXPECT_TRUE(options_.do_progress_updates); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); - EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); } diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 5597bcd915..a427c8dd68 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -29,6 +29,7 @@ #include <utils/Log.h> #include <utils/Vector.h> +#include <iostream> #include <fcntl.h> #include <getopt.h> #include <stdio.h> @@ -231,14 +232,14 @@ int Dumpsys::main(int argc, char* const argv[]) { const size_t N = services.size(); if (N > 1) { // first print a list of the current services - aout << "Currently running services:" << endl; + std::cout << "Currently running services:" << std::endl; for (size_t i=0; i<N; i++) { sp<IBinder> service = sm_->checkService(services[i]); if (service != nullptr) { bool skipped = IsSkipped(skippedServices, services[i]); - aout << " " << services[i] << (skipped ? " (skipped)" : "") << endl; + std::cout << " " << services[i] << (skipped ? " (skipped)" : "") << std::endl; } } } @@ -263,10 +264,10 @@ int Dumpsys::main(int argc, char* const argv[]) { asProto, elapsedDuration, bytesWritten); if (status == TIMED_OUT) { - aout << endl + std::cout << std::endl << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs - << "ms) EXPIRED ***" << endl - << endl; + << "ms) EXPIRED ***" << std::endl + << std::endl; } if (addSeparator) { @@ -332,14 +333,14 @@ status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, const Vector<String16>& args) { sp<IBinder> service = sm_->checkService(serviceName); if (service == nullptr) { - aerr << "Can't find service: " << serviceName << endl; + std::cerr << "Can't find service: " << serviceName << std::endl; return NAME_NOT_FOUND; } int sfd[2]; if (pipe(sfd) != 0) { - aerr << "Failed to create pipe to dump service info for " << serviceName << ": " - << strerror(errno) << endl; + std::cerr << "Failed to create pipe to dump service info for " << serviceName << ": " + << strerror(errno) << std::endl; return -errno; } @@ -359,13 +360,13 @@ status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, err = dumpPidToFd(service, remote_end); break; default: - aerr << "Unknown dump type" << static_cast<int>(type) << endl; + std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl; return; } if (err != OK) { - aerr << "Error dumping service info status_t: " << statusToString(err) << " " - << serviceName << endl; + std::cerr << "Error dumping service info status_t: " << statusToString(err) << " " + << serviceName << std::endl; } }); return OK; @@ -422,8 +423,8 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); if (rc < 0) { - aerr << "Error in poll while dumping service " << serviceName << " : " - << strerror(errno) << endl; + std::cerr << "Error in poll while dumping service " << serviceName << " : " + << strerror(errno) << std::endl; status = -errno; break; } else if (rc == 0) { @@ -434,8 +435,8 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi char buf[4096]; rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf))); if (rc < 0) { - aerr << "Failed to read while dumping service " << serviceName << ": " - << strerror(errno) << endl; + std::cerr << "Failed to read while dumping service " << serviceName << ": " + << strerror(errno) << std::endl; status = -errno; break; } else if (rc == 0) { @@ -444,8 +445,8 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi } if (!WriteFully(fd, buf, rc)) { - aerr << "Failed to write while dumping service " << serviceName << ": " - << strerror(errno) << endl; + std::cerr << "Failed to write while dumping service " << serviceName << ": " + << strerror(errno) << std::endl; status = -errno; break; } diff --git a/cmds/dumpsys/main.cpp b/cmds/dumpsys/main.cpp index 8ba0eba0fa..fa4cc97b91 100644 --- a/cmds/dumpsys/main.cpp +++ b/cmds/dumpsys/main.cpp @@ -21,10 +21,9 @@ #include "dumpsys.h" #include <binder/IServiceManager.h> -#include <binder/TextOutput.h> +#include <iostream> #include <signal.h> -#include <stdio.h> using namespace android; @@ -34,7 +33,7 @@ int main(int argc, char* const argv[]) { fflush(stdout); if (sm == nullptr) { ALOGE("Unable to get default service manager!"); - aerr << "dumpsys: Unable to get default service manager!" << endl; + std::cerr << "dumpsys: Unable to get default service manager!" << std::endl; return 20; } diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index f95e445ade..70bbc33b42 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -299,9 +299,10 @@ const char* select_execution_binary( // Namespace for Android Runtime flags applied during boot time. static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot"; // Feature flag name for running the JIT in Zygote experiment, b/119800099. -static const char* ENABLE_APEX_IMAGE = "enable_apex_image"; -// Location of the apex image. -static const char* kApexImage = "/system/framework/apex.art"; +static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image"; +// Location of the JIT Zygote image. +static const char* kJitZygoteImage = + "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof"; // Phenotype property name for enabling profiling the boot class path. static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath"; @@ -405,9 +406,9 @@ class RunDex2Oat : public ExecVHelper { GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault); std::string boot_image; - std::string use_apex_image = + std::string use_jitzygote_image = server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, - ENABLE_APEX_IMAGE, + ENABLE_JITZYGOTE_IMAGE, /*default_value=*/ ""); std::string profile_boot_class_path = GetProperty("dalvik.vm.profilebootclasspath", ""); @@ -417,10 +418,10 @@ class RunDex2Oat : public ExecVHelper { PROFILE_BOOT_CLASS_PATH, /*default_value=*/ profile_boot_class_path); - if (use_apex_image == "true" || profile_boot_class_path == "true") { - boot_image = StringPrintf("-Ximage:%s", kApexImage); + if (use_jitzygote_image == "true" || profile_boot_class_path == "true") { + boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage); } else { - boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s"); + boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s"); } // clang FORTIFY doesn't let us use strlen in constant array bounds, so we @@ -513,7 +514,8 @@ class RunDex2Oat : public ExecVHelper { AddArg(instruction_set_variant_arg); AddArg(instruction_set_features_arg); - AddRuntimeArg(boot_image); + AddArg(boot_image); + AddRuntimeArg(bootclasspath); AddRuntimeArg(dex2oat_Xms_arg); AddRuntimeArg(dex2oat_Xmx_arg); diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index 3ff9d11806..7c989f6ced 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -73,7 +73,7 @@ static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_pac for (const apex::ApexFile& apex_file : active_packages) { const std::string& package_path = apex_file.GetPath(); base::Result<void> status = apex::deactivatePackage(package_path); - if (!status) { + if (!status.ok()) { LOG(ERROR) << "Failed to deactivate " << package_path << ": " << status.error(); } diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 5f9d4004f4..bc541f4d31 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -170,7 +170,6 @@ aidl_interface { name: "libbinder_aidl_test_stub", local_include_dir: "aidl", srcs: [":libbinder_aidl"], - visibility: [":__subpackages__"], vendor_available: true, backend: { java: { diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 4dcd07a776..9e89c57da3 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -1230,6 +1230,11 @@ status_t IPCThreadState::executeCommand(int32_t cmd) if (error < NO_ERROR) reply.setError(error); sendReply(reply, 0); } else { + if (error != OK || reply.dataSize() != 0) { + alog << "oneway function results will be dropped but finished with status " + << statusToString(error) + << " and parcel size " << reply.dataSize() << endl; + } LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); } diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 328653ad0c..9888b59854 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -85,31 +85,36 @@ private: sp<AidlServiceManager> mTheRealServiceManager; }; -static Mutex gDefaultServiceManagerLock; +static std::once_flag gSmOnce; static sp<IServiceManager> gDefaultServiceManager; sp<IServiceManager> defaultServiceManager() { - - if (gDefaultServiceManager != nullptr) return gDefaultServiceManager; - - { - AutoMutex _l(gDefaultServiceManagerLock); - while (gDefaultServiceManager == nullptr) { - gDefaultServiceManager = new ServiceManagerShim( - interface_cast<AidlServiceManager>( - ProcessState::self()->getContextObject(nullptr))); - if (gDefaultServiceManager == nullptr) + std::call_once(gSmOnce, []() { + sp<AidlServiceManager> sm = nullptr; + while (sm == nullptr) { + sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr)); + if (sm == nullptr) { sleep(1); + } } - } + + gDefaultServiceManager = new ServiceManagerShim(sm); + }); return gDefaultServiceManager; } void setDefaultServiceManager(const sp<IServiceManager>& sm) { - AutoMutex _l(gDefaultServiceManagerLock); - gDefaultServiceManager = sm; + bool called = false; + std::call_once(gSmOnce, [&]() { + gDefaultServiceManager = sm; + called = true; + }); + + if (!called) { + LOG_ALWAYS_FATAL("setDefaultServiceManager() called after defaultServiceManager()."); + } } #if !defined(__ANDROID_VNDK__) && defined(__ANDROID__) diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 9f71f3e534..822247f561 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -2543,11 +2543,13 @@ status_t Parcel::continueWrite(size_t desired) if (objectsSize == 0) { free(mObjects); mObjects = nullptr; + mObjectsCapacity = 0; } else { binder_size_t* objects = (binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t)); if (objects) { mObjects = objects; + mObjectsCapacity = objectsSize; } } mObjectsSize = objectsSize; diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 31f022dca9..1d520c127b 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -102,6 +102,9 @@ sp<IServiceManager> defaultServiceManager(); /** * Directly set the default service manager. Only used for testing. + * Note that the caller is responsible for caling this method + * *before* any call to defaultServiceManager(); if the latter is + * called first, setDefaultServiceManager() will abort. */ void setDefaultServiceManager(const sp<IServiceManager>& sm); diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index e752c45d60..75dcdc8389 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -24,10 +24,13 @@ #include <android-base/logging.h> #include <binder/IPCThreadState.h> +#include <binder/IResultReceiver.h> +#include <private/android_filesystem_config.h> using DeathRecipient = ::android::IBinder::DeathRecipient; using ::android::IBinder; +using ::android::IResultReceiver; using ::android::Parcel; using ::android::sp; using ::android::status_t; @@ -158,6 +161,45 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce binder_status_t status = getClass()->onTransact(this, code, &in, &out); return PruneStatusT(status); + } else if (code == SHELL_COMMAND_TRANSACTION) { + int in = data.readFileDescriptor(); + int out = data.readFileDescriptor(); + int err = data.readFileDescriptor(); + + int argc = data.readInt32(); + std::vector<String8> utf8Args; // owns memory of utf8s + std::vector<const char*> utf8Pointers; // what can be passed over NDK API + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + utf8Args.push_back(String8(data.readString16())); + utf8Pointers.push_back(utf8Args[i].c_str()); + } + + data.readStrongBinder(); // skip over the IShellCallback + sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(data.readStrongBinder()); + + // Shell commands should only be callable by ADB. + uid_t uid = AIBinder_getCallingUid(); + if (uid != AID_ROOT && uid != AID_SHELL) { + if (resultReceiver != nullptr) { + resultReceiver->send(-1); + } + return STATUS_PERMISSION_DENIED; + } + + // Check that the file descriptors are valid. + if (in == STATUS_BAD_TYPE || out == STATUS_BAD_TYPE || err == STATUS_BAD_TYPE) { + if (resultReceiver != nullptr) { + resultReceiver->send(-1); + } + return STATUS_BAD_VALUE; + } + + binder_status_t status = getClass()->handleShellCommand( + this, in, out, err, utf8Pointers.data(), utf8Pointers.size()); + if (resultReceiver != nullptr) { + resultReceiver->send(status); + } + return status; } else { return BBinder::onTransact(code, data, reply, flags); } @@ -266,6 +308,13 @@ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) { clazz->onDump = onDump; } +void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz, + AIBinder_handleShellCommand handleShellCommand) { + CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz"; + + clazz->handleShellCommand = handleShellCommand; +} + void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) { CHECK(who == mWho); diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 5cb68c291b..57794279f2 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -17,6 +17,7 @@ #pragma once #include <android/binder_ibinder.h> +#include <android/binder_shell.h> #include "ibinder_internal.h" #include <atomic> @@ -115,6 +116,7 @@ struct AIBinder_Class { // optional methods for a class AIBinder_onDump onDump; + AIBinder_handleShellCommand handleShellCommand; private: // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h index 83a10488e0..7331ba20c4 100644 --- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h @@ -30,6 +30,11 @@ #include <android/binder_auto_utils.h> #include <android/binder_ibinder.h> +#if __has_include(<android/binder_shell.h>) +#include <android/binder_shell.h> +#define HAS_BINDER_SHELL_COMMAND +#endif //_has_include + #include <assert.h> #include <memory> @@ -108,7 +113,15 @@ class ICInterface : public SharedRefBase { /** * Dumps information about the interface. By default, dumps nothing. */ - virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/); + virtual inline binder_status_t dump(int fd, const char** args, uint32_t numArgs); + +#ifdef HAS_BINDER_SHELL_COMMAND + /** + * Process shell commands. By default, does nothing. + */ + virtual inline binder_status_t handleShellCommand(int in, int out, int err, const char** argv, + uint32_t argc); +#endif /** * Interprets this binder as this underlying interface if this has stored an ICInterface in the @@ -136,6 +149,11 @@ class ICInterface : public SharedRefBase { static inline void onDestroy(void* userData); static inline binder_status_t onDump(AIBinder* binder, int fd, const char** args, uint32_t numArgs); + +#ifdef HAS_BINDER_SHELL_COMMAND + static inline binder_status_t handleShellCommand(AIBinder* binder, int in, int out, int err, + const char** argv, uint32_t argc); +#endif }; }; @@ -191,6 +209,13 @@ binder_status_t ICInterface::dump(int /*fd*/, const char** /*args*/, uint32_t /* return STATUS_OK; } +#ifdef HAS_BINDER_SHELL_COMMAND +binder_status_t ICInterface::handleShellCommand(int /*in*/, int /*out*/, int /*err*/, + const char** /*argv*/, uint32_t /*argc*/) { + return STATUS_OK; +} +#endif + std::shared_ptr<ICInterface> ICInterface::asInterface(AIBinder* binder) { return ICInterfaceData::getInterface(binder); } @@ -203,9 +228,12 @@ AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor, return nullptr; } - // We can't know if this method is overriden by a subclass interface, so we must register - // ourselves. The default (nothing to dump) is harmless. + // We can't know if these methods are overridden by a subclass interface, so we must register + // ourselves. The defaults are harmless. AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump); +#ifdef HAS_BINDER_SHELL_COMMAND + AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand); +#endif return clazz; } @@ -234,6 +262,15 @@ binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, c return interface->dump(fd, args, numArgs); } +#ifdef HAS_BINDER_SHELL_COMMAND +binder_status_t ICInterface::ICInterfaceData::handleShellCommand(AIBinder* binder, int in, int out, + int err, const char** argv, + uint32_t argc) { + std::shared_ptr<ICInterface> interface = getInterface(binder); + return interface->handleShellCommand(in, out, err, argv, argc); +} +#endif + template <typename INTERFACE> SpAIBinder BnCInterface<INTERFACE>::asBinder() { std::lock_guard<std::mutex> l(mMutex); diff --git a/libs/binder/ndk/include_platform/android/binder_shell.h b/libs/binder/ndk/include_platform/android/binder_shell.h new file mode 100644 index 0000000000..17b38b0dae --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_shell.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_ibinder.h> + +__BEGIN_DECLS + +/** + * Function to execute a shell command. + * + * Available since API level 30. + * + * \param binder the binder executing the command + * \param in input file descriptor, should be flushed, ownership is not passed + * \param out output file descriptor, should be flushed, ownership is not passed + * \param err error file descriptor, should be flushed, ownership is not passed + * \param argv array of null-terminated strings for command (may be null if argc + * is 0) + * \param argc length of argv array + * + * \return binder_status_t result of transaction + */ +typedef binder_status_t (*AIBinder_handleShellCommand)(AIBinder* binder, int in, int out, int err, + const char** argv, uint32_t argc); + +/** + * This sets the implementation of handleShellCommand for a class. + * + * If this isn't set, nothing will be executed when handleShellCommand is called. + * + * Available since API level 30. + * + * \param handleShellCommand function to call when a shell transaction is + * received + */ +void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz, + AIBinder_handleShellCommand handleShellCommand) + __INTRODUCED_IN(30); + +__END_DECLS diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index f3158d7e18..7e72f22d64 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -110,6 +110,7 @@ LIBBINDER_NDK30 { # introduced=30 AIBinder_markSystemStability; # apex AIBinder_markVendorStability; # llndk AIBinder_markVintfStability; # apex llndk + AIBinder_Class_setHandleShellCommand; # apex llndk local: *; }; diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp index 513d8c2eba..cb4b20ff9d 100644 --- a/libs/binder/ndk/test/Android.bp +++ b/libs/binder/ndk/test/Android.bp @@ -60,6 +60,7 @@ cc_test { defaults: ["test_libbinder_ndk_test_defaults"], srcs: ["libbinder_ndk_unit_test.cpp"], static_libs: [ + "IBinderNdkUnitTest-cpp", "IBinderNdkUnitTest-ndk_platform", ], test_suites: ["general-tests"], diff --git a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp index 51dd169ec0..fd30d87c76 100644 --- a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <IBinderNdkUnitTest.h> #include <aidl/BnBinderNdkUnitTest.h> #include <aidl/BnEmpty.h> #include <android-base/logging.h> @@ -26,13 +27,16 @@ // warning: this is assuming that libbinder_ndk is using the same copy // of libbinder that we are. #include <binder/IPCThreadState.h> +#include <binder/IResultReceiver.h> +#include <binder/IServiceManager.h> +#include <binder/IShellCallback.h> #include <sys/prctl.h> #include <chrono> #include <condition_variable> #include <mutex> -using ::android::sp; +using namespace android; constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest"; @@ -48,6 +52,14 @@ class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { android::IPCThreadState::self()->flushCommands(); return ndk::ScopedAStatus::ok(); } + binder_status_t handleShellCommand(int /*in*/, int out, int /*err*/, const char** args, + uint32_t numArgs) override { + for (uint32_t i = 0; i < numArgs; i++) { + dprintf(out, "%s", args[i]); + } + fsync(out); + return STATUS_OK; + } }; int generatedService() { @@ -296,6 +308,92 @@ TEST(NdkBinder, SentAidlBinderCanBeDestroyed) { EXPECT_TRUE(destroyed); } +class MyResultReceiver : public BnResultReceiver { + public: + Mutex mMutex; + Condition mCondition; + bool mHaveResult = false; + int32_t mResult = 0; + + virtual void send(int32_t resultCode) { + AutoMutex _l(mMutex); + mResult = resultCode; + mHaveResult = true; + mCondition.signal(); + } + + int32_t waitForResult() { + AutoMutex _l(mMutex); + while (!mHaveResult) { + mCondition.wait(mMutex); + } + return mResult; + } +}; + +class MyShellCallback : public BnShellCallback { + public: + virtual int openFile(const String16& /*path*/, const String16& /*seLinuxContext*/, + const String16& /*mode*/) { + // Empty implementation. + return 0; + } +}; + +bool ReadFdToString(int fd, std::string* content) { + char buf[64]; + ssize_t n; + while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) { + content->append(buf, n); + } + return (n == 0) ? true : false; +} + +std::string shellCmdToString(sp<IBinder> unitTestService, const std::vector<const char*>& args) { + int inFd[2] = {-1, -1}; + int outFd[2] = {-1, -1}; + int errFd[2] = {-1, -1}; + + EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, inFd)); + EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, outFd)); + EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, errFd)); + + sp<MyShellCallback> cb = new MyShellCallback(); + sp<MyResultReceiver> resultReceiver = new MyResultReceiver(); + + Vector<String16> argsVec; + for (int i = 0; i < args.size(); i++) { + argsVec.add(String16(args[i])); + } + status_t error = IBinder::shellCommand(unitTestService, inFd[0], outFd[0], errFd[0], argsVec, + cb, resultReceiver); + EXPECT_EQ(error, android::OK); + + status_t res = resultReceiver->waitForResult(); + EXPECT_EQ(res, android::OK); + + close(inFd[0]); + close(inFd[1]); + close(outFd[0]); + close(errFd[0]); + close(errFd[1]); + + std::string ret; + EXPECT_TRUE(ReadFdToString(outFd[1], &ret)); + close(outFd[1]); + return ret; +} + +TEST(NdkBinder, UseHandleShellCommand) { + static const sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<IBinder> testService = sm->getService(String16(kBinderNdkUnitTestService)); + + EXPECT_EQ("", shellCmdToString(testService, {})); + EXPECT_EQ("", shellCmdToString(testService, {"", ""})); + EXPECT_EQ("Hello world!", shellCmdToString(testService, {"Hello ", "world!"})); + EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"})); +} + int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 5a7f9a97fa..3ee818734a 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -40,7 +40,7 @@ cc_test { }, srcs: ["binderDriverInterfaceTest.cpp"], - test_suites: ["device-tests"], + test_suites: ["device-tests", "vts-core"], } cc_test { @@ -69,7 +69,7 @@ cc_test { "libbinder", "libutils", ], - test_suites: ["device-tests"], + test_suites: ["device-tests", "vts-core"], require_root: true, } @@ -131,7 +131,7 @@ cc_test { "liblog", "libutils", ], - test_suites: ["device-tests"], + test_suites: ["device-tests", "vts-core"], require_root: true, } diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 1465296bac..05a462e026 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -58,6 +58,7 @@ static std::vector<std::vector<uint32_t>> gPolicyCpus; static std::set<uint32_t> gAllFreqs; static unique_fd gTisMapFd; static unique_fd gConcurrentMapFd; +static unique_fd gUidLastUpdateMapFd; static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) { std::string data; @@ -144,6 +145,10 @@ static bool initGlobals() { unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; if (gConcurrentMapFd < 0) return false; + gUidLastUpdateMapFd = + unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_last_update_map")}; + if (gUidLastUpdateMapFd < 0) return false; + gInitialized = true; return true; } @@ -263,6 +268,18 @@ std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t ui return out; } +static std::optional<bool> uidUpdatedSince(uint32_t uid, uint64_t lastUpdate, + uint64_t *newLastUpdate) { + uint64_t uidLastUpdate; + if (findMapEntry(gUidLastUpdateMapFd, &uid, &uidLastUpdate)) return {}; + // Updates that occurred during the previous read may have been missed. To mitigate + // this, don't ignore entries updated up to 1s before *lastUpdate + constexpr uint64_t NSEC_PER_SEC = 1000000000; + if (uidLastUpdate + NSEC_PER_SEC < lastUpdate) return false; + if (uidLastUpdate > *newLastUpdate) *newLastUpdate = uidLastUpdate; + return true; +} + // Retrieve the times in ns that each uid spent running at each CPU freq. // Return contains no value on error, otherwise it contains a map from uids to vectors of vectors // using the format: @@ -271,6 +288,14 @@ std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t ui // where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq. std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> getUidsCpuFreqTimes() { + return getUidsUpdatedCpuFreqTimes(nullptr); +} + +// Retrieve the times in ns that each uid spent running at each CPU freq, excluding UIDs that have +// not run since before lastUpdate. +// Return format is the same as getUidsCpuFreqTimes() +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> +getUidsUpdatedCpuFreqTimes(uint64_t *lastUpdate) { if (!gInitialized && !initGlobals()) return {}; time_key_t key, prevKey; std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map; @@ -282,8 +307,14 @@ getUidsCpuFreqTimes() { std::vector<std::vector<uint64_t>> mapFormat; for (const auto &freqList : gPolicyFreqs) mapFormat.emplace_back(freqList.size(), 0); + uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0; std::vector<tis_val_t> vals(gNCpus); do { + if (lastUpdate) { + auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate); + if (!uidUpdated.has_value()) return {}; + if (!*uidUpdated) continue; + } if (findMapEntry(gTisMapFd, &key, vals.data())) return {}; if (map.find(key.uid) == map.end()) map.emplace(key.uid, mapFormat); @@ -299,8 +330,9 @@ getUidsCpuFreqTimes() { } } prevKey = key; - } while (!getNextMapKey(gTisMapFd, &prevKey, &key)); + } while (prevKey = key, !getNextMapKey(gTisMapFd, &prevKey, &key)); if (errno != ENOENT) return {}; + if (lastUpdate && newLastUpdate > *lastUpdate) *lastUpdate = newLastUpdate; return map; } @@ -365,6 +397,15 @@ std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) // where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent // running on the ith cluster, concurrently with tasks on j other cpus in the same cluster. std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes() { + return getUidsUpdatedConcurrentTimes(nullptr); +} + +// Retrieve the times in ns that each uid spent running concurrently with each possible number of +// other tasks on each cluster (policy times) and overall (active times), excluding UIDs that have +// not run since before lastUpdate. +// Return format is the same as getUidsConcurrentTimes() +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsUpdatedConcurrentTimes( + uint64_t *lastUpdate) { if (!gInitialized && !initGlobals()) return {}; time_key_t key, prevKey; std::unordered_map<uint32_t, concurrent_time_t> ret; @@ -379,7 +420,13 @@ std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrent std::vector<concurrent_val_t> vals(gNCpus); std::vector<uint64_t>::iterator activeBegin, activeEnd, policyBegin, policyEnd; + uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0; do { + if (lastUpdate) { + auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate); + if (!uidUpdated.has_value()) return {}; + if (!*uidUpdated) continue; + } if (findMapEntry(gConcurrentMapFd, &key, vals.data())) return {}; if (ret.find(key.uid) == ret.end()) ret.emplace(key.uid, retFormat); @@ -405,8 +452,7 @@ std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrent std::plus<uint64_t>()); } } - prevKey = key; - } while (!getNextMapKey(gConcurrentMapFd, &prevKey, &key)); + } while (prevKey = key, !getNextMapKey(gConcurrentMapFd, &prevKey, &key)); if (errno != ENOENT) return {}; for (const auto &[key, value] : ret) { if (!verifyConcurrentTimes(value)) { @@ -414,6 +460,7 @@ std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrent if (val.has_value()) ret[key] = val.value(); } } + if (lastUpdate && newLastUpdate > *lastUpdate) *lastUpdate = newLastUpdate; return ret; } @@ -446,6 +493,8 @@ bool clearUidTimes(uint32_t uid) { return false; if (deleteMapEntry(gConcurrentMapFd, &key) && errno != ENOENT) return false; } + + if (deleteMapEntry(gUidLastUpdateMapFd, &uid) && errno != ENOENT) return false; return true; } diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h index 49469d8e04..b7600f59e0 100644 --- a/libs/cputimeinstate/cputimeinstate.h +++ b/libs/cputimeinstate/cputimeinstate.h @@ -26,6 +26,8 @@ bool startTrackingUidTimes(); std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid); std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> getUidsCpuFreqTimes(); +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> + getUidsUpdatedCpuFreqTimes(uint64_t *lastUpdate); std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs(); struct concurrent_time_t { @@ -35,6 +37,8 @@ struct concurrent_time_t { std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry = true); std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes(); +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> + getUidsUpdatedConcurrentTimes(uint64_t *lastUpdate); bool clearUidTimes(unsigned int uid); } // namespace bpf diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 23d87fd646..ea2a2008b7 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -115,71 +115,169 @@ TEST(TimeInStateTest, SingleUidTimesConsistent) { } TEST(TimeInStateTest, AllUidTimeInState) { - vector<size_t> sizes; - auto map = getUidsCpuFreqTimes(); - ASSERT_TRUE(map.has_value()); + uint64_t zero = 0; + auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); - ASSERT_FALSE(map->empty()); + ASSERT_FALSE(map->empty()); - auto firstEntry = map->begin()->second; - for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); + vector<size_t> sizes; + auto firstEntry = map->begin()->second; + for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); - for (const auto &vec : *map) { - ASSERT_EQ(vec.second.size(), sizes.size()); - for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]); + for (const auto &vec : *map) { + ASSERT_EQ(vec.second.size(), sizes.size()); + for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]); + } } } -TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { - auto map = getUidsCpuFreqTimes(); - ASSERT_TRUE(map.has_value()); - ASSERT_FALSE(map->empty()); +void TestCheckUpdate(const std::vector<std::vector<uint64_t>> &before, + const std::vector<std::vector<uint64_t>> &after) { + ASSERT_EQ(before.size(), after.size()); + uint64_t sumBefore = 0, sumAfter = 0; + for (size_t i = 0; i < before.size(); ++i) { + ASSERT_EQ(before[i].size(), after[i].size()); + for (size_t j = 0; j < before[i].size(); ++j) { + // Times should never decrease + ASSERT_LE(before[i][j], after[i][j]); + } + sumBefore += std::accumulate(before[i].begin(), before[i].end(), (uint64_t)0); + sumAfter += std::accumulate(after[i].begin(), after[i].end(), (uint64_t)0); + } + ASSERT_LE(sumBefore, sumAfter); + ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC); +} - for (const auto &kv : *map) { - uint32_t uid = kv.first; - auto times1 = kv.second; - auto times2 = getUidCpuFreqTimes(uid); - ASSERT_TRUE(times2.has_value()); - - ASSERT_EQ(times1.size(), times2->size()); - for (uint32_t i = 0; i < times1.size(); ++i) { - ASSERT_EQ(times1[i].size(), (*times2)[i].size()); - for (uint32_t j = 0; j < times1[i].size(); ++j) { - ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC); +TEST(TimeInStateTest, AllUidUpdatedTimeInState) { + uint64_t lastUpdate = 0; + auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate); + ASSERT_TRUE(map1.has_value()); + ASSERT_FALSE(map1->empty()); + ASSERT_NE(lastUpdate, (uint64_t)0); + uint64_t oldLastUpdate = lastUpdate; + + // Sleep briefly to trigger a context switch, ensuring we see at least one update. + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep (&ts, NULL); + + auto map2 = getUidsUpdatedCpuFreqTimes(&lastUpdate); + ASSERT_TRUE(map2.has_value()); + ASSERT_FALSE(map2->empty()); + ASSERT_NE(lastUpdate, oldLastUpdate); + + bool someUidsExcluded = false; + for (const auto &[uid, v] : *map1) { + if (map2->find(uid) == map2->end()) { + someUidsExcluded = true; + break; + } + } + ASSERT_TRUE(someUidsExcluded); + + for (const auto &[uid, newTimes] : *map2) { + ASSERT_NE(map1->find(uid), map1->end()); + ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid], newTimes)); + } +} + +TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { + uint64_t zero = 0; + auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + ASSERT_FALSE(map->empty()); + + for (const auto &kv : *map) { + uint32_t uid = kv.first; + auto times1 = kv.second; + auto times2 = getUidCpuFreqTimes(uid); + ASSERT_TRUE(times2.has_value()); + + ASSERT_EQ(times1.size(), times2->size()); + for (uint32_t i = 0; i < times1.size(); ++i) { + ASSERT_EQ(times1[i].size(), (*times2)[i].size()); + for (uint32_t j = 0; j < times1[i].size(); ++j) { + ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC); + } } } } } TEST(TimeInStateTest, AllUidConcurrentTimes) { - auto map = getUidsConcurrentTimes(); - ASSERT_TRUE(map.has_value()); - ASSERT_FALSE(map->empty()); - - auto firstEntry = map->begin()->second; - for (const auto &kv : *map) { - ASSERT_EQ(kv.second.active.size(), firstEntry.active.size()); - ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size()); - for (size_t i = 0; i < kv.second.policy.size(); ++i) { - ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size()); + uint64_t zero = 0; + auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + ASSERT_FALSE(map->empty()); + + auto firstEntry = map->begin()->second; + for (const auto &kv : *map) { + ASSERT_EQ(kv.second.active.size(), firstEntry.active.size()); + ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size()); + for (size_t i = 0; i < kv.second.policy.size(); ++i) { + ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size()); + } } } } -TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) { - auto map = getUidsConcurrentTimes(); - ASSERT_TRUE(map.has_value()); - for (const auto &kv : *map) { - uint32_t uid = kv.first; - auto times1 = kv.second; - auto times2 = getUidConcurrentTimes(uid); - ASSERT_TRUE(times2.has_value()); - for (uint32_t i = 0; i < times1.active.size(); ++i) { - ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC); +TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) { + uint64_t lastUpdate = 0; + auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate); + ASSERT_TRUE(map1.has_value()); + ASSERT_FALSE(map1->empty()); + ASSERT_NE(lastUpdate, (uint64_t)0); + + // Sleep briefly to trigger a context switch, ensuring we see at least one update. + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep (&ts, NULL); + + uint64_t oldLastUpdate = lastUpdate; + auto map2 = getUidsUpdatedConcurrentTimes(&lastUpdate); + ASSERT_TRUE(map2.has_value()); + ASSERT_FALSE(map2->empty()); + ASSERT_NE(lastUpdate, oldLastUpdate); + + bool someUidsExcluded = false; + for (const auto &[uid, v] : *map1) { + if (map2->find(uid) == map2->end()) { + someUidsExcluded = true; + break; } - for (uint32_t i = 0; i < times1.policy.size(); ++i) { - for (uint32_t j = 0; j < times1.policy[i].size(); ++j) { - ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC); + } + ASSERT_TRUE(someUidsExcluded); + + for (const auto &[uid, newTimes] : *map2) { + ASSERT_NE(map1->find(uid), map1->end()); + ASSERT_NO_FATAL_FAILURE(TestCheckUpdate({(*map1)[uid].active},{newTimes.active})); + ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid].policy, newTimes.policy)); + } +} + +TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) { + uint64_t zero = 0; + auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + for (const auto &kv : *map) { + uint32_t uid = kv.first; + auto times1 = kv.second; + auto times2 = getUidConcurrentTimes(uid); + ASSERT_TRUE(times2.has_value()); + for (uint32_t i = 0; i < times1.active.size(); ++i) { + ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC); + } + for (uint32_t i = 0; i < times1.policy.size(); ++i) { + for (uint32_t j = 0; j < times1.policy[i].size(); ++j) { + ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC); + } } } } @@ -242,45 +340,51 @@ TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) { } TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) { - auto map = getUidsCpuFreqTimes(); - ASSERT_TRUE(map.has_value()); - - bool foundLargeValue = false; - for (const auto &kv : *map) { - for (const auto &timeVec : kv.second) { - for (const auto &time : timeVec) { - ASSERT_LE(time, NSEC_PER_YEAR); - if (time > UINT32_MAX) foundLargeValue = true; + uint64_t zero = 0; + auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + + bool foundLargeValue = false; + for (const auto &kv : *map) { + for (const auto &timeVec : kv.second) { + for (const auto &time : timeVec) { + ASSERT_LE(time, NSEC_PER_YEAR); + if (time > UINT32_MAX) foundLargeValue = true; + } } } + // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using + // uint64_t as expected, we should have some times higher than that. + ASSERT_TRUE(foundLargeValue); } - // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using - // uint64_t as expected, we should have some times higher than that. - ASSERT_TRUE(foundLargeValue); } TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { - auto concurrentMap = getUidsConcurrentTimes(); - ASSERT_TRUE(concurrentMap); - - bool activeFoundLargeValue = false; - bool policyFoundLargeValue = false; - for (const auto &kv : *concurrentMap) { - for (const auto &time : kv.second.active) { - ASSERT_LE(time, NSEC_PER_YEAR); - if (time > UINT32_MAX) activeFoundLargeValue = true; - } - for (const auto &policyTimeVec : kv.second.policy) { - for (const auto &time : policyTimeVec) { + uint64_t zero = 0; + auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; + for (const auto &concurrentMap : maps) { + ASSERT_TRUE(concurrentMap); + + bool activeFoundLargeValue = false; + bool policyFoundLargeValue = false; + for (const auto &kv : *concurrentMap) { + for (const auto &time : kv.second.active) { ASSERT_LE(time, NSEC_PER_YEAR); - if (time > UINT32_MAX) policyFoundLargeValue = true; + if (time > UINT32_MAX) activeFoundLargeValue = true; + } + for (const auto &policyTimeVec : kv.second.policy) { + for (const auto &time : policyTimeVec) { + ASSERT_LE(time, NSEC_PER_YEAR); + if (time > UINT32_MAX) policyFoundLargeValue = true; + } } } + // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using + // uint64_t as expected, we should have some times higher than that. + ASSERT_TRUE(activeFoundLargeValue); + ASSERT_TRUE(policyFoundLargeValue); } - // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using - // uint64_t as expected, we should have some times higher than that. - ASSERT_TRUE(activeFoundLargeValue); - ASSERT_TRUE(policyFoundLargeValue); } TEST(TimeInStateTest, AllUidTimesConsistent) { diff --git a/services/stats/Android.bp b/services/stats/Android.bp new file mode 100644 index 0000000000..1ce0524299 --- /dev/null +++ b/services/stats/Android.bp @@ -0,0 +1,22 @@ +cc_library_shared { + name: "libstatshidl", + srcs: [ + "StatsHal.cpp", + ], + cflags: ["-Wall", "-Werror"], + shared_libs: [ + "android.frameworks.stats@1.0", + "libhidlbase", + "liblog", + "libstatslog", + "libstatssocket", + "libutils", + ], + export_include_dirs: [ + "include/", + ], + local_include_dirs: [ + "include/stats", + ], + vintf_fragments: ["android.frameworks.stats@1.0-service.xml"] +} diff --git a/services/stats/OWNERS b/services/stats/OWNERS new file mode 100644 index 0000000000..a61babf32e --- /dev/null +++ b/services/stats/OWNERS @@ -0,0 +1,9 @@ +jeffreyhuang@google.com +joeo@google.com +jtnguyen@google.com +muhammadq@google.com +ruchirr@google.com +singhtejinder@google.com +tsaichristine@google.com +yaochen@google.com +yro@google.com diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp new file mode 100644 index 0000000000..b775431cff --- /dev/null +++ b/services/stats/StatsHal.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG false // STOPSHIP if true +#define LOG_TAG "StatsHal" + +#include <log/log.h> +#include <statslog.h> + +#include "StatsHal.h" + +namespace android { +namespace frameworks { +namespace stats { +namespace V1_0 { +namespace implementation { + +StatsHal::StatsHal() {} + +hardware::Return<void> StatsHal::reportSpeakerImpedance( + const SpeakerImpedance& speakerImpedance) { + android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED, + speakerImpedance.speakerLocation, speakerImpedance.milliOhms); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportHardwareFailed(const HardwareFailed& hardwareFailed) { + android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType), + hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode)); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportPhysicalDropDetected( + const PhysicalDropDetected& physicalDropDetected) { + android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED, + int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak, + physicalDropDetected.freefallDuration); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportChargeCycles(const ChargeCycles& chargeCycles) { + std::vector<int32_t> buckets = chargeCycles.cycleBucket; + int initialSize = buckets.size(); + for (int i = 0; i < 10 - initialSize; i++) { + buckets.push_back(-1); // Push -1 for buckets that do not exist. + } + android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1], + buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8], + buckets[9]); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportBatteryHealthSnapshot( + const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) { + android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT, + int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC, + batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA, + batteryHealthSnapshotArgs.openCircuitVoltageMicroV, + batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportSlowIo(const SlowIo& slowIo) { + android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportBatteryCausedShutdown( + const BatteryCausedShutdown& batteryCausedShutdown) { + android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN, + batteryCausedShutdown.voltageMicroV); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportUsbPortOverheatEvent( + const UsbPortOverheatEvent& usbPortOverheatEvent) { + android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED, + usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC, + usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis, + usbPortOverheatEvent.timeToInactive); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportSpeechDspStat( + const SpeechDspStat& speechDspStat) { + android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED, + speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis, + speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { + ALOGW("reportVendorAtom unsupported"); + std::string reverDomainName = vendorAtom.reverseDomainName; + return hardware::Void(); +} + +} // namespace implementation +} // namespace V1_0 +} // namespace stats +} // namespace frameworks +} // namespace android diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml new file mode 100644 index 0000000000..bb02f66a28 --- /dev/null +++ b/services/stats/android.frameworks.stats@1.0-service.xml @@ -0,0 +1,11 @@ +<manifest version="1.0" type="framework"> + <hal> + <name>android.frameworks.stats</name> + <transport>hwbinder</transport> + <version>1.0</version> + <interface> + <name>IStats</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/services/stats/include/stats/StatsHal.h b/services/stats/include/stats/StatsHal.h new file mode 100644 index 0000000000..ad14263426 --- /dev/null +++ b/services/stats/include/stats/StatsHal.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/frameworks/stats/1.0/IStats.h> +#include <android/frameworks/stats/1.0/types.h> + +using namespace android::frameworks::stats::V1_0; + +namespace android { +namespace frameworks { +namespace stats { +namespace V1_0 { +namespace implementation { + +using android::hardware::Return; + +/** +* Implements the Stats HAL +*/ +class StatsHal : public IStats { +public: + StatsHal(); + + /** + * Binder call to get SpeakerImpedance atom. + */ + virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override; + + /** + * Binder call to get HardwareFailed atom. + */ + virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override; + + /** + * Binder call to get PhysicalDropDetected atom. + */ + virtual Return<void> reportPhysicalDropDetected( + const PhysicalDropDetected& physicalDropDetected) override; + + /** + * Binder call to get ChargeCyclesReported atom. + */ + virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override; + + /** + * Binder call to get BatteryHealthSnapshot atom. + */ + virtual Return<void> reportBatteryHealthSnapshot( + const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override; + + /** + * Binder call to get SlowIo atom. + */ + virtual Return<void> reportSlowIo(const SlowIo& slowIo) override; + + /** + * Binder call to get BatteryCausedShutdown atom. + */ + virtual Return<void> reportBatteryCausedShutdown( + const BatteryCausedShutdown& batteryCausedShutdown) override; + + /** + * Binder call to get UsbPortOverheatEvent atom. + */ + virtual Return<void> reportUsbPortOverheatEvent( + const UsbPortOverheatEvent& usbPortOverheatEvent) override; + + /** + * Binder call to get Speech DSP state atom. + */ + virtual Return<void> reportSpeechDspStat( + const SpeechDspStat& speechDspStat) override; + + /** + * Binder call to get vendor atom. + */ + virtual Return<void> reportVendorAtom(const VendorAtom& vendorAtom) override; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace stats +} // namespace frameworks +} // namespace android |