diff options
83 files changed, 2525 insertions, 584 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 36a53bbbe2..e048a19145 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -7,16 +7,19 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp cmds/idlcli/ include/input/ libs/binder/ndk/ + libs/binderthreadstate/ libs/graphicsenv/ libs/gui/ libs/input/ libs/renderengine/ libs/ui/ libs/vr/ + opengl/libs/ services/bufferhub/ services/inputflinger/ services/surfaceflinger/ services/vr/ + vulkan/ [Hook Scripts] owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$" diff --git a/aidl/binder/android/os/PersistableBundle.aidl b/aidl/binder/android/os/PersistableBundle.aidl index 94e8607630..493ecb414c 100644 --- a/aidl/binder/android/os/PersistableBundle.aidl +++ b/aidl/binder/android/os/PersistableBundle.aidl @@ -17,4 +17,4 @@ package android.os; -parcelable PersistableBundle cpp_header "binder/PersistableBundle.h"; +@JavaOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h"; diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 5186ad3a1f..cf75bbab3b 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -854,7 +854,6 @@ static bool setUpUserspaceTracing() tags |= c.tags; } } - ok &= setTagsProperty(tags); bool coreServicesTagEnabled = false; for (size_t i = 0; i < arraysize(k_categories); i++) { @@ -876,9 +875,11 @@ static bool setUpUserspaceTracing() packageList += android::base::GetProperty(k_coreServicesProp, ""); } ok &= setAppCmdlineProperty(&packageList[0]); + ok &= setTagsProperty(tags); +#if !ATRACE_SHMEM ok &= pokeBinderServices(); pokeHalServices(); - +#endif if (g_tracePdx) { ok &= ServiceUtility::PokeServices(); } diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp index 7b4aeb2cc2..8dad47502f 100644 --- a/cmds/cmd/cmd.cpp +++ b/cmds/cmd/cmd.cpp @@ -223,7 +223,8 @@ int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, Te sp<MyResultReceiver> result = new MyResultReceiver(); #if DEBUG - ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", cmd, in, out, err); + ALOGD("cmd: Invoking %.*s in=%d, out=%d, err=%d", + static_cast<int>(cmd.size()), cmd.data(), in, out, err); #endif // TODO: block until a result is returned to MyResultReceiver. diff --git a/cmds/dumpstate/DumpstateInternal.h b/cmds/dumpstate/DumpstateInternal.h index 10db5d65e3..c1ec55ee61 100644 --- a/cmds/dumpstate/DumpstateInternal.h +++ b/cmds/dumpstate/DumpstateInternal.h @@ -49,6 +49,7 @@ // TODO: use functions from <chrono> instead const uint64_t NANOS_PER_SEC = 1000000000; +const uint64_t NANOS_PER_MILLI = 1000000; uint64_t Nanotime(); // Switches to non-root user and group. diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 498f7270bc..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 @@ -156,6 +161,8 @@ void add_mountinfo(); #define WLUTIL "/vendor/xbin/wlutil" #define WMTRACE_DATA_DIR "/data/misc/wmtrace" #define OTA_METADATA_DIR "/metadata/ota" +#define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log" +#define LINKERCONFIG_DIR "/linkerconfig" // TODO(narayan): Since this information has to be kept in sync // with tombstoned, we should just put it in a common header. @@ -661,7 +668,7 @@ UserConsentResult Dumpstate::ConsentCallback::getResult() { } uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const { - return Nanotime() - start_time_; + return (Nanotime() - start_time_) / NANOS_PER_MILLI; } void Dumpstate::PrintHeader() const { @@ -889,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"); @@ -907,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"}); @@ -1412,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()) { @@ -1517,6 +1531,9 @@ static Dumpstate::RunStatus dumpstate() { // This differs from the usual dumpsys stats, which is the stats report data. RunDumpsys("STATSDSTATS", {"stats", "--metadata"}); + // Add linker configuration directory + ds.AddDir(LINKERCONFIG_DIR, true); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport); return Dumpstate::RunStatus::OK; @@ -1552,6 +1569,7 @@ static Dumpstate::RunStatus DumpstateDefault() { ds.AddDir(RECOVERY_DATA_DIR, true); ds.AddDir(UPDATE_ENGINE_LOG_DIR, true); ds.AddDir(LOGPERSIST_DATA_DIR, false); + ds.AddDir(SNAPSHOTCTL_LOG_DIR, false); if (!PropertiesHelper::IsUserBuild()) { ds.AddDir(PROFILE_DATA_DIR_CUR, true); ds.AddDir(PROFILE_DATA_DIR_REF, true); @@ -1595,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); @@ -1605,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"); @@ -1632,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"); @@ -1648,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_); @@ -2271,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; @@ -2821,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/idlcli/Android.bp b/cmds/idlcli/Android.bp index 08a31c1636..402767a426 100644 --- a/cmds/idlcli/Android.bp +++ b/cmds/idlcli/Android.bp @@ -15,6 +15,7 @@ cc_defaults { name: "idlcli-defaults", shared_libs: [ + "android.hardware.vibrator-ndk_platform", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", @@ -24,7 +25,6 @@ cc_defaults { "libhidlbase", "liblog", "libutils", - "vintf-vibrator-ndk_platform", ], cflags: [ "-DLOG_TAG=\"idlcli\"", diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 737c6c9582..0fde31a5cc 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -103,6 +103,9 @@ static constexpr size_t kSha256Size = 32; static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode"; static constexpr const char* kFuseProp = "persist.sys.fuse"; +static constexpr const char* kMntSdcardfs = "/mnt/runtime/default/"; +static constexpr const char* kMntFuse = "/mnt/pass_through/0/"; + namespace { constexpr const char* kDump = "android.permission.DUMP"; @@ -626,10 +629,8 @@ binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::st if (delete_dir_contents(path, true) != 0) { res = error("Failed to delete contents of " + path); } - path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); - if (delete_dir_contents(path, true) != 0) { - res = error("Failed to delete contents of " + path); - } + // Note that we explicitly don't delete OBBs - those are only removed on + // app uninstall. } } } @@ -2799,15 +2800,13 @@ binder::Status InstalldNativeService::invalidateMounts() { std::getline(in, ignored); if (android::base::GetBoolProperty(kFuseProp, false)) { - // TODO(b/146139106): Use sdcardfs mounts on devices running sdcardfs so we don't bypass - // it's VFS cache - if (target.compare(0, 17, "/mnt/pass_through") == 0) { + if (target.find(kMntFuse) == 0) { LOG(DEBUG) << "Found storage mount " << source << " at " << target; mStorageMounts[source] = target; } } else { #if !BYPASS_SDCARDFS - if (target.compare(0, 21, "/mnt/runtime/default/") == 0) { + if (target.find(kMntSdcardfs) == 0) { LOG(DEBUG) << "Found storage mount " << source << " at " << target; mStorageMounts[source] = target; } @@ -2822,17 +2821,6 @@ std::string InstalldNativeService::findDataMediaPath( std::lock_guard<std::recursive_mutex> lock(mMountsLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str()); - if (android::base::GetBoolProperty(kFuseProp, false)) { - // TODO(b/146139106): This is only safe on devices not running sdcardfs where there is no - // risk of bypassing the sdcardfs VFS cache - - // Always use the lower filesystem path on FUSE enabled devices not running sdcardfs - // The upper filesystem path, /mnt/pass_through/<userid>/<vol>/ which was a bind mount - // to the lower filesytem may have been unmounted already when a user is - // removed and the path will now be pointing to a tmpfs without content - return StringPrintf("%s/%u", path.c_str(), userid); - } - auto resolved = mStorageMounts[path]; if (resolved.empty()) { LOG(WARNING) << "Failed to find storage mount for " << path; diff --git a/cmds/installd/QuotaUtils.cpp b/cmds/installd/QuotaUtils.cpp index b238dd36e3..f2abf3aea3 100644 --- a/cmds/installd/QuotaUtils.cpp +++ b/cmds/installd/QuotaUtils.cpp @@ -97,6 +97,26 @@ int64_t GetOccupiedSpaceForUid(const std::string& uuid, uid_t uid) { } } +int64_t GetOccupiedSpaceForProjectId(const std::string& uuid, int projectId) { + const std::string device = FindQuotaDeviceForUuid(uuid); + if (device == "") { + return -1; + } + struct dqblk dq; + if (quotactl(QCMD(Q_GETQUOTA, PRJQUOTA), device.c_str(), projectId, + reinterpret_cast<char*>(&dq)) != 0) { + if (errno != ESRCH) { + PLOG(ERROR) << "Failed to quotactl " << device << " for Project ID " << projectId; + } + return -1; + } else { +#if MEASURE_DEBUG + LOG(DEBUG) << "quotactl() for Project ID " << projectId << " " << dq.dqb_curspace; +#endif + return dq.dqb_curspace; + } +} + int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid) { const std::string device = FindQuotaDeviceForUuid(uuid); if (device == "") { diff --git a/cmds/installd/QuotaUtils.h b/cmds/installd/QuotaUtils.h index 9ad170fcbb..96aca0448e 100644 --- a/cmds/installd/QuotaUtils.h +++ b/cmds/installd/QuotaUtils.h @@ -35,6 +35,8 @@ int64_t GetOccupiedSpaceForUid(const std::string& uuid, uid_t uid); /* Get the current occupied space in bytes for a gid or -1 if fails */ int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid); +/* Get the current occupied space in bytes for a project id or -1 if fails */ +int64_t GetOccupiedSpaceForProjectId(const std::string& uuid, int projectId); } // namespace installd } // namespace android diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING index 287f2d9f41..c6583a1bf4 100644 --- a/cmds/installd/TEST_MAPPING +++ b/cmds/installd/TEST_MAPPING @@ -15,6 +15,10 @@ { "name": "installd_utils_test" }, + // AdoptableHostTest moves packages, part of which is handled by installd + { + "name": "AdoptableHostTest" + }, { "name": "CtsUsesLibraryHostTestCases" }, 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/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index e61eb6e52f..d236f76645 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -104,12 +104,12 @@ TEST_F(UtilsTest, IsValidApkPath_Internal) { EXPECT_EQ(-1, validate_apk_path(badint2)) << badint2 << " should be rejected as a invalid path"; - // Only one subdir should be allowed. - const char *bad_path3 = TEST_APP_DIR "example.com/subdir/pkg.apk"; + // Should not have more than two sub directories + const char *bad_path3 = TEST_APP_DIR "random/example.com/subdir/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path3)) << bad_path3 << " should be rejected as a invalid path"; - const char *bad_path4 = TEST_APP_DIR "example.com/subdir/../pkg.apk"; + const char *bad_path4 = TEST_APP_DIR "random/example.com/subdir/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path4)) << bad_path4 << " should be rejected as a invalid path"; @@ -120,6 +120,7 @@ TEST_F(UtilsTest, IsValidApkPath_Internal) { TEST_F(UtilsTest, IsValidApkPath_TopDir) { EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example")); + EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example")); EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example")); EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example")); EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example")); @@ -127,6 +128,7 @@ TEST_F(UtilsTest, IsValidApkPath_TopDir) { TEST_F(UtilsTest, IsValidApkPath_TopFile) { EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk")); + EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example/base.apk")); EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk")); EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk")); EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk")); @@ -134,6 +136,7 @@ TEST_F(UtilsTest, IsValidApkPath_TopFile) { TEST_F(UtilsTest, IsValidApkPath_OatDir) { EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat")); EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat")); @@ -141,6 +144,7 @@ TEST_F(UtilsTest, IsValidApkPath_OatDir) { TEST_F(UtilsTest, IsValidApkPath_OatDirDir) { EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64")); EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64")); @@ -148,6 +152,7 @@ TEST_F(UtilsTest, IsValidApkPath_OatDirDir) { TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) { EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64/base.odex")); EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex")); @@ -164,6 +169,10 @@ TEST_F(UtilsTest, IsValidApkPath_Private) { EXPECT_EQ(0, validate_apk_path(path2)) << path2 << " should be allowed as a valid path"; + const char *path3 = TEST_APP_DIR "random/example.com/example.apk"; + EXPECT_EQ(0, validate_apk_path(path3)) + << path3 << " should be allowed as a valid path"; + const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk"; EXPECT_EQ(-1, validate_apk_path(badpriv1)) << badpriv1 << " should be rejected as a invalid path"; @@ -172,16 +181,16 @@ TEST_F(UtilsTest, IsValidApkPath_Private) { EXPECT_EQ(-1, validate_apk_path(badpriv2)) << badpriv2 << " should be rejected as a invalid path"; - // Only one subdir should be allowed. - const char *bad_path3 = TEST_APP_PRIVATE_DIR "example.com/subdir/pkg.apk"; + // Only one or two subdir should be allowed. + const char *bad_path3 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path3)) << bad_path3 << " should be rejected as a invalid path"; - const char *bad_path4 = TEST_APP_PRIVATE_DIR "example.com/subdir/../pkg.apk"; + const char *bad_path4 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/../pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path4)) << bad_path4 << " should be rejected as a invalid path"; - const char *bad_path5 = TEST_APP_PRIVATE_DIR "example.com1/../example.com2/pkg.apk"; + const char *bad_path5 = TEST_APP_PRIVATE_DIR "random/example.com1/../example.com2/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path5)) << bad_path5 << " should be rejected as a invalid path"; } @@ -229,10 +238,16 @@ TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeSingleFail) { << badasec6 << " should be rejected as a invalid path"; } -TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) { - const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk"; - EXPECT_EQ(-1, validate_apk_path(badasec7)) - << badasec7 << " should be rejected as a invalid path"; +TEST_F(UtilsTest, IsValidApkPath_TwoSubdir) { + const char *badasec7 = TEST_ASEC_DIR "random/com.example.asec/pkg.apk"; + EXPECT_EQ(0, validate_apk_path(badasec7)) + << badasec7 << " should be allowed as a valid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_ThreeSubdirFail) { + const char *badasec8 = TEST_ASEC_DIR "random/com.example.asec/subdir/pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(badasec8)) + << badasec8 << " should be rejcted as an invalid path"; } TEST_F(UtilsTest, CheckSystemApp_Dir1) { @@ -511,8 +526,8 @@ TEST_F(UtilsTest, ValidateApkPath) { EXPECT_EQ(0, validate_apk_path("/data/app/com.example")); EXPECT_EQ(0, validate_apk_path("/data/app/com.example/file")); EXPECT_EQ(0, validate_apk_path("/data/app/com.example//file")); - EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/")); - EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/file")); + EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/")); + EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir//file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir/file")); @@ -527,8 +542,10 @@ TEST_F(UtilsTest, ValidateApkPathSubdirs) { EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/file")); EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/file")); EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir//file")); - EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file")); - EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file")); + EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file")); + EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file")); + EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir/file")); + EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir//file")); } TEST_F(UtilsTest, MatchExtension_Valid) { diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 4eb1df0b2e..2f79552d1c 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -945,11 +945,11 @@ static int validate_apk_path_internal(const std::string& path, int maxSubdirs) { } int validate_apk_path(const char* path) { - return validate_apk_path_internal(path, 1 /* maxSubdirs */); + return validate_apk_path_internal(path, 2 /* maxSubdirs */); } int validate_apk_path_subdirs(const char* path) { - return validate_apk_path_internal(path, 3 /* maxSubdirs */); + return validate_apk_path_internal(path, 4 /* maxSubdirs */); } int ensure_config_user_dirs(userid_t userid) { diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index ae74ac3847..abe64365f3 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -423,11 +423,12 @@ ssize_t ServiceManager::Service::getNodeStrongRefCount() { void ServiceManager::handleClientCallbacks() { for (const auto& [name, service] : mNameToService) { - handleServiceClientCallback(name); + handleServiceClientCallback(name, true); } } -ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName) { +ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName, + bool isCalledOnInterval) { auto serviceIt = mNameToService.find(serviceName); if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) { return -1; @@ -451,14 +452,17 @@ ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceNa service.guaranteeClient = false; } - if (hasClients && !service.hasClients) { - // client was retrieved in some other way - sendClientCallbackNotifications(serviceName, true); - } + // only send notifications if this was called via the interval checking workflow + if (isCalledOnInterval) { + if (hasClients && !service.hasClients) { + // client was retrieved in some other way + sendClientCallbackNotifications(serviceName, true); + } - // there are no more clients, but the callback has not been called yet - if (!hasClients && service.hasClients) { - sendClientCallbackNotifications(serviceName, false); + // there are no more clients, but the callback has not been called yet + if (!hasClients && service.hasClients) { + sendClientCallbackNotifications(serviceName, false); + } } return count; @@ -518,7 +522,7 @@ Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IB return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } - int clients = handleServiceClientCallback(name); + int clients = handleServiceClientCallback(name, false); // clients < 0: feature not implemented or other error. Assume clients. // Otherwise: @@ -527,7 +531,7 @@ Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IB // So, if clients > 2, then at least one other service on the system must hold a refcount. if (clients < 0 || clients > 2) { // client callbacks are either disabled or there are other clients - LOG(INFO) << "Tried to unregister " << name << " but there are clients: " << clients; + LOG(INFO) << "Tried to unregister " << name << ", but there are clients: " << clients; return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h index 77f52506b9..a2fc5a84c5 100644 --- a/cmds/servicemanager/ServiceManager.h +++ b/cmds/servicemanager/ServiceManager.h @@ -75,7 +75,7 @@ private: void removeRegistrationCallback(const wp<IBinder>& who, ServiceCallbackMap::iterator* it, bool* found); - ssize_t handleServiceClientCallback(const std::string& serviceName); + ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval); // Also updates mHasClients (of what the last callback was) void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients); // removes a callback from mNameToClientCallback, deleting the entry if the vector is empty diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp index 9cf014380c..8ac044c091 100644 --- a/libs/adbd_auth/Android.bp +++ b/libs/adbd_auth/Android.bp @@ -20,11 +20,14 @@ cc_library { "-Wthread-safety", "-Werror", ], + stl: "libc++_static", + srcs: ["adbd_auth.cpp"], export_include_dirs: ["include"], version_script: "libadbd_auth.map.txt", stubs: { + versions: ["1"], symbol_file: "libadbd_auth.map.txt", }, @@ -36,7 +39,7 @@ cc_library { } }, - shared_libs: [ + static_libs: [ "libbase", "libcutils", "liblog", diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp index 64791098ee..a9c23110c9 100644 --- a/libs/adbd_auth/adbd_auth.cpp +++ b/libs/adbd_auth/adbd_auth.cpp @@ -178,6 +178,10 @@ public: this->callbacks_.key_authorized(arg, id); this->dispatched_prompt_ = std::nullopt; + + // We need to dispatch pending prompts here upon success as well, + // since we might have multiple queued prompts. + DispatchPendingPrompt(); } else if (packet[0] == 'N' && packet[1] == 'O') { CHECK_EQ(2UL, packet.length()); // TODO: Do we want a callback if the key is denied? diff --git a/libs/android_runtime_lazy/android_runtime_lazy.cpp b/libs/android_runtime_lazy/android_runtime_lazy.cpp index 98d8e8a511..8062be676d 100644 --- a/libs/android_runtime_lazy/android_runtime_lazy.cpp +++ b/libs/android_runtime_lazy/android_runtime_lazy.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "ANDROID_RUNTIME_LAZY" #include "android_runtime/AndroidRuntime.h" +#include "android_os_Parcel.h" #include "android_util_Binder.h" #include <dlfcn.h> @@ -28,12 +29,18 @@ namespace { std::once_flag loadFlag; typedef JNIEnv* (*getJNIEnv_t)(); + +// android_util_Binder.h typedef sp<IBinder> (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj); typedef jobject (*javaObjectForIBinder_t)(JNIEnv* env, const sp<IBinder>& val); +// android_os_Parcel.h +typedef Parcel* (*parcelForJavaObject_t)(JNIEnv* env, jobject obj); + getJNIEnv_t _getJNIEnv; ibinderForJavaObject_t _ibinderForJavaObject; javaObjectForIBinder_t _javaObjectForIBinder; +parcelForJavaObject_t _parcelForJavaObject; void load() { std::call_once(loadFlag, []() { @@ -64,6 +71,13 @@ void load() { ALOGW("Could not find javaObjectForIBinder."); // no return } + + _parcelForJavaObject = reinterpret_cast<parcelForJavaObject_t>( + dlsym(handle, "_ZN7android19parcelForJavaObjectEP7_JNIEnvP8_jobject")); + if (_parcelForJavaObject == nullptr) { + ALOGW("Could not find parcelForJavaObject."); + // no return + } }); } @@ -95,4 +109,12 @@ jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) { return _javaObjectForIBinder(env, val); } +Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) { + load(); + if (_parcelForJavaObject == nullptr) { + return nullptr; + } + return _parcelForJavaObject(env, obj); +} + } // namespace android diff --git a/libs/android_runtime_lazy/include/android_os_Parcel.h b/libs/android_runtime_lazy/include/android_os_Parcel.h new file mode 100644 index 0000000000..19b094d02a --- /dev/null +++ b/libs/android_runtime_lazy/include/android_os_Parcel.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/Parcel.h> +#include "jni.h" + +namespace android { + +// The name of this file is same with the file in frameworks/base/core/jni/ +// This is intentional to make the client use these exported functions +// in the same way with the original. + +Parcel* parcelForJavaObject(JNIEnv* env, jobject obj); + +} // namespace android diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 079dd82ecd..bc541f4d31 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -73,7 +73,6 @@ cc_library { // or dessert updates. Instead, apex users should use libbinder_ndk. apex_available: [ "//apex_available:platform", - "com.android.vndk.current", // TODO(b/139016109) remove these three "com.android.media.swcodec", "test_com.android.media.swcodec", @@ -139,7 +138,6 @@ cc_library { "liblog", "libcutils", "libutils", - "libbinderthreadstate", ], header_libs: [ @@ -172,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/BpBinder.cpp b/libs/binder/BpBinder.cpp index 238c9dcc7f..f16c39cdc1 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -435,7 +435,8 @@ void BpBinder::onLastStrongRef(const void* /*id*/) Vector<Obituary>* obits = mObituaries; if(obits != nullptr) { if (!obits->isEmpty()) { - ALOGI("onLastStrongRef automatically unlinking death recipients"); + ALOGI("onLastStrongRef automatically unlinking death recipients: %s", + mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>"); } if (ipc) ipc->clearDeathNotification(mHandle, this); diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp index fb424fdcfb..856a178f58 100644 --- a/libs/binder/BufferedTextOutput.cpp +++ b/libs/binder/BufferedTextOutput.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <binder/BufferedTextOutput.h> +#include "BufferedTextOutput.h" #include <binder/Debug.h> #include <cutils/atomic.h> diff --git a/libs/binder/include/binder/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h index 1b27bb2249..1b27bb2249 100644 --- a/libs/binder/include/binder/BufferedTextOutput.h +++ b/libs/binder/BufferedTextOutput.h diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 4981d7a111..4dcd07a776 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "IPCThreadState" #include <binder/IPCThreadState.h> -#include <binderthreadstate/IPCThreadStateBase.h> #include <binder/Binder.h> #include <binder/BpBinder.h> @@ -803,6 +802,7 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) IPCThreadState::IPCThreadState() : mProcess(ProcessState::self()), + mServingStackPointer(nullptr), mWorkSource(kUnsetWorkSource), mPropagateWorkSource(false), mStrictModePolicy(0), @@ -813,7 +813,6 @@ IPCThreadState::IPCThreadState() clearCaller(); mIn.setDataCapacity(256); mOut.setDataCapacity(256); - mIPCThreadStateBase = IPCThreadStateBase::self(); } IPCThreadState::~IPCThreadState() @@ -1163,9 +1162,6 @@ status_t IPCThreadState::executeCommand(int32_t cmd) "Not enough command data for brTRANSACTION"); if (result != NO_ERROR) break; - //Record the fact that we're in a binder call. - mIPCThreadStateBase->pushCurrentState( - IPCThreadStateBase::CallState::BINDER); Parcel buffer; buffer.ipcSetDataReference( reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), @@ -1173,6 +1169,9 @@ status_t IPCThreadState::executeCommand(int32_t cmd) reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), freeBuffer, this); + const void* origServingStackPointer = mServingStackPointer; + mServingStackPointer = &origServingStackPointer; // anything on the stack + const pid_t origPid = mCallingPid; const char* origSid = mCallingSid; const uid_t origUid = mCallingUid; @@ -1223,7 +1222,6 @@ status_t IPCThreadState::executeCommand(int32_t cmd) error = the_context_object->transact(tr.code, buffer, &reply, tr.flags); } - mIPCThreadStateBase->popCurrentState(); //ALOGI("<<<< TRANSACT from pid %d restore pid %d sid %s uid %d\n", // mCallingPid, origPid, (origSid ? origSid : "<N/A>"), origUid); @@ -1235,6 +1233,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); } + mServingStackPointer = origServingStackPointer; mCallingPid = origPid; mCallingSid = origSid; mCallingUid = origUid; @@ -1290,8 +1289,8 @@ status_t IPCThreadState::executeCommand(int32_t cmd) return result; } -bool IPCThreadState::isServingCall() const { - return mIPCThreadStateBase->getCurrentBinderCallState() == IPCThreadStateBase::CallState::BINDER; +const void* IPCThreadState::getServingStackPointer() const { + return mServingStackPointer; } void IPCThreadState::threadDestructor(void *st) diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 5ca9156fb2..9888b59854 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -85,27 +85,38 @@ private: sp<AidlServiceManager> mTheRealServiceManager; }; +static std::once_flag gSmOnce; +static sp<IServiceManager> gDefaultServiceManager; + sp<IServiceManager> defaultServiceManager() { - static Mutex gDefaultServiceManagerLock; - static sp<IServiceManager> gDefaultServiceManager; - - 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) { + 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__) // IPermissionController is not accessible to vendors diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp index dc9482c536..f064bd77ce 100644 --- a/libs/binder/LazyServiceRegistrar.cpp +++ b/libs/binder/LazyServiceRegistrar.cpp @@ -53,14 +53,13 @@ private: struct Service { sp<IBinder> service; - std::string name; bool allowIsolated; int dumpFlags; }; /** - * Number of services that have been registered. + * Map of registered names and services */ - std::vector<Service> mRegisteredServices; + std::map<std::string, Service> mRegisteredServices; }; bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name, @@ -68,20 +67,24 @@ bool ClientCounterCallback::registerService(const sp<IBinder>& service, const st auto manager = interface_cast<AidlServiceManager>( ProcessState::self()->getContextObject(nullptr)); - ALOGI("Registering service %s", name.c_str()); + bool reRegister = mRegisteredServices.count(name) > 0; + std::string regStr = (reRegister) ? "Re-registering" : "Registering"; + ALOGI("%s service %s", regStr.c_str(), name.c_str()); if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) { ALOGE("Failed to register service %s", name.c_str()); return false; } - if (!manager->registerClientCallback(name, service, this).isOk()) - { - ALOGE("Failed to add client callback for service %s", name.c_str()); - return false; + if (!manager->registerClientCallback(name, service, this).isOk()) { + ALOGE("Failed to add client callback for service %s", name.c_str()); + return false; } - mRegisteredServices.push_back({service, name, allowIsolated, dumpFlags}); + if (!reRegister) { + // Only add this when a service is added for the first time, as it is not removed + mRegisteredServices[name] = {service, allowIsolated, dumpFlags}; + } return true; } @@ -119,10 +122,11 @@ void ClientCounterCallback::tryShutdown() { for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) { auto& entry = (*unRegisterIt); - bool success = manager->tryUnregisterService(entry.name, entry.service).isOk(); + bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk(); + if (!success) { - ALOGI("Failed to unregister service %s", entry.name.c_str()); + ALOGI("Failed to unregister service %s", entry.first.c_str()); break; } } @@ -137,7 +141,8 @@ void ClientCounterCallback::tryShutdown() { auto& entry = (*reRegisterIt); // re-register entry - if (!registerService(entry.service, entry.name, entry.allowIsolated, entry.dumpFlags)) { + if (!registerService(entry.second.service, entry.first, entry.second.allowIsolated, + entry.second.dumpFlags)) { // Must restart. Otherwise, clients will never be able to get a hold of this service. ALOGE("Bad state: could not re-register services"); } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 9be06cdd19..822247f561 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -505,7 +505,7 @@ void Parcel::updateWorkSourceRequestHeaderPosition() const { } } -#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)) +#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R'); #else constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T'); @@ -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/ProcessState.cpp b/libs/binder/ProcessState.cpp index ce2cd9969f..c0f5e31d72 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -60,14 +60,14 @@ public: : mIsMain(isMain) { } - + protected: virtual bool threadLoop() { IPCThreadState::self()->joinThreadPool(mIsMain); return false; } - + const bool mIsMain; }; @@ -296,7 +296,7 @@ sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) void ProcessState::expungeHandle(int32_t handle, IBinder* binder) { AutoMutex _l(mLock); - + handle_entry* e = lookupHandleLocked(handle); // This handle may have already been replaced with a new BpBinder @@ -387,7 +387,7 @@ ProcessState::ProcessState(const char *driver) { // TODO(b/139016109): enforce in build system -#if defined(__ANDROID_APEX__) && !defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) +#if defined(__ANDROID_APEX__) LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable."); #endif @@ -416,5 +416,5 @@ ProcessState::~ProcessState() } mDriverFD = -1; } - + } // namespace android diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp index bd40536884..7a77f6de54 100644 --- a/libs/binder/Static.cpp +++ b/libs/binder/Static.cpp @@ -19,7 +19,7 @@ #include "Static.h" -#include <binder/BufferedTextOutput.h> +#include "BufferedTextOutput.h" #include <binder/IPCThreadState.h> #include <utils/Log.h> diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index b3afd817c1..9aa76512c8 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -19,7 +19,16 @@ "name": "binderStabilityTest" }, { + "name": "libbinder_ndk_unit_test" + }, + { "name": "CtsNdkBinderTestCases" + }, + { + "name": "aidl_lazy_test" + }, + { + "name": "libbinderthreadstateutils_test" } ] } diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index ff9244e08a..4818889436 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -29,8 +29,6 @@ typedef int uid_t; // --------------------------------------------------------------------------- namespace android { -class IPCThreadStateBase; - class IPCThreadState { public: @@ -113,31 +111,12 @@ public: // Service manager registration void setTheContextObject(sp<BBinder> obj); - // Is this thread currently serving a binder call. This method - // returns true if while traversing backwards from the function call - // stack for this thread, we encounter a function serving a binder - // call before encountering a hwbinder call / hitting the end of the - // call stack. - // Eg: If thread T1 went through the following call pattern - // 1) T1 receives and executes hwbinder call H1. - // 2) While handling H1, T1 makes binder call B1. - // 3) The handler of B1, calls into T1 with a callback B2. - // If isServingCall() is called during H1 before 3), this method - // will return false, else true. - // - // ---- - // | B2 | ---> While callback B2 is being handled, during 3). - // ---- - // | H1 | ---> While H1 is being handled. - // ---- - // Fig: Thread Call stack while handling B2 + // WARNING: DO NOT USE THIS API // - // This is since after 3), while traversing the thread call stack, - // we hit a binder call before a hwbinder call / end of stack. This - // method may be typically used to determine whether to use - // hardware::IPCThreadState methods or IPCThreadState methods to - // infer information about thread state. - bool isServingCall() const; + // Returns a pointer to the stack from the last time a transaction + // was initiated by the kernel. Used to compare when making nested + // calls between multiple different transports. + const void* getServingStackPointer() const; // The work source represents the UID of the process we should attribute the transaction // to. We use -1 to specify that the work source was not set using #setWorkSource. @@ -181,6 +160,7 @@ private: Parcel mIn; Parcel mOut; status_t mLastError; + const void* mServingStackPointer; pid_t mCallingPid; const char* mCallingSid; uid_t mCallingUid; @@ -191,7 +171,6 @@ private: bool mPropagateWorkSource; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; - IPCThreadStateBase *mIPCThreadStateBase; ProcessState::CallRestriction mCallRestriction; }; diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 2c4326393e..1d520c127b 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -100,6 +100,14 @@ public: 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); + template<typename INTERFACE> sp<INTERFACE> waitForService(const String16& name) { const sp<IServiceManager> sm = defaultServiceManager(); diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h index b2f51d381c..2894482f55 100644 --- a/libs/binder/include/binder/Stability.h +++ b/libs/binder/include/binder/Stability.h @@ -81,7 +81,7 @@ private: VINTF = 0b111111, }; -#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)) +#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) static constexpr Level kLocalStability = Level::VENDOR; #else static constexpr Level kLocalStability = Level::SYSTEM; diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index c0ea6d7b1e..e66e425aee 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -50,6 +50,7 @@ cc_library_shared { "ibinder.cpp", "ibinder_jni.cpp", "parcel.cpp", + "parcel_jni.cpp", "process.cpp", "stability.cpp", "status.cpp", 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_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h index be3029c3ff..cd1ff1fd79 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h @@ -36,13 +36,13 @@ __BEGIN_DECLS /** * Converts an android.os.IBinder object into an AIBinder* object. * - * If either env or the binder is null, null is returned. If this binder object was originally an + * If the binder is null, null is returned. If this binder object was originally an * AIBinder object, the original object is returned. The returned object has one refcount * associated with it, and so this should be accompanied with an AIBinder_decStrong call. * * Available since API level 29. * - * \param env Java environment. + * \param env Java environment. Must not be null. * \param binder android.os.IBinder java object. * * \return an AIBinder object representing the Java binder object. If either parameter is null, or @@ -54,12 +54,12 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_fromJavaBinder(JNIEnv* en /** * Converts an AIBinder* object into an android.os.IBinder object. * - * If either env or the binder is null, null is returned. If this binder object was originally an - * IBinder object, the original java object will be returned. + * If the binder is null, null is returned. If this binder object was originally an IBinder object, + * the original java object will be returned. * * Available since API level 29. * - * \param env Java environment. + * \param env Java environment. Must not be null. * \param binder the object to convert. * * \return an android.os.IBinder object or null if the parameters were null. 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_ndk/android/binder_parcel_jni.h b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h new file mode 100644 index 0000000000..65e1704439 --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NdkBinder + * @{ + */ + +/** + * @file binder_parcel_jni.h + * @brief Conversions between AParcel and android.os.Parcel + */ + +#pragma once + +#include <android/binder_parcel.h> + +#include <jni.h> + +__BEGIN_DECLS +#if __ANDROID_API__ >= 30 + +/** + * Converts an android.os.Parcel object into an AParcel* object. + * + * If the parcel is null, null is returned. + * + * Available since API level 30. + * + * \param env Java environment. Must not be null. + * \param parcel android.os.Parcel java object. + * + * \return an AParcel object representing the Java parcel object. If either parameter is null, this + * will return null. This must be deleted with AParcel_delete. This does not take ownership of the + * jobject and is only good for as long as the jobject is alive. + */ +__attribute__((warn_unused_result)) AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject parcel) + __INTRODUCED_IN(30); + +#endif //__ANDROID_API__ >= 30 +__END_DECLS + +/** @} */ 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/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h index 56d95a7759..f5e8bf60ef 100644 --- a/libs/binder/ndk/include_platform/android/binder_stability.h +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -30,8 +30,7 @@ enum { FLAG_PRIVATE_VENDOR = 0x10000000, }; -#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || \ - (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)) +#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) enum { FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR, @@ -46,8 +45,7 @@ static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) { AIBinder_markVendorStability(binder); } -#else // defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) && - // !defined(__ANDROID_APEX__)) +#else // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) enum { FLAG_PRIVATE_LOCAL = 0, @@ -64,8 +62,7 @@ static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) { AIBinder_markSystemStability(binder); } -#endif // defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) && - // !defined(__ANDROID_APEX__)) +#endif // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) /** * This interface has system<->vendor stability diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 71d8103161..7e72f22d64 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -105,10 +105,12 @@ LIBBINDER_NDK30 { # introduced=30 AIBinder_setExtension; AStatus_getDescription; AStatus_deleteDescription; + AParcel_fromJavaParcel; AIBinder_markSystemStability; # apex AIBinder_markVendorStability; # llndk AIBinder_markVintfStability; # apex llndk + AIBinder_Class_setHandleShellCommand; # apex llndk local: *; }; diff --git a/libs/binder/ndk/parcel_jni.cpp b/libs/binder/ndk/parcel_jni.cpp new file mode 100644 index 0000000000..53b2d7c4da --- /dev/null +++ b/libs/binder/ndk/parcel_jni.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_parcel_jni.h> +#include "parcel_internal.h" + +#include <android_os_Parcel.h> + +using ::android::Parcel; +using ::android::parcelForJavaObject; + +AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject jbinder) { + if (jbinder == nullptr) { + return nullptr; + } + + Parcel* parcel = parcelForJavaObject(env, jbinder); + + if (parcel == nullptr) { + return nullptr; + } + + return new AParcel(nullptr /*binder*/, parcel, false /*shouldOwn*/); +} diff --git a/libs/binder/ndk/runtests.sh b/libs/binder/ndk/runtests.sh deleted file mode 100755 index a0c49fb167..0000000000 --- a/libs/binder/ndk/runtests.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -# 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. - -if [ -z $ANDROID_BUILD_TOP ]; then - echo "You need to source and lunch before you can use this script" - exit 1 -fi - -set -ex - -function run_libbinder_ndk_test() { - adb shell /data/nativetest64/libbinder_ndk_test_server/libbinder_ndk_test_server & - - # avoid getService 1s delay for most runs, non-critical - sleep 0.1 - - adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client; \ - adb shell killall libbinder_ndk_test_server -} - -[ "$1" != "--skip-build" ] && $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \ - MODULES-IN-frameworks-native-libs-binder-ndk - -adb root -adb wait-for-device -adb sync data - -# very simple unit tests, tests things outside of the NDK as well -run_libbinder_ndk_test - -# CTS tests (much more comprehensive, new tests should ideally go here) -atest android.binder.cts diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp index ebd08b2f71..cb4b20ff9d 100644 --- a/libs/binder/ndk/test/Android.bp +++ b/libs/binder/ndk/test/Android.bp @@ -56,16 +56,18 @@ cc_defaults { // specifically the parts which are outside of the NDK. Actual users should // also instead use AIDL to generate these stubs. See android.binder.cts. cc_test { - name: "libbinder_ndk_test_client", + name: "libbinder_ndk_unit_test", defaults: ["test_libbinder_ndk_test_defaults"], - srcs: ["main_client.cpp"], -} + srcs: ["libbinder_ndk_unit_test.cpp"], + static_libs: [ + "IBinderNdkUnitTest-cpp", + "IBinderNdkUnitTest-ndk_platform", + ], + test_suites: ["general-tests"], + require_root: true, -cc_test { - name: "libbinder_ndk_test_server", - defaults: ["test_libbinder_ndk_test_defaults"], - srcs: ["main_server.cpp"], - gtest: false, + // force since binderVendorDoubleLoadTest has its own + auto_gen_config: true, } cc_test { @@ -85,14 +87,21 @@ cc_test { "libbinder_ndk", "libutils", ], - test_suites: ["device-tests"], + test_suites: ["general-tests"], } aidl_interface { name: "IBinderVendorDoubleLoadTest", - // TODO(b/119771576): only vendor is needed - vendor_available: true, + vendor: true, srcs: [ "IBinderVendorDoubleLoadTest.aidl", ], } + +aidl_interface { + name: "IBinderNdkUnitTest", + srcs: [ + "IBinderNdkUnitTest.aidl", + "IEmpty.aidl", + ], +} diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl new file mode 100644 index 0000000000..6e8e463ff1 --- /dev/null +++ b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This AIDL is to test things that can't be tested in CtsNdkBinderTestCases +// because it requires libbinder_ndk implementation details or APIs not +// available to apps. Please prefer adding tests to CtsNdkBinderTestCases +// over here. + +import IEmpty; + +interface IBinderNdkUnitTest { + void takeInterface(IEmpty test); + void forceFlushCommands(); +} diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/test/IEmpty.aidl new file mode 100644 index 0000000000..95e4341531 --- /dev/null +++ b/libs/binder/ndk/test/IEmpty.aidl @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +interface IEmpty { } diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp index 8467734c75..fd30d87c76 100644 --- a/libs/binder/ndk/test/main_client.cpp +++ b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#include <IBinderNdkUnitTest.h> +#include <aidl/BnBinderNdkUnitTest.h> +#include <aidl/BnEmpty.h> #include <android-base/logging.h> #include <android/binder_ibinder_jni.h> #include <android/binder_manager.h> @@ -21,13 +24,88 @@ #include <gtest/gtest.h> #include <iface/iface.h> +// 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"; + +class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { + ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) { + (void)empty; + return ndk::ScopedAStatus::ok(); + } + ndk::ScopedAStatus forceFlushCommands() { + // warning: this is assuming that libbinder_ndk is using the same copy + // of libbinder that we are. + 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() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + + auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>(); + binder_status_t status = + AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService); + + if (status != STATUS_OK) { + LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService; + } + + ABinderProcess_joinThreadPool(); + + return 1; // should not return +} + +// manually-written parceling class considered bad practice +class MyFoo : public IFoo { + binder_status_t doubleNumber(int32_t in, int32_t* out) override { + *out = 2 * in; + LOG(INFO) << "doubleNumber (" << in << ") => " << *out; + return STATUS_OK; + } + + binder_status_t die() override { + LOG(FATAL) << "IFoo::die called!"; + return STATUS_UNKNOWN_ERROR; + } +}; + +int manualService(const char* instance) { + ABinderProcess_setThreadPoolMaxThreadCount(0); + + // Strong reference to MyFoo kept by service manager. + binder_status_t status = (new MyFoo)->addService(instance); + + if (status != STATUS_OK) { + LOG(FATAL) << "Could not register: " << status << " " << instance; + } + + ABinderProcess_joinThreadPool(); + + return 1; // should not return +} // This is too slow // TEST(NdkBinder, GetServiceThatDoesntExist) { @@ -87,14 +165,14 @@ TEST(NdkBinder, DeathRecipient) { EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die()); foo = nullptr; - AIBinder_decStrong(binder); - binder = nullptr; std::unique_lock<std::mutex> lock(deathMutex); EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; })); EXPECT_TRUE(deathRecieved); AIBinder_DeathRecipient_delete(recipient); + AIBinder_decStrong(binder); + binder = nullptr; } TEST(NdkBinder, RetrieveNonNdkService) { @@ -196,9 +274,142 @@ TEST(NdkBinder, AddServiceMultipleTimes) { EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2)); } +TEST(NdkBinder, SentAidlBinderCanBeDestroyed) { + static volatile bool destroyed = false; + static std::mutex dMutex; + static std::condition_variable cv; + + class MyEmpty : public aidl::BnEmpty { + virtual ~MyEmpty() { + destroyed = true; + cv.notify_one(); + } + }; + + std::shared_ptr<MyEmpty> empty = ndk::SharedRefBase::make<MyEmpty>(); + + ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService)); + std::shared_ptr<aidl::IBinderNdkUnitTest> service = + aidl::IBinderNdkUnitTest::fromBinder(binder); + + EXPECT_FALSE(destroyed); + + service->takeInterface(empty); + service->forceFlushCommands(); + empty = nullptr; + + // give other binder thread time to process commands + { + using namespace std::chrono_literals; + std::unique_lock<std::mutex> lk(dMutex); + cv.wait_for(lk, 1s, [] { return destroyed; }); + } + + 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); + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return manualService(IFoo::kInstanceNameToDieFor); + } + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return manualService(IFoo::kSomeInstanceName); + } + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return generatedService(); + } + ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks ABinderProcess_startThreadPool(); diff --git a/libs/binder/ndk/test/main_server.cpp b/libs/binder/ndk/test/main_server.cpp deleted file mode 100644 index a6e17e8d98..0000000000 --- a/libs/binder/ndk/test/main_server.cpp +++ /dev/null @@ -1,57 +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. - */ - -#include <android-base/logging.h> -#include <android/binder_process.h> -#include <iface/iface.h> - -using ::android::sp; - -class MyFoo : public IFoo { - binder_status_t doubleNumber(int32_t in, int32_t* out) override { - *out = 2 * in; - LOG(INFO) << "doubleNumber (" << in << ") => " << *out; - return STATUS_OK; - } - - binder_status_t die() override { - LOG(FATAL) << "IFoo::die called!"; - return STATUS_UNKNOWN_ERROR; - } -}; - -int service(const char* instance) { - ABinderProcess_setThreadPoolMaxThreadCount(0); - - // Strong reference to MyFoo kept by service manager. - binder_status_t status = (new MyFoo)->addService(instance); - - if (status != STATUS_OK) { - LOG(FATAL) << "Could not register: " << status << " " << instance; - } - - ABinderProcess_joinThreadPool(); - - return 1; // should not return -} - -int main() { - if (fork() == 0) { - return service(IFoo::kInstanceNameToDieFor); - } - - return service(IFoo::kSomeInstanceName); -} diff --git a/libs/binderthreadstate/1.0/Android.bp b/libs/binderthreadstate/1.0/Android.bp new file mode 100644 index 0000000000..ebdc932591 --- /dev/null +++ b/libs/binderthreadstate/1.0/Android.bp @@ -0,0 +1,14 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "binderthreadstateutilstest@1.0", + root: "binderthreadstateutilstest", + system_ext_specific: true, + srcs: [ + "IHidlStuff.hal", + ], + interfaces: [ + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/libs/binderthreadstate/1.0/IHidlStuff.hal b/libs/binderthreadstate/1.0/IHidlStuff.hal new file mode 100644 index 0000000000..ffb64998ce --- /dev/null +++ b/libs/binderthreadstate/1.0/IHidlStuff.hal @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package binderthreadstateutilstest@1.0; + +interface IHidlStuff { + callLocal(); + call(int32_t idx); +}; diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp index ee1a6a4860..4655e1d8f4 100644 --- a/libs/binderthreadstate/Android.bp +++ b/libs/binderthreadstate/Android.bp @@ -12,37 +12,66 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_library { - name: "libbinderthreadstate", - recovery_available: true, - vendor_available: false, - vndk: { - enabled: true, - support_system_process: true, - }, +// DO NOT ADD NEW USAGES OF THIS +// See comments in header file. +cc_library_static { + name: "libbinderthreadstateutils", + double_loadable: true, + vendor_available: true, host_supported: true, - srcs: [ - "IPCThreadStateBase.cpp", - ], - - header_libs: [ - "libbase_headers", - "libutils_headers", - ], - shared_libs: [ - "liblog", + "libbinder", + "libhidlbase", // libhwbinder is in here ], export_include_dirs: ["include"], - sanitize: { - misc_undefined: ["integer"], - }, - cflags: [ "-Wall", "-Werror", ], } + +hidl_package_root { + name: "binderthreadstateutilstest", +} + +aidl_interface { + name: "binderthreadstateutilstest.aidl", + srcs: ["IAidlStuff.aidl"], +} + +cc_test { + name: "libbinderthreadstateutils_test", + srcs: ["test.cpp"], + static_libs: [ + "binderthreadstateutilstest@1.0", + "binderthreadstateutilstest.aidl-cpp", + "libbinderthreadstateutils", + ], + shared_libs: [ + "libbase", + "libbinder", + "libcutils", + "libhidlbase", + "libutils", + "liblog", + ], + test_suites: [ + "general-tests", + ], + require_root: true, +} + +// TODO(b/148692216): remove empty lib +cc_library { + name: "libbinderthreadstate", + recovery_available: true, + vendor_available: false, + vndk: { + enabled: true, + support_system_process: true, + }, + host_supported: true, +} diff --git a/libs/binderthreadstate/IAidlStuff.aidl b/libs/binderthreadstate/IAidlStuff.aidl new file mode 100644 index 0000000000..0c81c42a79 --- /dev/null +++ b/libs/binderthreadstate/IAidlStuff.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +interface IAidlStuff { + void callLocal(); + void call(int idx); +} diff --git a/libs/binderthreadstate/IPCThreadStateBase.cpp b/libs/binderthreadstate/IPCThreadStateBase.cpp deleted file mode 100644 index fede151774..0000000000 --- a/libs/binderthreadstate/IPCThreadStateBase.cpp +++ /dev/null @@ -1,89 +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 "IPCThreadStateBase" - -#include <binderthreadstate/IPCThreadStateBase.h> -#include <android-base/macros.h> - -#include <utils/Log.h> - -#include <errno.h> -#include <inttypes.h> -#include <pthread.h> - -namespace android { - -static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; -static bool gHaveTLS = false; -static pthread_key_t gTLS = 0; - -IPCThreadStateBase::IPCThreadStateBase() { - pthread_setspecific(gTLS, this); -} - -IPCThreadStateBase* IPCThreadStateBase::self() -{ - if (gHaveTLS) { -restart: - const pthread_key_t k = gTLS; - IPCThreadStateBase* st = (IPCThreadStateBase*)pthread_getspecific(k); - if (st) return st; - return new IPCThreadStateBase; - } - - pthread_mutex_lock(&gTLSMutex); - if (!gHaveTLS) { - int key_create_value = pthread_key_create(&gTLS, threadDestructor); - if (key_create_value != 0) { - pthread_mutex_unlock(&gTLSMutex); - ALOGW("IPCThreadStateBase::self() unable to create TLS key, expect a crash: %s\n", - strerror(key_create_value)); - return nullptr; - } - gHaveTLS = true; - } - pthread_mutex_unlock(&gTLSMutex); - goto restart; -} - -void IPCThreadStateBase::pushCurrentState(CallState callState) { - mCallStateStack.emplace(callState); -} - -IPCThreadStateBase::CallState IPCThreadStateBase::popCurrentState() { - ALOG_ASSERT(mCallStateStack.size > 0); - CallState val = mCallStateStack.top(); - mCallStateStack.pop(); - return val; -} - -IPCThreadStateBase::CallState IPCThreadStateBase::getCurrentBinderCallState() { - if (mCallStateStack.size() > 0) { - return mCallStateStack.top(); - } - return CallState::NONE; -} - -void IPCThreadStateBase::threadDestructor(void *st) -{ - IPCThreadStateBase* const self = static_cast<IPCThreadStateBase*>(st); - if (self) { - delete self; - } -} - -}; // namespace android diff --git a/libs/binderthreadstate/TEST_MAPPING b/libs/binderthreadstate/TEST_MAPPING new file mode 100644 index 0000000000..2bd046339f --- /dev/null +++ b/libs/binderthreadstate/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libbinderthreadstateutils_test" + } + ] +} diff --git a/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h new file mode 100644 index 0000000000..a3e5026a07 --- /dev/null +++ b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// WARNING: DO NOT USE THIS +// You should: +// - have code know how it is handling things. Pass in caller information rather +// than assuming that code is running in a specific global context +// - use AIDL exclusively in your stack (HIDL is no longer required anywhere) + +#include <binder/IPCThreadState.h> +#include <hwbinder/IPCThreadState.h> + +namespace android { + +enum class BinderCallType { + NONE, + BINDER, + HWBINDER, +}; + +// Based on where we are in recursion of nested binder/hwbinder calls, determine +// which one we are closer to. +inline static BinderCallType getCurrentServingCall() { + const void* hwbinderSp = android::hardware::IPCThreadState::self()->getServingStackPointer(); + const void* binderSp = android::IPCThreadState::self()->getServingStackPointer(); + + if (hwbinderSp == nullptr && binderSp == nullptr) return BinderCallType::NONE; + if (hwbinderSp == nullptr) return BinderCallType::BINDER; + if (binderSp == nullptr) return BinderCallType::HWBINDER; + + if (hwbinderSp < binderSp) return BinderCallType::HWBINDER; + return BinderCallType::BINDER; +} + +} // namespace android diff --git a/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h b/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h deleted file mode 100644 index 6fdcc84054..0000000000 --- a/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h +++ /dev/null @@ -1,44 +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 BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H -#define BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H - -#include <stack> -namespace android { - -class IPCThreadStateBase { -public: - enum CallState { - HWBINDER, - BINDER, - NONE, - }; - static IPCThreadStateBase* self(); - void pushCurrentState(CallState callState); - CallState popCurrentState(); - CallState getCurrentBinderCallState(); - -private: - IPCThreadStateBase(); - static void threadDestructor(void *st); - - std::stack<CallState> mCallStateStack; -}; - -}; // namespace android - -#endif // BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp new file mode 100644 index 0000000000..68cc225057 --- /dev/null +++ b/libs/binderthreadstate/test.cpp @@ -0,0 +1,195 @@ +/* + * 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 <BnAidlStuff.h> +#include <android-base/logging.h> +#include <binder/IServiceManager.h> +#include <binderthreadstate/CallerUtils.h> +#include <binderthreadstateutilstest/1.0/IHidlStuff.h> +#include <gtest/gtest.h> +#include <hidl/HidlTransportSupport.h> +#include <linux/prctl.h> +#include <sys/prctl.h> + +using android::BinderCallType; +using android::defaultServiceManager; +using android::getCurrentServingCall; +using android::getService; +using android::OK; +using android::sp; +using android::String16; +using android::binder::Status; +using android::hardware::Return; +using binderthreadstateutilstest::V1_0::IHidlStuff; + +constexpr size_t kP1Id = 1; +constexpr size_t kP2Id = 2; + +// AIDL and HIDL are in separate namespaces so using same service names +std::string id2name(size_t id) { + return "libbinderthreadstateutils-" + std::to_string(id); +} + +// There are two servers calling each other recursively like this. +// +// P1 P2 +// | --HIDL--> | +// | <--HIDL-- | +// | --AIDL--> | +// | <--AIDL-- | +// | --HIDL--> | +// | <--HIDL-- | +// | --AIDL--> | +// | <--AIDL-- | +// .......... +// +// Calls always come in pairs (AIDL returns AIDL, HIDL returns HIDL) because +// this means that P1 always has a 'waitForResponse' call which can service the +// returning call and continue the recursion. Of course, with more threads, more +// complicated calls are possible, but this should do here. + +static void callHidl(size_t id, int32_t idx) { + auto stuff = IHidlStuff::getService(id2name(id)); + CHECK(stuff->call(idx).isOk()); +} + +static void callAidl(size_t id, int32_t idx) { + sp<IAidlStuff> stuff; + CHECK(OK == android::getService<IAidlStuff>(String16(id2name(id).c_str()), &stuff)); + CHECK(stuff->call(idx).isOk()); +} + +class HidlServer : public IHidlStuff { +public: + HidlServer(size_t thisId, size_t otherId) : thisId(thisId), otherId(otherId) {} + size_t thisId; + size_t otherId; + + Return<void> callLocal() { + CHECK(BinderCallType::NONE == getCurrentServingCall()); + return android::hardware::Status::ok(); + } + Return<void> call(int32_t idx) { + LOG(INFO) << "HidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx + << " with tid: " << gettid(); + CHECK(BinderCallType::HWBINDER == getCurrentServingCall()); + if (idx > 0) { + if (thisId == kP1Id && idx % 4 < 2) { + callHidl(otherId, idx - 1); + } else { + callAidl(otherId, idx - 1); + } + } + CHECK(BinderCallType::HWBINDER == getCurrentServingCall()); + return android::hardware::Status::ok(); + } +}; +class AidlServer : public BnAidlStuff { +public: + AidlServer(size_t thisId, size_t otherId) : thisId(thisId), otherId(otherId) {} + size_t thisId; + size_t otherId; + + Status callLocal() { + CHECK(BinderCallType::NONE == getCurrentServingCall()); + return Status::ok(); + } + Status call(int32_t idx) { + LOG(INFO) << "AidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx + << " with tid: " << gettid(); + CHECK(BinderCallType::BINDER == getCurrentServingCall()); + if (idx > 0) { + if (thisId == kP2Id && idx % 4 < 2) { + callHidl(otherId, idx - 1); + } else { + callAidl(otherId, idx - 1); + } + } + CHECK(BinderCallType::BINDER == getCurrentServingCall()); + return Status::ok(); + } +}; + +TEST(BinderThreadState, LocalHidlCall) { + sp<IHidlStuff> server = new HidlServer(0, 0); + EXPECT_TRUE(server->callLocal().isOk()); +} + +TEST(BinderThreadState, LocalAidlCall) { + sp<IAidlStuff> server = new AidlServer(0, 0); + EXPECT_TRUE(server->callLocal().isOk()); +} + +TEST(BindThreadState, RemoteHidlCall) { + auto stuff = IHidlStuff::getService(id2name(kP1Id)); + ASSERT_NE(nullptr, stuff); + ASSERT_TRUE(stuff->call(0).isOk()); +} +TEST(BindThreadState, RemoteAidlCall) { + sp<IAidlStuff> stuff; + ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff)); + ASSERT_NE(nullptr, stuff); + ASSERT_TRUE(stuff->call(0).isOk()); +} + +TEST(BindThreadState, RemoteNestedStartHidlCall) { + auto stuff = IHidlStuff::getService(id2name(kP1Id)); + ASSERT_NE(nullptr, stuff); + ASSERT_TRUE(stuff->call(100).isOk()); +} +TEST(BindThreadState, RemoteNestedStartAidlCall) { + sp<IAidlStuff> stuff; + ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff)); + ASSERT_NE(nullptr, stuff); + EXPECT_TRUE(stuff->call(100).isOk()); +} + +int server(size_t thisId, size_t otherId) { + // AIDL + android::ProcessState::self()->setThreadPoolMaxThreadCount(1); + sp<AidlServer> aidlServer = new AidlServer(thisId, otherId); + CHECK(OK == defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer)); + android::ProcessState::self()->startThreadPool(); + + // HIDL + setenv("TREBLE_TESTING_OVERRIDE", "true", true); + android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/); + sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId); + CHECK(OK == hidlServer->registerAsService(id2name(thisId).c_str())); + android::hardware::joinRpcThreadpool(); + + return EXIT_FAILURE; +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + setenv("TREBLE_TESTING_OVERRIDE", "true", true); + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return server(kP1Id, kP2Id); + } + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return server(kP2Id, kP1Id); + } + + android::waitForService<IAidlStuff>(String16(id2name(kP1Id).c_str())); + android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP1Id).c_str()); + android::waitForService<IAidlStuff>(String16(id2name(kP2Id).c_str())); + android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP2Id).c_str()); + + return RUN_ALL_TESTS(); +} diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp index 28cb13827d..b1943a4afd 100644 --- a/libs/cputimeinstate/Android.bp +++ b/libs/cputimeinstate/Android.bp @@ -8,19 +8,26 @@ cc_library { "liblog", "libnetdutils" ], + header_libs: ["bpf_prog_headers"], cflags: [ "-Werror", "-Wall", "-Wextra", ], + export_include_dirs: ["."], } cc_test { name: "libtimeinstate_test", srcs: ["testtimeinstate.cpp"], shared_libs: [ + "libbase", + "libbpf", + "libbpf_android", "libtimeinstate", + "libnetdutils", ], + header_libs: ["bpf_prog_headers"], cflags: [ "-Werror", "-Wall", diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 5fd4a95d7b..1465296bac 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -17,12 +17,16 @@ #define LOG_TAG "libtimeinstate" #include "cputimeinstate.h" +#include <bpf_timeinstate.h> #include <dirent.h> #include <errno.h> #include <inttypes.h> +#include <sys/sysinfo.h> #include <mutex> +#include <numeric> +#include <optional> #include <set> #include <string> #include <unordered_map> @@ -37,44 +41,38 @@ #include <libbpf.h> #include <log/log.h> -#define BPF_FS_PATH "/sys/fs/bpf/" - using android::base::StringPrintf; using android::base::unique_fd; namespace android { namespace bpf { -struct time_key_t { - uint32_t uid; - uint32_t freq; -}; - -struct val_t { - uint64_t ar[100]; -}; - static std::mutex gInitializedMutex; static bool gInitialized = false; +static std::mutex gTrackingMutex; +static bool gTracking = false; static uint32_t gNPolicies = 0; +static uint32_t gNCpus = 0; static std::vector<std::vector<uint32_t>> gPolicyFreqs; static std::vector<std::vector<uint32_t>> gPolicyCpus; static std::set<uint32_t> gAllFreqs; -static unique_fd gMapFd; +static unique_fd gTisMapFd; +static unique_fd gConcurrentMapFd; -static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) { +static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) { std::string data; - if (!android::base::ReadFileToString(path, &data)) return false; + if (!android::base::ReadFileToString(path, &data)) return {}; auto strings = android::base::Split(data, " \n"); + std::vector<uint32_t> ret; for (const auto &s : strings) { if (s.empty()) continue; uint32_t n; - if (!android::base::ParseUint(s, &n)) return false; - out->emplace_back(n); + if (!android::base::ParseUint(s, &n)) return {}; + ret.emplace_back(n); } - return true; + return ret; } static int isPolicyFile(const struct dirent *d) { @@ -89,10 +87,22 @@ static int comparePolicyFiles(const struct dirent **d1, const struct dirent **d2 return policyN1 - policyN2; } +static int bpf_obj_get_wronly(const char *pathname) { + union bpf_attr attr; + + memset(&attr, 0, sizeof(attr)); + attr.pathname = ptr_to_u64((void *)pathname); + attr.file_flags = BPF_F_WRONLY; + + return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr)); +} + static bool initGlobals() { std::lock_guard<std::mutex> guard(gInitializedMutex); if (gInitialized) return true; + gNCpus = get_nprocs_conf(); + struct dirent **dirlist; const char basepath[] = "/sys/devices/system/cpu/cpufreq"; int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles); @@ -111,21 +121,28 @@ static bool initGlobals() { for (const auto &name : {"available", "boost"}) { std::string path = StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name); - if (!readNumbersFromFile(path, &freqs)) return false; + auto nums = readNumbersFromFile(path); + if (!nums) continue; + freqs.insert(freqs.end(), nums->begin(), nums->end()); } + if (freqs.empty()) return false; std::sort(freqs.begin(), freqs.end()); gPolicyFreqs.emplace_back(freqs); for (auto freq : freqs) gAllFreqs.insert(freq); - std::vector<uint32_t> cpus; std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus"); - if (!readNumbersFromFile(path, &cpus)) return false; - gPolicyCpus.emplace_back(cpus); + auto cpus = readNumbersFromFile(path); + if (!cpus) return false; + gPolicyCpus.emplace_back(*cpus); } - gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")}; - if (gMapFd < 0) return false; + gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + if (gTisMapFd < 0) return false; + + gConcurrentMapFd = + unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + if (gConcurrentMapFd < 0) return false; gInitialized = true; return true; @@ -145,97 +162,289 @@ static bool attachTracepointProgram(const std::string &eventType, const std::str // process dies then it must be called again to resume tracking. // This function should *not* be called while tracking is already active; doing so is unnecessary // and can lead to accounting errors. -bool startTrackingUidCpuFreqTimes() { - return attachTracepointProgram("sched", "sched_switch") && +bool startTrackingUidTimes() { + std::lock_guard<std::mutex> guard(gTrackingMutex); + if (!initGlobals()) return false; + if (gTracking) return true; + + unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map")); + if (cpuPolicyFd < 0) return false; + + for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) { + for (auto &cpu : gPolicyCpus[i]) { + if (writeToMapEntry(cpuPolicyFd, &cpu, &i, BPF_ANY)) return false; + } + } + + unique_fd freqToIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_freq_to_idx_map")); + if (freqToIdxFd < 0) return false; + freq_idx_key_t key; + for (uint32_t i = 0; i < gNPolicies; ++i) { + key.policy = i; + for (uint32_t j = 0; j < gPolicyFreqs[i].size(); ++j) { + key.freq = gPolicyFreqs[i][j]; + // Start indexes at 1 so that uninitialized state is distinguishable from lowest freq. + // The uid_times map still uses 0-based indexes, and the sched_switch program handles + // conversion between them, so this does not affect our map reading code. + uint32_t idx = j + 1; + if (writeToMapEntry(freqToIdxFd, &key, &idx, BPF_ANY)) return false; + } + } + + unique_fd cpuLastUpdateFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_last_update_map")); + if (cpuLastUpdateFd < 0) return false; + std::vector<uint64_t> zeros(get_nprocs_conf(), 0); + uint32_t zero = 0; + if (writeToMapEntry(cpuLastUpdateFd, &zero, zeros.data(), BPF_ANY)) return false; + + unique_fd nrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_nr_active_map")); + if (nrActiveFd < 0) return false; + if (writeToMapEntry(nrActiveFd, &zero, &zero, BPF_ANY)) return false; + + unique_fd policyNrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_nr_active_map")); + if (policyNrActiveFd < 0) return false; + for (uint32_t i = 0; i < gNPolicies; ++i) { + if (writeToMapEntry(policyNrActiveFd, &i, &zero, BPF_ANY)) return false; + } + + unique_fd policyFreqIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map")); + if (policyFreqIdxFd < 0) return false; + for (uint32_t i = 0; i < gNPolicies; ++i) { + if (writeToMapEntry(policyFreqIdxFd, &i, &zero, BPF_ANY)) return false; + } + + gTracking = attachTracepointProgram("sched", "sched_switch") && attachTracepointProgram("power", "cpu_frequency"); + return gTracking; } -// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes. -// Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors -// using the format: +std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() { + if (!gInitialized && !initGlobals()) return {}; + return gPolicyFreqs; +} + +// Retrieve the times in ns that uid spent running at each CPU frequency. +// Return contains no value on error, otherwise it contains a vector of vectors using the format: // [[t0_0, t0_1, ...], // [t1_0, t1_1, ...], ...] // where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq. -bool getUidCpuFreqTimes(uint32_t uid, std::vector<std::vector<uint64_t>> *freqTimes) { - if (!gInitialized && !initGlobals()) return false; - time_key_t key = {.uid = uid, .freq = 0}; - - freqTimes->clear(); - freqTimes->resize(gNPolicies); - std::vector<uint32_t> idxs(gNPolicies, 0); - - val_t value; - for (uint32_t freq : gAllFreqs) { - key.freq = freq; - int ret = findMapEntry(gMapFd, &key, &value); - if (ret) { - if (errno == ENOENT) - memset(&value.ar, 0, sizeof(value.ar)); - else - return false; +std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid) { + if (!gInitialized && !initGlobals()) return {}; + + std::vector<std::vector<uint64_t>> out; + uint32_t maxFreqCount = 0; + for (const auto &freqList : gPolicyFreqs) { + if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); + out.emplace_back(freqList.size(), 0); + } + + std::vector<tis_val_t> vals(gNCpus); + time_key_t key = {.uid = uid}; + for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) { + key.bucket = i; + if (findMapEntry(gTisMapFd, &key, vals.data())) { + if (errno != ENOENT) return {}; + continue; } - for (uint32_t i = 0; i < gNPolicies; ++i) { - if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue; - uint64_t time = 0; - for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu]; - idxs[i] += 1; - (*freqTimes)[i].emplace_back(time); + + auto offset = i * FREQS_PER_ENTRY; + auto nextOffset = (i + 1) * FREQS_PER_ENTRY; + for (uint32_t j = 0; j < gNPolicies; ++j) { + if (offset >= gPolicyFreqs[j].size()) continue; + auto begin = out[j].begin() + offset; + auto end = nextOffset < gPolicyFreqs[j].size() ? begin + FREQS_PER_ENTRY : out[j].end(); + + for (const auto &cpu : gPolicyCpus[j]) { + std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>()); + } } } - return true; + return out; } -// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap. -// Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to -// vectors of vectors using the format: +// 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: // { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...], // uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... } // where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq. -bool getUidsCpuFreqTimes( - std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *freqTimeMap) { - if (!gInitialized && !initGlobals()) return false; +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> +getUidsCpuFreqTimes() { + if (!gInitialized && !initGlobals()) return {}; + time_key_t key, prevKey; + std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map; + if (getFirstMapKey(gTisMapFd, &key)) { + if (errno == ENOENT) return map; + return std::nullopt; + } - int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times"); - if (fd < 0) return false; - BpfMap<time_key_t, val_t> m(fd); + std::vector<std::vector<uint64_t>> mapFormat; + for (const auto &freqList : gPolicyFreqs) mapFormat.emplace_back(freqList.size(), 0); - std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs; - for (uint32_t i = 0; i < gNPolicies; ++i) { - std::unordered_map<uint32_t, uint32_t> freqIdxs; - for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j; - policyFreqIdxs.emplace_back(freqIdxs); + std::vector<tis_val_t> vals(gNCpus); + do { + if (findMapEntry(gTisMapFd, &key, vals.data())) return {}; + if (map.find(key.uid) == map.end()) map.emplace(key.uid, mapFormat); + + auto offset = key.bucket * FREQS_PER_ENTRY; + auto nextOffset = (key.bucket + 1) * FREQS_PER_ENTRY; + for (uint32_t i = 0; i < gNPolicies; ++i) { + if (offset >= gPolicyFreqs[i].size()) continue; + auto begin = map[key.uid][i].begin() + offset; + auto end = nextOffset < gPolicyFreqs[i].size() ? begin + FREQS_PER_ENTRY : + map[key.uid][i].end(); + for (const auto &cpu : gPolicyCpus[i]) { + std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>()); + } + } + prevKey = key; + } while (!getNextMapKey(gTisMapFd, &prevKey, &key)); + if (errno != ENOENT) return {}; + return map; +} + +static bool verifyConcurrentTimes(const concurrent_time_t &ct) { + uint64_t activeSum = std::accumulate(ct.active.begin(), ct.active.end(), (uint64_t)0); + uint64_t policySum = 0; + for (const auto &vec : ct.policy) { + policySum += std::accumulate(vec.begin(), vec.end(), (uint64_t)0); } + return activeSum == policySum; +} + +// Retrieve the times in ns that uid spent running concurrently with each possible number of other +// tasks on each cluster (policy times) and overall (active times). +// Return contains no value on error, otherwise it contains a concurrent_time_t with the format: +// {.active = [a0, a1, ...], .policy = [[p0_0, p0_1, ...], [p1_0, p1_1, ...], ...]} +// 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<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) { + if (!gInitialized && !initGlobals()) return {}; + concurrent_time_t ret = {.active = std::vector<uint64_t>(gNCpus, 0)}; + for (const auto &cpuList : gPolicyCpus) ret.policy.emplace_back(cpuList.size(), 0); + std::vector<concurrent_val_t> vals(gNCpus); + time_key_t key = {.uid = uid}; + for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) { + if (findMapEntry(gConcurrentMapFd, &key, vals.data())) { + if (errno != ENOENT) return {}; + continue; + } + auto offset = key.bucket * CPUS_PER_ENTRY; + auto nextOffset = (key.bucket + 1) * CPUS_PER_ENTRY; - auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val, - const BpfMap<time_key_t, val_t> &) { - if (freqTimeMap->find(key.uid) == freqTimeMap->end()) { - (*freqTimeMap)[key.uid].resize(gNPolicies); - for (uint32_t i = 0; i < gNPolicies; ++i) { - (*freqTimeMap)[key.uid][i].resize(gPolicyFreqs[i].size(), 0); + auto activeBegin = ret.active.begin() + offset; + auto activeEnd = nextOffset < gNCpus ? activeBegin + CPUS_PER_ENTRY : ret.active.end(); + + for (uint32_t cpu = 0; cpu < gNCpus; ++cpu) { + std::transform(activeBegin, activeEnd, std::begin(vals[cpu].active), activeBegin, + std::plus<uint64_t>()); + } + + for (uint32_t policy = 0; policy < gNPolicies; ++policy) { + if (offset >= gPolicyCpus[policy].size()) continue; + auto policyBegin = ret.policy[policy].begin() + offset; + auto policyEnd = nextOffset < gPolicyCpus[policy].size() ? policyBegin + CPUS_PER_ENTRY + : ret.policy[policy].end(); + + for (const auto &cpu : gPolicyCpus[policy]) { + std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin, + std::plus<uint64_t>()); } } + } + if (!verifyConcurrentTimes(ret) && retry) return getUidConcurrentTimes(uid, false); + return ret; +} + +// 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). +// Return contains no value on error, otherwise it contains a map from uids to concurrent_time_t's +// using the format: +// { uid0 -> {.active = [a0, a1, ...], .policy = [[p0_0, p0_1, ...], [p1_0, p1_1, ...], ...] }, ...} +// 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() { + if (!gInitialized && !initGlobals()) return {}; + time_key_t key, prevKey; + std::unordered_map<uint32_t, concurrent_time_t> ret; + if (getFirstMapKey(gConcurrentMapFd, &key)) { + if (errno == ENOENT) return ret; + return {}; + } + + concurrent_time_t retFormat = {.active = std::vector<uint64_t>(gNCpus, 0)}; + for (const auto &cpuList : gPolicyCpus) retFormat.policy.emplace_back(cpuList.size(), 0); + + std::vector<concurrent_val_t> vals(gNCpus); + std::vector<uint64_t>::iterator activeBegin, activeEnd, policyBegin, policyEnd; + + do { + if (findMapEntry(gConcurrentMapFd, &key, vals.data())) return {}; + if (ret.find(key.uid) == ret.end()) ret.emplace(key.uid, retFormat); + + auto offset = key.bucket * CPUS_PER_ENTRY; + auto nextOffset = (key.bucket + 1) * CPUS_PER_ENTRY; + + activeBegin = ret[key.uid].active.begin(); + activeEnd = nextOffset < gNCpus ? activeBegin + CPUS_PER_ENTRY : ret[key.uid].active.end(); + + for (uint32_t cpu = 0; cpu < gNCpus; ++cpu) { + std::transform(activeBegin, activeEnd, std::begin(vals[cpu].active), activeBegin, + std::plus<uint64_t>()); + } + + for (uint32_t policy = 0; policy < gNPolicies; ++policy) { + if (offset >= gPolicyCpus[policy].size()) continue; + policyBegin = ret[key.uid].policy[policy].begin() + offset; + policyEnd = nextOffset < gPolicyCpus[policy].size() ? policyBegin + CPUS_PER_ENTRY + : ret[key.uid].policy[policy].end(); - for (size_t policy = 0; policy < gNPolicies; ++policy) { for (const auto &cpu : gPolicyCpus[policy]) { - auto freqIdx = policyFreqIdxs[policy][key.freq]; - (*freqTimeMap)[key.uid][policy][freqIdx] += val.ar[cpu]; + std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin, + std::plus<uint64_t>()); } } - return android::netdutils::status::ok; - }; - return isOk(m.iterateWithValue(fn)); + prevKey = key; + } while (!getNextMapKey(gConcurrentMapFd, &prevKey, &key)); + if (errno != ENOENT) return {}; + for (const auto &[key, value] : ret) { + if (!verifyConcurrentTimes(value)) { + auto val = getUidConcurrentTimes(key, false); + if (val.has_value()) ret[key] = val.value(); + } + } + return ret; } // Clear all time in state data for a given uid. Returns false on error, true otherwise. -bool clearUidCpuFreqTimes(uint32_t uid) { +// This is only suitable for clearing data when an app is uninstalled; if called on a UID with +// running tasks it will cause time in state vs. concurrent time totals to be inconsistent for that +// UID. +bool clearUidTimes(uint32_t uid) { if (!gInitialized && !initGlobals()) return false; - time_key_t key = {.uid = uid, .freq = 0}; - std::vector<uint32_t> idxs(gNPolicies, 0); - for (auto freq : gAllFreqs) { - key.freq = freq; - if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false; + time_key_t key = {.uid = uid}; + + uint32_t maxFreqCount = 0; + for (const auto &freqList : gPolicyFreqs) { + if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); + } + + tis_val_t zeros = {0}; + std::vector<tis_val_t> vals(gNCpus, zeros); + for (key.bucket = 0; key.bucket <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++key.bucket) { + if (writeToMapEntry(gTisMapFd, &key, vals.data(), BPF_EXIST) && errno != ENOENT) + return false; + if (deleteMapEntry(gTisMapFd, &key) && errno != ENOENT) return false; + } + + concurrent_val_t czeros = {.policy = {0}, .active = {0}}; + std::vector<concurrent_val_t> cvals(gNCpus, czeros); + for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) { + if (writeToMapEntry(gConcurrentMapFd, &key, cvals.data(), BPF_EXIST) && errno != ENOENT) + return false; + if (deleteMapEntry(gConcurrentMapFd, &key) && errno != ENOENT) return false; } return true; } diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h index 9f6103ed9b..49469d8e04 100644 --- a/libs/cputimeinstate/cputimeinstate.h +++ b/libs/cputimeinstate/cputimeinstate.h @@ -22,10 +22,20 @@ namespace android { namespace bpf { -bool startTrackingUidCpuFreqTimes(); -bool getUidCpuFreqTimes(unsigned int uid, std::vector<std::vector<uint64_t>> *freqTimes); -bool getUidsCpuFreqTimes(std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *tisMap); -bool clearUidCpuFreqTimes(unsigned int uid); +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::vector<std::vector<uint32_t>>> getCpuFreqs(); + +struct concurrent_time_t { + std::vector<uint64_t> active; + std::vector<std::vector<uint64_t>> policy; +}; + +std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry = true); +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes(); +bool clearUidTimes(unsigned int uid); } // namespace bpf } // namespace android diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 9837865dfb..23d87fd646 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -1,57 +1,381 @@ +/* + * 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. + */ + +#include <bpf_timeinstate.h> + +#include <sys/sysinfo.h> + +#include <numeric> #include <unordered_map> #include <vector> #include <gtest/gtest.h> +#include <android-base/unique_fd.h> +#include <bpf/BpfMap.h> #include <cputimeinstate.h> +#include <libbpf.h> namespace android { namespace bpf { +static constexpr uint64_t NSEC_PER_SEC = 1000000000; +static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365; + using std::vector; -TEST(TimeInStateTest, SingleUid) { - vector<vector<uint64_t>> times; - ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); - EXPECT_FALSE(times.empty()); +TEST(TimeInStateTest, SingleUidTimeInState) { + auto times = getUidCpuFreqTimes(0); + ASSERT_TRUE(times.has_value()); + EXPECT_FALSE(times->empty()); +} + +TEST(TimeInStateTest, SingleUidConcurrentTimes) { + auto concurrentTimes = getUidConcurrentTimes(0); + ASSERT_TRUE(concurrentTimes.has_value()); + ASSERT_FALSE(concurrentTimes->active.empty()); + ASSERT_FALSE(concurrentTimes->policy.empty()); + + uint64_t policyEntries = 0; + for (const auto &policyTimeVec : concurrentTimes->policy) policyEntries += policyTimeVec.size(); + ASSERT_EQ(concurrentTimes->active.size(), policyEntries); +} + +static void TestConcurrentTimesConsistent(const struct concurrent_time_t &concurrentTime) { + size_t maxPolicyCpus = 0; + for (const auto &vec : concurrentTime.policy) { + maxPolicyCpus = std::max(maxPolicyCpus, vec.size()); + } + uint64_t policySum = 0; + for (size_t i = 0; i < maxPolicyCpus; ++i) { + for (const auto &vec : concurrentTime.policy) { + if (i < vec.size()) policySum += vec[i]; + } + ASSERT_LE(concurrentTime.active[i], policySum); + policySum -= concurrentTime.active[i]; + } + policySum = 0; + for (size_t i = 0; i < concurrentTime.active.size(); ++i) { + for (const auto &vec : concurrentTime.policy) { + if (i < vec.size()) policySum += vec[vec.size() - 1 - i]; + } + auto activeSum = concurrentTime.active[concurrentTime.active.size() - 1 - i]; + // This check is slightly flaky because we may read a map entry in the middle of an update + // when active times have been updated but policy times have not. This happens infrequently + // and can be distinguished from more serious bugs by re-running the test: if the underlying + // data itself is inconsistent, the test will fail every time. + ASSERT_LE(activeSum, policySum); + policySum -= activeSum; + } +} + +static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &timeInState, + const struct concurrent_time_t &concurrentTime) { + ASSERT_NO_FATAL_FAILURE(TestConcurrentTimesConsistent(concurrentTime)); + ASSERT_EQ(timeInState.size(), concurrentTime.policy.size()); + uint64_t policySum = 0; + for (uint32_t i = 0; i < timeInState.size(); ++i) { + uint64_t tisSum = + std::accumulate(timeInState[i].begin(), timeInState[i].end(), (uint64_t)0); + uint64_t concurrentSum = std::accumulate(concurrentTime.policy[i].begin(), + concurrentTime.policy[i].end(), (uint64_t)0); + if (tisSum < concurrentSum) + ASSERT_LE(concurrentSum - tisSum, NSEC_PER_SEC); + else + ASSERT_LE(tisSum - concurrentSum, NSEC_PER_SEC); + policySum += concurrentSum; + } + uint64_t activeSum = std::accumulate(concurrentTime.active.begin(), concurrentTime.active.end(), + (uint64_t)0); + EXPECT_EQ(activeSum, policySum); +} + +TEST(TimeInStateTest, SingleUidTimesConsistent) { + auto times = getUidCpuFreqTimes(0); + ASSERT_TRUE(times.has_value()); + + auto concurrentTimes = getUidConcurrentTimes(0); + ASSERT_TRUE(concurrentTimes.has_value()); + + ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes)); } -TEST(TimeInStateTest, AllUid) { +TEST(TimeInStateTest, AllUidTimeInState) { vector<size_t> sizes; - std::unordered_map<uint32_t, vector<vector<uint64_t>>> map; - ASSERT_TRUE(getUidsCpuFreqTimes(&map)); + auto map = getUidsCpuFreqTimes(); + ASSERT_TRUE(map.has_value()); - ASSERT_FALSE(map.empty()); + ASSERT_FALSE(map->empty()); - auto firstEntry = map.begin()->second; + auto firstEntry = map->begin()->second; for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); - for (const auto &vec : map) { + 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()); + + 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()); + } + } +} + +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); + } + 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); + } + } + } +} + +void TestCheckDelta(uint64_t before, uint64_t after) { + // Times should never decrease + ASSERT_LE(before, after); + // UID can't have run for more than ~1s on each CPU + ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf()); +} + +TEST(TimeInStateTest, AllUidTimeInStateMonotonic) { + auto map1 = getUidsCpuFreqTimes(); + ASSERT_TRUE(map1.has_value()); + sleep(1); + auto map2 = getUidsCpuFreqTimes(); + ASSERT_TRUE(map2.has_value()); + + for (const auto &kv : *map1) { + uint32_t uid = kv.first; + auto times = kv.second; + ASSERT_NE(map2->find(uid), map2->end()); + for (uint32_t policy = 0; policy < times.size(); ++policy) { + for (uint32_t freqIdx = 0; freqIdx < times[policy].size(); ++freqIdx) { + auto before = times[policy][freqIdx]; + auto after = (*map2)[uid][policy][freqIdx]; + ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after)); + } + } + } +} + +TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) { + auto map1 = getUidsConcurrentTimes(); + ASSERT_TRUE(map1.has_value()); + ASSERT_FALSE(map1->empty()); + sleep(1); + auto map2 = getUidsConcurrentTimes(); + ASSERT_TRUE(map2.has_value()); + ASSERT_FALSE(map2->empty()); + + for (const auto &kv : *map1) { + uint32_t uid = kv.first; + auto times = kv.second; + ASSERT_NE(map2->find(uid), map2->end()); + for (uint32_t i = 0; i < times.active.size(); ++i) { + auto before = times.active[i]; + auto after = (*map2)[uid].active[i]; + ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after)); + } + for (uint32_t policy = 0; policy < times.policy.size(); ++policy) { + for (uint32_t idx = 0; idx < times.policy[policy].size(); ++idx) { + auto before = times.policy[policy][idx]; + auto after = (*map2)[uid].policy[policy][idx]; + ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after)); + } + } + } +} + +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; + } + } + } + // 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) { + 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); +} + +TEST(TimeInStateTest, AllUidTimesConsistent) { + auto tisMap = getUidsCpuFreqTimes(); + ASSERT_TRUE(tisMap.has_value()); + + auto concurrentMap = getUidsConcurrentTimes(); + ASSERT_TRUE(concurrentMap.has_value()); + + ASSERT_EQ(tisMap->size(), concurrentMap->size()); + for (const auto &kv : *tisMap) { + uint32_t uid = kv.first; + auto times = kv.second; + ASSERT_NE(concurrentMap->find(uid), concurrentMap->end()); + + auto concurrentTimes = (*concurrentMap)[uid]; + ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(times, concurrentTimes)); + } +} + TEST(TimeInStateTest, RemoveUid) { - vector<vector<uint64_t>> times, times2; - ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); - ASSERT_FALSE(times.empty()); + uint32_t uid = 0; + { + // Find an unused UID + auto times = getUidsCpuFreqTimes(); + ASSERT_TRUE(times.has_value()); + ASSERT_FALSE(times->empty()); + for (const auto &kv : *times) uid = std::max(uid, kv.first); + ++uid; + } + { + // Add a map entry for our fake UID by copying a real map entry + android::base::unique_fd fd{ + bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + ASSERT_GE(fd, 0); + time_key_t k; + ASSERT_FALSE(getFirstMapKey(fd, &k)); + std::vector<tis_val_t> vals(get_nprocs_conf()); + ASSERT_FALSE(findMapEntry(fd, &k, vals.data())); + uint32_t copiedUid = k.uid; + k.uid = uid; + ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST)); + + android::base::unique_fd fd2{ + bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + k.uid = copiedUid; + k.bucket = 0; + std::vector<concurrent_val_t> cvals(get_nprocs_conf()); + ASSERT_FALSE(findMapEntry(fd2, &k, cvals.data())); + k.uid = uid; + ASSERT_FALSE(writeToMapEntry(fd2, &k, cvals.data(), BPF_NOEXIST)); + } + auto times = getUidCpuFreqTimes(uid); + ASSERT_TRUE(times.has_value()); + ASSERT_FALSE(times->empty()); + + auto concurrentTimes = getUidConcurrentTimes(0); + ASSERT_TRUE(concurrentTimes.has_value()); + ASSERT_FALSE(concurrentTimes->active.empty()); + ASSERT_FALSE(concurrentTimes->policy.empty()); uint64_t sum = 0; - for (size_t i = 0; i < times.size(); ++i) { - for (auto x : times[i]) sum += x; + for (size_t i = 0; i < times->size(); ++i) { + for (auto x : (*times)[i]) sum += x; } ASSERT_GT(sum, (uint64_t)0); - ASSERT_TRUE(clearUidCpuFreqTimes(0)); - - ASSERT_TRUE(getUidCpuFreqTimes(0, ×2)); - ASSERT_EQ(times2.size(), times.size()); - for (size_t i = 0; i < times.size(); ++i) { - ASSERT_EQ(times2[i].size(), times[i].size()); - for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]); + uint64_t activeSum = 0; + for (size_t i = 0; i < concurrentTimes->active.size(); ++i) { + activeSum += concurrentTimes->active[i]; } + ASSERT_GT(activeSum, (uint64_t)0); + + ASSERT_TRUE(clearUidTimes(uid)); + + auto allTimes = getUidsCpuFreqTimes(); + ASSERT_TRUE(allTimes.has_value()); + ASSERT_FALSE(allTimes->empty()); + ASSERT_EQ(allTimes->find(uid), allTimes->end()); + + auto allConcurrentTimes = getUidsConcurrentTimes(); + ASSERT_TRUE(allConcurrentTimes.has_value()); + ASSERT_FALSE(allConcurrentTimes->empty()); + ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end()); +} + +TEST(TimeInStateTest, GetCpuFreqs) { + auto freqs = getCpuFreqs(); + ASSERT_TRUE(freqs.has_value()); + + auto times = getUidCpuFreqTimes(0); + ASSERT_TRUE(times.has_value()); + + ASSERT_EQ(freqs->size(), times->size()); + for (size_t i = 0; i < freqs->size(); ++i) EXPECT_EQ((*freqs)[i].size(), (*times)[i].size()); } } // namespace bpf diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 250f902f9d..0ede8b1916 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -16,7 +16,9 @@ #include <set> #include <android-base/file.h> +#include <android-base/properties.h> #include <android-base/stringprintf.h> +#include <android-base/strings.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <dumputils/dump_utils.h> #include <log/log.h> @@ -32,7 +34,6 @@ static const char* native_processes_to_dump[] = { "/system/bin/mediametrics", // media.metrics "/system/bin/mediaserver", "/system/bin/netd", - "/system/bin/vold", "/system/bin/sdcard", "/system/bin/statsd", "/system/bin/surfaceflinger", @@ -42,11 +43,19 @@ static const char* native_processes_to_dump[] = { NULL, }; + +// Native processes to dump on debuggable builds. +static const char* debuggable_native_processes_to_dump[] = { + "/system/bin/vold", + NULL, +}; + /* list of hal interface to dump containing process during native dumps */ static const char* hal_interfaces_to_dump[] { "android.hardware.audio@2.0::IDevicesFactory", "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", + "android.hardware.audio@6.0::IDevicesFactory", "android.hardware.biometrics.face@1.0::IBiometricsFace", "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", @@ -65,13 +74,34 @@ static const char* hal_interfaces_to_dump[] { NULL, }; -bool should_dump_hal_interface(const char* interface) { +/* list of extra hal interfaces to dump containing process during native dumps */ +// This is filled when dumpstate is called. +static std::set<const std::string> extra_hal_interfaces_to_dump; + +static void read_extra_hals_to_dump_from_property() { + // extra hals to dump are already filled + if (extra_hal_interfaces_to_dump.size() > 0) { + return; + } + std::string value = android::base::GetProperty("ro.dump.hals.extra", ""); + std::vector<std::string> tokens = android::base::Split(value, ","); + for (const auto &token : tokens) { + std::string trimmed_token = android::base::Trim(token); + if (trimmed_token.length() == 0) { + continue; + } + extra_hal_interfaces_to_dump.insert(trimmed_token); + } +} + +// check if interface is included in either default hal list or extra hal list +bool should_dump_hal_interface(const std::string& interface) { for (const char** i = hal_interfaces_to_dump; *i; i++) { - if (!strcmp(*i, interface)) { + if (interface == *i) { return true; } } - return false; + return extra_hal_interfaces_to_dump.find(interface) != extra_hal_interfaces_to_dump.end(); } bool should_dump_native_traces(const char* path) { @@ -80,6 +110,15 @@ bool should_dump_native_traces(const char* path) { return true; } } + + if (android::base::GetBoolProperty("ro.debuggable", false)) { + for (const char** p = debuggable_native_processes_to_dump; *p; p++) { + if (!strcmp(*p, path)) { + return true; + } + } + } + return false; } @@ -91,13 +130,15 @@ std::set<int> get_interesting_hal_pids() { sp<IServiceManager> manager = IServiceManager::getService(); std::set<int> pids; + read_extra_hals_to_dump_from_property(); + Return<void> ret = manager->debugDump([&](auto& hals) { for (const auto &info : hals) { if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) { continue; } - if (!should_dump_hal_interface(info.interfaceName.c_str())) { + if (!should_dump_hal_interface(info.interfaceName)) { continue; } diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp new file mode 100644 index 0000000000..de32ff4e64 --- /dev/null +++ b/libs/fakeservicemanager/Android.bp @@ -0,0 +1,25 @@ +cc_defaults { + name: "fakeservicemanager_defaults", + srcs: [ + "ServiceManager.cpp", + ], + + shared_libs: [ + "libbinder", + "libutils", + ], +} + +cc_library { + name: "libfakeservicemanager", + defaults: ["fakeservicemanager_defaults"], +} + +cc_test_host { + name: "fakeservicemanager_test", + defaults: ["fakeservicemanager_defaults"], + srcs: [ + "test_sm.cpp", + ], + static_libs: ["libgmock"], +} diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp new file mode 100644 index 0000000000..69643249e3 --- /dev/null +++ b/libs/fakeservicemanager/ServiceManager.cpp @@ -0,0 +1,64 @@ +/* + * 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 "ServiceManager.h" + +namespace android { + +ServiceManager::ServiceManager() {} + +sp<IBinder> ServiceManager::getService( const String16& name) const { + // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons. + return checkService(name); +} + +sp<IBinder> ServiceManager::checkService( const String16& name) const { + auto it = mNameToService.find(name); + if (it == mNameToService.end()) { + return nullptr; + } + return it->second; +} + +status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service, + bool /*allowIsolated*/, + int /*dumpsysFlags*/) { + mNameToService[name] = service; + return NO_ERROR; +} + +Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) { + Vector<String16> services; + for (auto const& [name, service] : mNameToService) { + (void) service; + services.push_back(name); + } + return services; +} + +IBinder* ServiceManager::onAsBinder() { + return nullptr; +} + +sp<IBinder> ServiceManager::waitForService(const String16& name) { + return checkService(name); +} + +bool ServiceManager::isDeclared(const String16& name) { + return mNameToService.find(name) != mNameToService.end(); +} + +} // namespace android diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h new file mode 100644 index 0000000000..62311d4727 --- /dev/null +++ b/libs/fakeservicemanager/ServiceManager.h @@ -0,0 +1,74 @@ +/* + * 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 <binder/IServiceManager.h> + +#include <map> + +namespace android { + +/** + * A local host simple implementation of IServiceManager, that does not + * communicate over binder. +*/ +class ServiceManager : public IServiceManager { +public: + ServiceManager(); + + /** + * Equivalent of checkService. + */ + sp<IBinder> getService( const String16& name) const override; + + /** + * Retrieve an existing service, non-blocking. + */ + sp<IBinder> checkService( const String16& name) const override; + + /** + * Register a service. + */ + status_t addService(const String16& name, const sp<IBinder>& service, + bool allowIsolated = false, + int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) override; + + /** + * Return list of all existing services. + */ + Vector<String16> listServices(int dumpsysFlags = 0) override; + + IBinder* onAsBinder() override; + + /** + * Effectively no-oped in this implementation - equivalent to checkService. + */ + sp<IBinder> waitForService(const String16& name) override; + + /** + * Check if a service is declared (e.g. VINTF manifest). + * + * If this returns true, waitForService should always be able to return the + * service. + */ + bool isDeclared(const String16& name) override; + +private: + std::map<String16, sp<IBinder>> mNameToService; +}; + +} // namespace android diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp new file mode 100644 index 0000000000..71e5abe126 --- /dev/null +++ b/libs/fakeservicemanager/test_sm.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 <gtest/gtest.h> +#include <gmock/gmock.h> + +#include <binder/Binder.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> + +#include "ServiceManager.h" + +using android::sp; +using android::BBinder; +using android::IBinder; +using android::OK; +using android::status_t; +using android::ServiceManager; +using android::String16; +using android::IServiceManager; +using testing::ElementsAre; + +static sp<IBinder> getBinder() { + class LinkableBinder : public BBinder { + status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override { + // let SM linkToDeath + return OK; + } + }; + + return new LinkableBinder; +} + +TEST(AddService, HappyHappy) { + auto sm = new ServiceManager(); + EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); +} + +TEST(AddService, HappyOverExistingService) { + auto sm = new ServiceManager(); + EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); +} + +TEST(GetService, HappyHappy) { + auto sm = new ServiceManager(); + sp<IBinder> service = getBinder(); + + EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + + EXPECT_EQ(sm->getService(String16("foo")), service); +} + +TEST(GetService, NonExistant) { + auto sm = new ServiceManager(); + + EXPECT_EQ(sm->getService(String16("foo")), nullptr); +} + +TEST(ListServices, AllServices) { + auto sm = new ServiceManager(); + + EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + EXPECT_EQ(sm->addService(String16("sc"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_NORMAL), OK); + EXPECT_EQ(sm->addService(String16("sb"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_HIGH), OK); + EXPECT_EQ(sm->addService(String16("sa"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL), OK); + + android::Vector<String16> out = sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL); + + // all there and in the right order + EXPECT_THAT(out, ElementsAre(String16("sa"), String16("sb"), String16("sc"), + String16("sd"))); +} + +TEST(WaitForService, NonExistant) { + auto sm = new ServiceManager(); + + EXPECT_EQ(sm->waitForService(String16("foo")), nullptr); +} + +TEST(WaitForService, HappyHappy) { + auto sm = new ServiceManager(); + sp<IBinder> service = getBinder(); + + EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + + EXPECT_EQ(sm->waitForService(String16("foo")), service); +} + +TEST(IsDeclared, NonExistant) { + auto sm = new ServiceManager(); + + EXPECT_FALSE(sm->isDeclared(String16("foo"))); +} + +TEST(IsDeclared, HappyHappy) { + auto sm = new ServiceManager(); + sp<IBinder> service = getBinder(); + + EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + + EXPECT_TRUE(sm->isDeclared(String16("foo"))); +} diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 166775b89a..ba3195a38e 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -186,6 +186,10 @@ cc_defaults { "libvndksupport", ], + static_libs: [ + "libbinderthreadstateutils", + ], + header_libs: [ "libgui_headers", "libnativebase_headers", diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp index 3b531ec752..c13030b1ed 100644 --- a/libs/gui/BufferQueueThreadState.cpp +++ b/libs/gui/BufferQueueThreadState.cpp @@ -15,6 +15,7 @@ */ #include <binder/IPCThreadState.h> +#include <binderthreadstate/CallerUtils.h> #include <hwbinder/IPCThreadState.h> #include <private/gui/BufferQueueThreadState.h> #include <unistd.h> @@ -22,14 +23,14 @@ namespace android { uid_t BufferQueueThreadState::getCallingUid() { - if (hardware::IPCThreadState::self()->isServingCall()) { + if (getCurrentServingCall() == BinderCallType::HWBINDER) { return hardware::IPCThreadState::self()->getCallingUid(); } return IPCThreadState::self()->getCallingUid(); } pid_t BufferQueueThreadState::getCallingPid() { - if (hardware::IPCThreadState::self()->isServingCall()) { + if (getCurrentServingCall() == BinderCallType::HWBINDER) { return hardware::IPCThreadState::self()->getCallingPid(); } return IPCThreadState::self()->getCallingPid(); diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS index 73150dcb58..c13401dc5c 100644 --- a/libs/gui/OWNERS +++ b/libs/gui/OWNERS @@ -1,7 +1,12 @@ +adyabr@google.com +akrulec@google.com +alecmouri@google.com jessehall@google.com jwcai@google.com lpy@google.com marissaw@google.com mathias@google.com racarr@google.com +steventhomas@google.com stoza@google.com +vhau@google.com diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index 139987e6a9..abc910302c 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -577,7 +577,8 @@ void Sensor::flattenString8(void*& buffer, size_t& size, uint32_t len = static_cast<uint32_t>(string8.length()); FlattenableUtils::write(buffer, size, len); memcpy(static_cast<char*>(buffer), string8.string(), len); - FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len)); + FlattenableUtils::advance(buffer, size, len); + size -= FlattenableUtils::align<4>(buffer); } bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& outputString8) { diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 42b578cce8..080336b890 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -32,12 +32,6 @@ cc_library_shared { sanitize: { integer_overflow: true, misc_undefined: ["bounds"], - diag: { - misc_undefined: ["bounds"], - no_recover: [ - "bounds", - ], - }, }, srcs: [ diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index eb90c8b45b..3c741ab4fb 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -161,6 +161,10 @@ cc_library_shared { ], ldflags: ["-Wl,--exclude-libs=ALL"], export_include_dirs: ["EGL/include"], + stubs: { + symbol_file: "libEGL.map.txt", + versions: ["29"], + }, } cc_test { 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 diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index 69d8c89b45..f2bc65db35 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -5,4 +5,6 @@ chaviw@google.com lpy@google.com marissaw@google.com racarr@google.com -stoza@google.com
\ No newline at end of file +steventhomas@google.com +stoza@google.com +vhau@google.com diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc index aea602bba4..d3942e8bbe 100644 --- a/services/surfaceflinger/surfaceflinger.rc +++ b/services/surfaceflinger/surfaceflinger.rc @@ -2,6 +2,7 @@ service surfaceflinger /system/bin/surfaceflinger class core animation user system group graphics drmrpc readproc + capabilities SYS_NICE onrestart restart zygote writepid /dev/stune/foreground/tasks socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0 |