diff options
46 files changed, 1353 insertions, 332 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 4d98520e01..310b5cecf0 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -135,6 +135,11 @@ typedef Dumpstate::ConsentCallback::ConsentResult UserConsentResult; static char cmdline_buf[16384] = "(unknown)"; static const char *dump_traces_path = nullptr; static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000; +// Because telephony reports are significantly faster to collect (< 10 seconds vs. > 2 minutes), +// it's often the case that they time out far too quickly for consent with such a hefty dialog for +// the user to read. For telephony reports only, we increase the default timeout to 2 minutes to +// roughly match full reports' durations. +static const uint64_t TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS = 2 * 60 * 1000; // TODO: variables and functions below should be part of dumpstate object @@ -149,6 +154,7 @@ void add_mountinfo(); #define RECOVERY_DATA_DIR "/data/misc/recovery" #define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log" #define LOGPERSIST_DATA_DIR "/data/misc/logd" +#define PREREBOOT_DATA_DIR "/data/misc/prereboot" #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" #define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref" #define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat" @@ -890,6 +896,14 @@ static void DoSystemLogcat(time_t since) { CommandOptions::WithTimeoutInMs(timeout_ms).Build()); } +static void DoRadioLogcat() { + unsigned long timeout_ms = logcat_timeout({"radio"}); + RunCommand( + "RADIO LOG", + {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); +} + static void DoLogcat() { unsigned long timeout_ms; // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags"); @@ -908,11 +922,7 @@ static void DoLogcat() { "STATS LOG", {"logcat", "-b", "stats", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); - timeout_ms = logcat_timeout({"radio"}); - RunCommand( - "RADIO LOG", - {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, - CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); + DoRadioLogcat(); RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"}); @@ -1413,11 +1423,14 @@ static Dumpstate::RunStatus dumpstate() { RunCommand("FILESYSTEMS & FREE SPACE", {"df"}); /* Binder state is expensive to look at as it uses a lot of memory. */ - DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log"); - DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log"); - DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions"); - DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats"); - DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state"); + std::string binder_logs_dir = access("/dev/binderfs/binder_logs", R_OK) ? + "/sys/kernel/debug/binder" : "/dev/binderfs/binder_logs"; + + DumpFile("BINDER FAILED TRANSACTION LOG", binder_logs_dir + "/failed_transaction_log"); + DumpFile("BINDER TRANSACTION LOG", binder_logs_dir + "/transaction_log"); + DumpFile("BINDER TRANSACTIONS", binder_logs_dir + "/transactions"); + DumpFile("BINDER STATS", binder_logs_dir + "/stats"); + DumpFile("BINDER STATE", binder_logs_dir + "/state"); /* Add window and surface trace files. */ if (!PropertiesHelper::IsUserBuild()) { @@ -1561,6 +1574,7 @@ static Dumpstate::RunStatus DumpstateDefault() { ds.AddDir(PROFILE_DATA_DIR_CUR, true); ds.AddDir(PROFILE_DATA_DIR_REF, true); } + ds.AddDir(PREREBOOT_DATA_DIR, false); add_mountinfo(); DumpIpTablesAsRoot(); DumpDynamicPartitionInfo(); @@ -1599,8 +1613,10 @@ static Dumpstate::RunStatus DumpstateDefault() { return status; } -// This method collects common dumpsys for telephony and wifi -static void DumpstateRadioCommon() { +// This method collects common dumpsys for telephony and wifi. Typically, wifi +// reports are fine to include all information, but telephony reports on user +// builds need to strip some content (see DumpstateTelephonyOnly). +static void DumpstateRadioCommon(bool include_sensitive_info = true) { DumpIpTablesAsRoot(); ds.AddDir(LOGPERSIST_DATA_DIR, false); @@ -1609,26 +1625,51 @@ static void DumpstateRadioCommon() { return; } - do_dmesg(); - DoLogcat(); + // We need to be picky about some stuff for telephony reports on user builds. + if (!include_sensitive_info) { + // Only dump the radio log buffer (other buffers and dumps contain too much unrelated info). + DoRadioLogcat(); + } else { + // Contains various system properties and process startup info. + do_dmesg(); + // Logs other than the radio buffer may contain package/component names and potential PII. + DoLogcat(); + // Too broad for connectivity problems. + DoKmsg(); + // Contains unrelated hardware info (camera, NFC, biometrics, ...). + DumpHals(); + } + DumpPacketStats(); - DoKmsg(); DumpIpAddrAndRules(); dump_route_tables(); - DumpHals(); - RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}, CommandOptions::WithTimeout(10).Build()); } -// This method collects dumpsys for telephony debugging only +// We use "telephony" here for legacy reasons, though this now really means "connectivity" (cellular +// + wifi + networking). This method collects dumpsys for connectivity debugging only. General rules +// for what can be included on user builds: all reported information MUST directly relate to +// connectivity debugging or customer support and MUST NOT contain unrelated personally identifiable +// information. This information MUST NOT identify user-installed packages (UIDs are OK, package +// names are not), and MUST NOT contain logs of user application traffic. +// TODO(b/148168577) rename this and other related fields/methods to "connectivity" instead. static void DumpstateTelephonyOnly() { DurationReporter duration_reporter("DUMPSTATE"); + const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build(); - DumpstateRadioCommon(); + const bool include_sensitive_info = !PropertiesHelper::IsUserBuild(); - RunCommand("SYSTEM PROPERTIES", {"getprop"}); + DumpstateRadioCommon(include_sensitive_info); + + if (include_sensitive_info) { + // Contains too much unrelated PII, and given the unstructured nature of sysprops, we can't + // really cherrypick all of the connectivity-related ones. Apps generally have no business + // reading these anyway, and there should be APIs to supply the info in a more app-friendly + // way. + RunCommand("SYSTEM PROPERTIES", {"getprop"}); + } printf("========================================================\n"); printf("== Android Framework Services\n"); @@ -1636,15 +1677,28 @@ static void DumpstateTelephonyOnly() { RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); - RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(), - SEC_TO_MSEC(10)); - RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + // TODO(b/146521742) build out an argument to include bound services here for user builds RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); - RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(), + RunDumpsys("DUMPSYS", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"network_management"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + if (include_sensitive_info) { + // Contains raw IP addresses, omit from reports on user builds. + RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + // Contains raw destination IP/MAC addresses, omit from reports on user builds. + RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + // Contains package/component names, omit from reports on user builds. + RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + // Contains package names, but should be relatively simple to remove them (also contains + // UIDs already), omit from reports on user builds. + RunDumpsys("BATTERYSTATS", {"deviceidle"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + } printf("========================================================\n"); printf("== Running Application Services\n"); @@ -1652,18 +1706,24 @@ static void DumpstateTelephonyOnly() { RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"}); - printf("========================================================\n"); - printf("== Running Application Services (non-platform)\n"); - printf("========================================================\n"); + if (include_sensitive_info) { + printf("========================================================\n"); + printf("== Running Application Services (non-platform)\n"); + printf("========================================================\n"); - RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, - DUMPSYS_COMPONENTS_OPTIONS); + // Contains package/component names and potential PII, omit from reports on user builds. + // To get dumps of the active CarrierService(s) on user builds, we supply an argument to the + // carrier_config dumpsys instead. + RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, + DUMPSYS_COMPONENTS_OPTIONS); - printf("========================================================\n"); - printf("== Checkins\n"); - printf("========================================================\n"); + printf("========================================================\n"); + printf("== Checkins\n"); + printf("========================================================\n"); - RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); + // Contains package/component names, omit from reports on user builds. + RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); + } printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); @@ -2275,6 +2335,7 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt break; case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: options->telephony_only = true; + options->do_progress_updates = true; options->do_fb = false; options->do_broadcast = true; break; @@ -2825,8 +2886,13 @@ Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() { if (consent_result == UserConsentResult::UNAVAILABLE) { // User has not responded yet. uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs(); - if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) { - uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000; + // Telephony is a fast report type, particularly on user builds where information may be + // more aggressively limited. To give the user time to read the consent dialog, increase the + // timeout. + uint64_t timeout_ms = options_->telephony_only ? TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS + : USER_CONSENT_TIMEOUT_MS; + if (elapsed_ms < timeout_ms) { + uint delay_seconds = (timeout_ms - elapsed_ms) / 1000; MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds); sleep(delay_seconds); } diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index cff1d439d9..99d482f034 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -372,12 +372,12 @@ TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { EXPECT_TRUE(options_.do_broadcast); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.telephony_only); + EXPECT_TRUE(options_.do_progress_updates); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); - EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); } diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 5597bcd915..a427c8dd68 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -29,6 +29,7 @@ #include <utils/Log.h> #include <utils/Vector.h> +#include <iostream> #include <fcntl.h> #include <getopt.h> #include <stdio.h> @@ -231,14 +232,14 @@ int Dumpsys::main(int argc, char* const argv[]) { const size_t N = services.size(); if (N > 1) { // first print a list of the current services - aout << "Currently running services:" << endl; + std::cout << "Currently running services:" << std::endl; for (size_t i=0; i<N; i++) { sp<IBinder> service = sm_->checkService(services[i]); if (service != nullptr) { bool skipped = IsSkipped(skippedServices, services[i]); - aout << " " << services[i] << (skipped ? " (skipped)" : "") << endl; + std::cout << " " << services[i] << (skipped ? " (skipped)" : "") << std::endl; } } } @@ -263,10 +264,10 @@ int Dumpsys::main(int argc, char* const argv[]) { asProto, elapsedDuration, bytesWritten); if (status == TIMED_OUT) { - aout << endl + std::cout << std::endl << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs - << "ms) EXPIRED ***" << endl - << endl; + << "ms) EXPIRED ***" << std::endl + << std::endl; } if (addSeparator) { @@ -332,14 +333,14 @@ status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, const Vector<String16>& args) { sp<IBinder> service = sm_->checkService(serviceName); if (service == nullptr) { - aerr << "Can't find service: " << serviceName << endl; + std::cerr << "Can't find service: " << serviceName << std::endl; return NAME_NOT_FOUND; } int sfd[2]; if (pipe(sfd) != 0) { - aerr << "Failed to create pipe to dump service info for " << serviceName << ": " - << strerror(errno) << endl; + std::cerr << "Failed to create pipe to dump service info for " << serviceName << ": " + << strerror(errno) << std::endl; return -errno; } @@ -359,13 +360,13 @@ status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, err = dumpPidToFd(service, remote_end); break; default: - aerr << "Unknown dump type" << static_cast<int>(type) << endl; + std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl; return; } if (err != OK) { - aerr << "Error dumping service info status_t: " << statusToString(err) << " " - << serviceName << endl; + std::cerr << "Error dumping service info status_t: " << statusToString(err) << " " + << serviceName << std::endl; } }); return OK; @@ -422,8 +423,8 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); if (rc < 0) { - aerr << "Error in poll while dumping service " << serviceName << " : " - << strerror(errno) << endl; + std::cerr << "Error in poll while dumping service " << serviceName << " : " + << strerror(errno) << std::endl; status = -errno; break; } else if (rc == 0) { @@ -434,8 +435,8 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi char buf[4096]; rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf))); if (rc < 0) { - aerr << "Failed to read while dumping service " << serviceName << ": " - << strerror(errno) << endl; + std::cerr << "Failed to read while dumping service " << serviceName << ": " + << strerror(errno) << std::endl; status = -errno; break; } else if (rc == 0) { @@ -444,8 +445,8 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi } if (!WriteFully(fd, buf, rc)) { - aerr << "Failed to write while dumping service " << serviceName << ": " - << strerror(errno) << endl; + std::cerr << "Failed to write while dumping service " << serviceName << ": " + << strerror(errno) << std::endl; status = -errno; break; } diff --git a/cmds/dumpsys/main.cpp b/cmds/dumpsys/main.cpp index 8ba0eba0fa..fa4cc97b91 100644 --- a/cmds/dumpsys/main.cpp +++ b/cmds/dumpsys/main.cpp @@ -21,10 +21,9 @@ #include "dumpsys.h" #include <binder/IServiceManager.h> -#include <binder/TextOutput.h> +#include <iostream> #include <signal.h> -#include <stdio.h> using namespace android; @@ -34,7 +33,7 @@ int main(int argc, char* const argv[]) { fflush(stdout); if (sm == nullptr) { ALOGE("Unable to get default service manager!"); - aerr << "dumpsys: Unable to get default service manager!" << endl; + std::cerr << "dumpsys: Unable to get default service manager!" << std::endl; return 20; } diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp index 8b868fb584..a61f6bf87b 100644 --- a/cmds/installd/CacheTracker.cpp +++ b/cmds/installd/CacheTracker.cpp @@ -75,8 +75,7 @@ void CacheTracker::loadStats() { bool CacheTracker::loadQuotaStats() { int cacheGid = multiuser_get_cache_gid(mUserId, mAppId); - int extCacheGid = multiuser_get_ext_cache_gid(mUserId, mAppId); - if (IsQuotaSupported(mUuid) && cacheGid != -1 && extCacheGid != -1) { + if (IsQuotaSupported(mUuid) && cacheGid != -1) { int64_t space; if ((space = GetOccupiedSpaceForGid(mUuid, cacheGid)) != -1) { cacheUsed += space; @@ -84,7 +83,7 @@ bool CacheTracker::loadQuotaStats() { return false; } - if ((space = GetOccupiedSpaceForGid(mUuid, extCacheGid)) != -1) { + if ((space = get_occupied_app_cache_space_external(mUuid, mUserId, mAppId)) != -1) { cacheUsed += space; } else { return false; diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 0fde31a5cc..b3e679284e 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -53,6 +53,7 @@ #include <log/log.h> // TODO: Move everything to base/logging. #include <logwrap/logwrap.h> #include <private/android_filesystem_config.h> +#include <private/android_projectid_config.h> #include <selinux/android.h> #include <system/thread_defs.h> #include <utils/Trace.h> @@ -689,11 +690,13 @@ binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std:: if (delete_dir_contents_and_dir(path) != 0) { res = error("Failed to delete " + path); } - destroy_app_current_profiles(packageName, userId); - // TODO(calin): If the package is still installed by other users it's probably - // beneficial to keep the reference profile around. - // Verify if it's ok to do that. - destroy_app_reference_profile(packageName); + if ((flags & FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) { + destroy_app_current_profiles(packageName, userId); + // TODO(calin): If the package is still installed by other users it's probably + // beneficial to keep the reference profile around. + // Verify if it's ok to do that. + destroy_app_reference_profile(packageName); + } } if (flags & FLAG_STORAGE_EXTERNAL) { std::lock_guard<std::recursive_mutex> lock(mMountsLock); @@ -1475,8 +1478,8 @@ static std::string toString(std::vector<int64_t> values) { static void collectQuotaStats(const std::string& uuid, int32_t userId, int32_t appId, struct stats* stats, struct stats* extStats) { int64_t space; + uid_t uid = multiuser_get_uid(userId, appId); if (stats != nullptr) { - uid_t uid = multiuser_get_uid(userId, appId); if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { stats->dataSize += space; } @@ -1497,20 +1500,44 @@ static void collectQuotaStats(const std::string& uuid, int32_t userId, } if (extStats != nullptr) { - int extGid = multiuser_get_ext_gid(userId, appId); - if (extGid != -1) { - if ((space = GetOccupiedSpaceForGid(uuid, extGid)) != -1) { - extStats->dataSize += space; + static const bool supportsSdCardFs = supports_sdcardfs(); + space = get_occupied_app_space_external(uuid, userId, appId); + + if (space != -1) { + extStats->dataSize += space; + if (!supportsSdCardFs && stats != nullptr) { + // On devices without sdcardfs, if internal and external are on + // the same volume, a uid such as u0_a123 is used for + // application dirs on both internal and external storage; + // therefore, substract that amount from internal to make sure + // we don't count it double. + stats->dataSize -= space; } } - int extCacheGid = multiuser_get_ext_cache_gid(userId, appId); - if (extCacheGid != -1) { - if ((space = GetOccupiedSpaceForGid(uuid, extCacheGid)) != -1) { - extStats->dataSize += space; - extStats->cacheSize += space; + space = get_occupied_app_cache_space_external(uuid, userId, appId); + if (space != -1) { + extStats->dataSize += space; // cache counts for "data" + extStats->cacheSize += space; + if (!supportsSdCardFs && stats != nullptr) { + // On devices without sdcardfs, if internal and external are on + // the same volume, a uid such as u0_a123 is used for both + // internal and external storage; therefore, substract that + // amount from internal to make sure we don't count it double. + stats->dataSize -= space; } } + + if (!supportsSdCardFs && stats != nullptr) { + // On devices without sdcardfs, the UID of OBBs on external storage + // matches the regular app UID (eg u0_a123); therefore, to avoid + // OBBs being include in stats->dataSize, compute the OBB size for + // this app, and substract it from the size reported on internal + // storage + long obbProjectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START; + int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId); + stats->dataSize -= appObbSize; + } } } @@ -1759,6 +1786,106 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri return ok(); } +struct external_sizes { + int64_t audioSize; + int64_t videoSize; + int64_t imageSize; + int64_t totalSize; // excludes OBBs (Android/obb), but includes app data + cache + int64_t obbSize; +}; + +#define PER_USER_RANGE 100000 + +static long getProjectIdForUser(int userId, long projectId) { + return userId * PER_USER_RANGE + projectId; +} + +static external_sizes getExternalSizesForUserWithQuota(const std::string& uuid, int32_t userId, const std::vector<int32_t>& appIds) { + struct external_sizes sizes = {}; + int64_t space; + + if (supports_sdcardfs()) { + uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW); + if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { + sizes.totalSize = space; + } + + gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO); + if ((space = GetOccupiedSpaceForGid(uuid, audioGid)) != -1) { + sizes.audioSize = space; + } + + gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO); + if ((space = GetOccupiedSpaceForGid(uuid, videoGid)) != -1) { + sizes.videoSize = space; + } + + gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE); + if ((space = GetOccupiedSpaceForGid(uuid, imageGid)) != -1) { + sizes.imageSize = space; + } + + if ((space = GetOccupiedSpaceForGid(uuid, AID_MEDIA_OBB)) != -1) { + sizes.obbSize = space; + } + } else { + int64_t totalSize = 0; + long defaultProjectId = getProjectIdForUser(userId, PROJECT_ID_EXT_DEFAULT); + if ((space = GetOccupiedSpaceForProjectId(uuid, defaultProjectId)) != -1) { + // This is all files that are not audio/video/images, excluding + // OBBs and app-private data + totalSize += space; + } + + long audioProjectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_AUDIO); + if ((space = GetOccupiedSpaceForProjectId(uuid, audioProjectId)) != -1) { + sizes.audioSize = space; + totalSize += space; + } + + long videoProjectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_VIDEO); + if ((space = GetOccupiedSpaceForProjectId(uuid, videoProjectId)) != -1) { + sizes.videoSize = space; + totalSize += space; + } + + long imageProjectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_IMAGE); + if ((space = GetOccupiedSpaceForProjectId(uuid, imageProjectId)) != -1) { + sizes.imageSize = space; + totalSize += space; + } + + int64_t totalAppDataSize = 0; + int64_t totalAppCacheSize = 0; + int64_t totalAppObbSize = 0; + for (auto appId : appIds) { + if (appId >= AID_APP_START) { + // App data + uid_t uid = multiuser_get_uid(userId, appId); + long projectId = uid - AID_APP_START + PROJECT_ID_EXT_DATA_START; + totalAppDataSize += GetOccupiedSpaceForProjectId(uuid, projectId); + + // App cache + long cacheProjectId = uid - AID_APP_START + PROJECT_ID_EXT_CACHE_START; + totalAppCacheSize += GetOccupiedSpaceForProjectId(uuid, cacheProjectId); + + // App OBBs + long obbProjectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START; + totalAppObbSize += GetOccupiedSpaceForProjectId(uuid, obbProjectId); + } + } + // Total size should include app data + cache + totalSize += totalAppDataSize; + totalSize += totalAppCacheSize; + sizes.totalSize = totalSize; + + // Only OBB is separate + sizes.obbSize = totalAppObbSize; + } + + return sizes; +} + binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::string>& uuid, int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, std::vector<int64_t>* _aidl_return) { @@ -1787,14 +1914,6 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str } if (flags & FLAG_USE_QUOTA) { - int64_t space; - - ATRACE_BEGIN("obb"); - if ((space = GetOccupiedSpaceForGid(uuidString, AID_MEDIA_OBB)) != -1) { - extStats.codeSize += space; - } - ATRACE_END(); - ATRACE_BEGIN("code"); calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize, -1, -1, true); ATRACE_END(); @@ -1816,10 +1935,9 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str } ATRACE_BEGIN("external"); - uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW); - if ((space = GetOccupiedSpaceForUid(uuidString, uid)) != -1) { - extStats.dataSize += space; - } + auto sizes = getExternalSizesForUserWithQuota(uuidString, userId, appIds); + extStats.dataSize += sizes.totalSize; + extStats.codeSize += sizes.obbSize; ATRACE_END(); if (!uuid) { @@ -1830,13 +1948,11 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str -1, -1, true); ATRACE_END(); } - ATRACE_BEGIN("quota"); int64_t dataSize = extStats.dataSize; for (auto appId : appIds) { if (appId >= AID_APP_START) { collectQuotaStats(uuidString, userId, appId, &stats, &extStats); - #if MEASURE_DEBUG // Sleep to make sure we don't lose logs usleep(1); @@ -1932,29 +2048,13 @@ binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std: } if (flags & FLAG_USE_QUOTA) { - int64_t space; - ATRACE_BEGIN("quota"); - uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW); - if ((space = GetOccupiedSpaceForUid(uuidString, uid)) != -1) { - totalSize = space; - } - - gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO); - if ((space = GetOccupiedSpaceForGid(uuidString, audioGid)) != -1) { - audioSize = space; - } - gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO); - if ((space = GetOccupiedSpaceForGid(uuidString, videoGid)) != -1) { - videoSize = space; - } - gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE); - if ((space = GetOccupiedSpaceForGid(uuidString, imageGid)) != -1) { - imageSize = space; - } - if ((space = GetOccupiedSpaceForGid(uuidString, AID_MEDIA_OBB)) != -1) { - obbSize = space; - } + auto sizes = getExternalSizesForUserWithQuota(uuidString, userId, appIds); + totalSize = sizes.totalSize; + audioSize = sizes.audioSize; + videoSize = sizes.videoSize; + imageSize = sizes.imageSize; + obbSize = sizes.obbSize; ATRACE_END(); ATRACE_BEGIN("apps"); diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index 7c989f6ced..6459805ba3 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -61,11 +61,15 @@ static void CloseDescriptor(const char* descriptor_string) { static std::vector<apex::ApexFile> ActivateApexPackages() { // The logic here is (partially) copied and adapted from - // system/apex/apexd/apexd_main.cpp. + // system/apex/apexd/apexd.cpp. // - // Only scan the APEX directory under /system (within the chroot dir). - // Cast call to void to suppress warn_unused_result. - static_cast<void>(apex::scanPackagesDirAndActivate(apex::kApexPackageSystemDir)); + // Only scan the APEX directory under /system, /system_ext and /vendor (within the chroot dir). + std::vector<const char*> apex_dirs{apex::kApexPackageSystemDir, apex::kApexPackageSystemExtDir, + apex::kApexPackageVendorDir}; + for (const auto& dir : apex_dirs) { + // Cast call to void to suppress warn_unused_result. + static_cast<void>(apex::scanPackagesDirAndActivate(dir)); + } return apex::getActivePackages(); } diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 2f79552d1c..f82afa83b3 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -26,6 +26,7 @@ #include <sys/xattr.h> #include <sys/statvfs.h> +#include <android-base/file.h> #include <android-base/logging.h> #include <android-base/strings.h> #include <android-base/stringprintf.h> @@ -34,9 +35,11 @@ #include <cutils/properties.h> #include <log/log.h> #include <private/android_filesystem_config.h> +#include <private/android_projectid_config.h> #include "dexopt_return_codes.h" #include "globals.h" // extern variables. +#include "QuotaUtils.h" #ifndef LOG_TAG #define LOG_TAG "installd" @@ -1060,6 +1063,51 @@ int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t ta return 0; } +static const char* kProcFilesystems = "/proc/filesystems"; +bool supports_sdcardfs() { + std::string supported; + if (!android::base::ReadFileToString(kProcFilesystems, &supported)) { + PLOG(ERROR) << "Failed to read supported filesystems"; + return false; + } + return supported.find("sdcardfs\n") != std::string::npos; +} + +int64_t get_occupied_app_space_external(const std::string& uuid, int32_t userId, int32_t appId) { + static const bool supportsSdcardFs = supports_sdcardfs(); + + if (supportsSdcardFs) { + int extGid = multiuser_get_ext_gid(userId, appId); + + if (extGid == -1) { + return -1; + } + + return GetOccupiedSpaceForGid(uuid, extGid); + } else { + uid_t uid = multiuser_get_uid(userId, appId); + long projectId = uid - AID_APP_START + PROJECT_ID_EXT_DATA_START; + return GetOccupiedSpaceForProjectId(uuid, projectId); + } +} +int64_t get_occupied_app_cache_space_external(const std::string& uuid, int32_t userId, int32_t appId) { + static const bool supportsSdcardFs = supports_sdcardfs(); + + if (supportsSdcardFs) { + int extCacheGid = multiuser_get_ext_cache_gid(userId, appId); + + if (extCacheGid == -1) { + return -1; + } + + return GetOccupiedSpaceForGid(uuid, extCacheGid); + } else { + uid_t uid = multiuser_get_uid(userId, appId); + long projectId = uid - AID_APP_START + PROJECT_ID_EXT_CACHE_START; + return GetOccupiedSpaceForProjectId(uuid, projectId); + } +} + // Collect all non empty profiles from the given directory and puts then into profile_paths. // The profiles are identified based on PROFILE_EXT extension. // If a subdirectory or profile file cannot be opened the method logs a warning and moves on. diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 6a420261a1..2503168f1e 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -150,6 +150,10 @@ int wait_child(pid_t pid); int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, uid_t uid, gid_t gid); +bool supports_sdcardfs(); +int64_t get_occupied_app_space_external(const std::string& uuid, int32_t userId, int32_t appId); +int64_t get_occupied_app_cache_space_external(const std::string& uuid, int32_t userId, int32_t appId); + // Collect all non empty profiles from the global profile directory and // put then into profile_paths. The profiles are identified based on PROFILE_EXT extension. // If a subdirectory or profile file cannot be opened the method logs a warning and moves on. diff --git a/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml new file mode 100644 index 0000000000..9c67d4abb7 --- /dev/null +++ b/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 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 is the standard feature indicating that the device passes Vulkan deQP + tests associated with date 2019-03-01 (0x07E30301). --> +<permissions> + <feature name="android.software.vulkan.deqp.level" version="132317953" /> +</permissions> diff --git a/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml new file mode 100644 index 0000000000..19b269b607 --- /dev/null +++ b/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 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 is the standard feature indicating that the device passes Vulkan deQP + tests associated with date 2020-03-01 (0x07E40301). --> +<permissions> + <feature name="android.software.vulkan.deqp.level" version="132383489" /> +</permissions> diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp index 8ac044c091..8883c0478a 100644 --- a/libs/adbd_auth/Android.bp +++ b/libs/adbd_auth/Android.bp @@ -27,7 +27,7 @@ cc_library { version_script: "libadbd_auth.map.txt", stubs: { - versions: ["1"], + versions: ["30"], symbol_file: "libadbd_auth.map.txt", }, diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp index a9c23110c9..5a0d3f6168 100644 --- a/libs/adbd_auth/adbd_auth.cpp +++ b/libs/adbd_auth/adbd_auth.cpp @@ -43,6 +43,8 @@ using android::base::unique_fd; +static constexpr uint32_t kAuthVersion = 1; + struct AdbdAuthPacketAuthenticated { std::string public_key; }; @@ -55,8 +57,21 @@ struct AdbdAuthPacketRequestAuthorization { std::string public_key; }; -using AdbdAuthPacket = std::variant<AdbdAuthPacketAuthenticated, AdbdAuthPacketDisconnected, - AdbdAuthPacketRequestAuthorization>; +struct AdbdPacketTlsDeviceConnected { + uint8_t transport_type; + std::string public_key; +}; + +struct AdbdPacketTlsDeviceDisconnected { + uint8_t transport_type; + std::string public_key; +}; + +using AdbdAuthPacket = std::variant<AdbdAuthPacketAuthenticated, + AdbdAuthPacketDisconnected, + AdbdAuthPacketRequestAuthorization, + AdbdPacketTlsDeviceConnected, + AdbdPacketTlsDeviceDisconnected>; struct AdbdAuthContext { static constexpr uint64_t kEpollConstSocket = 0; @@ -65,6 +80,7 @@ struct AdbdAuthContext { public: explicit AdbdAuthContext(AdbdAuthCallbacksV1* callbacks) : next_id_(0), callbacks_(*callbacks) { + InitFrameworkHandlers(); epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC)); if (epoll_fd_ == -1) { PLOG(FATAL) << "failed to create epoll fd"; @@ -163,36 +179,58 @@ public: } } - void HandlePacket(std::string_view packet) REQUIRES(mutex_) { + void HandlePacket(std::string_view packet) EXCLUDES(mutex_) { LOG(INFO) << "received packet: " << packet; - if (packet.length() < 2) { - LOG(ERROR) << "received packet of invalid length"; - ReplaceFrameworkFd(unique_fd()); + if (packet.size() < 2) { + LOG(ERROR) << "received packet of invalid length"; + std::lock_guard<std::mutex> lock(mutex_); + ReplaceFrameworkFd(unique_fd()); } - if (packet[0] == 'O' && packet[1] == 'K') { - CHECK(this->dispatched_prompt_.has_value()); - auto& [id, key, arg] = *this->dispatched_prompt_; - keys_.emplace(id, std::move(key)); - - 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? - this->dispatched_prompt_ = std::nullopt; - DispatchPendingPrompt(); - } else { - LOG(ERROR) << "unhandled packet: " << packet; - ReplaceFrameworkFd(unique_fd()); + bool handled_packet = false; + for (size_t i = 0; i < framework_handlers_.size(); ++i) { + if (android::base::ConsumePrefix(&packet, framework_handlers_[i].code)) { + framework_handlers_[i].cb(packet); + handled_packet = true; + break; + } + } + if (!handled_packet) { + LOG(ERROR) << "unhandled packet: " << packet; + std::lock_guard<std::mutex> lock(mutex_); + ReplaceFrameworkFd(unique_fd()); } } + void AllowUsbDevice(std::string_view buf) EXCLUDES(mutex_) { + std::lock_guard<std::mutex> lock(mutex_); + CHECK(buf.empty()); + CHECK(dispatched_prompt_.has_value()); + auto& [id, key, arg] = *dispatched_prompt_; + keys_.emplace(id, std::move(key)); + + callbacks_.key_authorized(arg, id); + dispatched_prompt_ = std::nullopt; + + // We need to dispatch pending prompts here upon success as well, + // since we might have multiple queued prompts. + DispatchPendingPrompt(); + } + + void DenyUsbDevice(std::string_view buf) EXCLUDES(mutex_) { + std::lock_guard<std::mutex> lock(mutex_); + CHECK(buf.empty()); + // TODO: Do we want a callback if the key is denied? + dispatched_prompt_ = std::nullopt; + DispatchPendingPrompt(); + } + + void KeyRemoved(std::string_view buf) EXCLUDES(mutex_) { + CHECK(!buf.empty()); + callbacks_.key_removed(buf.data(), buf.size()); + } + bool SendPacket() REQUIRES(mutex_) { if (output_queue_.empty()) { return false; @@ -201,7 +239,8 @@ public: CHECK_NE(-1, framework_fd_.get()); auto& packet = output_queue_.front(); - struct iovec iovs[2]; + struct iovec iovs[3]; + int iovcnt = 2; if (auto* p = std::get_if<AdbdAuthPacketAuthenticated>(&packet)) { iovs[0].iov_base = const_cast<char*>("CK"); iovs[0].iov_len = 2; @@ -217,13 +256,29 @@ public: iovs[0].iov_len = 2; iovs[1].iov_base = p->public_key.data(); iovs[1].iov_len = p->public_key.size(); + } else if (auto* p = std::get_if<AdbdPacketTlsDeviceConnected>(&packet)) { + iovcnt = 3; + iovs[0].iov_base = const_cast<char*>("WE"); + iovs[0].iov_len = 2; + iovs[1].iov_base = &p->transport_type; + iovs[1].iov_len = 1; + iovs[2].iov_base = p->public_key.data(); + iovs[2].iov_len = p->public_key.size(); + } else if (auto* p = std::get_if<AdbdPacketTlsDeviceDisconnected>(&packet)) { + iovcnt = 3; + iovs[0].iov_base = const_cast<char*>("WF"); + iovs[0].iov_len = 2; + iovs[1].iov_base = &p->transport_type; + iovs[1].iov_len = 1; + iovs[2].iov_base = p->public_key.data(); + iovs[2].iov_len = p->public_key.size(); } else { LOG(FATAL) << "unhandled packet type?"; } output_queue_.pop_front(); - ssize_t rc = writev(framework_fd_.get(), iovs, 2); + ssize_t rc = writev(framework_fd_.get(), iovs, iovcnt); if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { PLOG(ERROR) << "failed to write to framework fd"; ReplaceFrameworkFd(unique_fd()); @@ -308,7 +363,6 @@ public: std::lock_guard<std::mutex> lock(mutex_); ReplaceFrameworkFd(unique_fd()); } else { - std::lock_guard<std::mutex> lock(mutex_); HandlePacket(std::string_view(buf, rc)); } } @@ -329,7 +383,7 @@ public: } static constexpr const char* key_paths[] = {"/adb_keys", "/data/misc/adb/adb_keys"}; - void IteratePublicKeys(bool (*callback)(const char*, size_t, void*), void* arg) { + void IteratePublicKeys(bool (*callback)(void*, const char*, size_t), void* opaque) { for (const auto& path : key_paths) { if (access(path, R_OK) == 0) { LOG(INFO) << "Loading keys from " << path; @@ -339,7 +393,7 @@ public: continue; } for (const auto& line : android::base::Split(content, "\n")) { - if (!callback(line.data(), line.size(), arg)) { + if (!callback(opaque, line.data(), line.size())) { return; } } @@ -361,7 +415,7 @@ public: std::lock_guard<std::mutex> lock(mutex_); keys_.emplace(id, public_key); output_queue_.emplace_back( - AdbdAuthPacketDisconnected{.public_key = std::string(public_key)}); + AdbdAuthPacketAuthenticated{.public_key = std::string(public_key)}); return id; } @@ -376,6 +430,32 @@ public: keys_.erase(it); } + uint64_t NotifyTlsDeviceConnected(AdbTransportType type, + std::string_view public_key) EXCLUDES(mutex_) { + uint64_t id = NextId(); + std::lock_guard<std::mutex> lock(mutex_); + keys_.emplace(id, public_key); + output_queue_.emplace_back(AdbdPacketTlsDeviceConnected{ + .transport_type = static_cast<uint8_t>(type), + .public_key = std::string(public_key)}); + Interrupt(); + return id; + } + + void NotifyTlsDeviceDisconnected(AdbTransportType type, uint64_t id) EXCLUDES(mutex_) { + std::lock_guard<std::mutex> lock(mutex_); + auto it = keys_.find(id); + if (it == keys_.end()) { + LOG(DEBUG) << "couldn't find public key to notify disconnection of tls device, skipping"; + return; + } + output_queue_.emplace_back(AdbdPacketTlsDeviceDisconnected{ + .transport_type = static_cast<uint8_t>(type), + .public_key = std::move(it->second)}); + keys_.erase(it); + Interrupt(); + } + // Interrupt the worker thread to do some work. void Interrupt() { uint64_t value = 1; @@ -387,6 +467,24 @@ public: } } + void InitFrameworkHandlers() { + // Framework wants to disconnect from a secured wifi device + framework_handlers_.emplace_back( + FrameworkPktHandler{ + .code = "DD", + .cb = std::bind(&AdbdAuthContext::KeyRemoved, this, std::placeholders::_1)}); + // Framework allows USB debugging for the device + framework_handlers_.emplace_back( + FrameworkPktHandler{ + .code = "OK", + .cb = std::bind(&AdbdAuthContext::AllowUsbDevice, this, std::placeholders::_1)}); + // Framework denies USB debugging for the device + framework_handlers_.emplace_back( + FrameworkPktHandler{ + .code = "NO", + .cb = std::bind(&AdbdAuthContext::DenyUsbDevice, this, std::placeholders::_1)}); + } + unique_fd epoll_fd_; unique_fd event_fd_; unique_fd sock_fd_; @@ -400,19 +498,27 @@ public: // We keep two separate queues: one to handle backpressure from the socket (output_queue_) // and one to make sure we only dispatch one authrequest at a time (pending_prompts_). - std::deque<AdbdAuthPacket> output_queue_; + std::deque<AdbdAuthPacket> output_queue_ GUARDED_BY(mutex_); std::optional<std::tuple<uint64_t, std::string, void*>> dispatched_prompt_ GUARDED_BY(mutex_); std::deque<std::tuple<uint64_t, std::string, void*>> pending_prompts_ GUARDED_BY(mutex_); + + // This is a list of commands that the framework could send to us. + using FrameworkHandlerCb = std::function<void(std::string_view)>; + struct FrameworkPktHandler { + const char* code; + FrameworkHandlerCb cb; + }; + std::vector<FrameworkPktHandler> framework_handlers_; }; AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) { - if (callbacks->version != 1) { + if (callbacks->version == 1) { + return new AdbdAuthContext(reinterpret_cast<AdbdAuthCallbacksV1*>(callbacks)); + } else { LOG(ERROR) << "received unknown AdbdAuthCallbacks version " << callbacks->version; return nullptr; } - - return new AdbdAuthContext(&callbacks->callbacks.v1); } void adbd_auth_delete(AdbdAuthContext* ctx) { @@ -424,9 +530,9 @@ void adbd_auth_run(AdbdAuthContext* ctx) { } void adbd_auth_get_public_keys(AdbdAuthContext* ctx, - bool (*callback)(const char* public_key, size_t len, void* arg), - void* arg) { - ctx->IteratePublicKeys(callback, arg); + bool (*callback)(void* opaque, const char* public_key, size_t len), + void* opaque) { + ctx->IteratePublicKeys(callback, opaque); } uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len) { @@ -438,10 +544,28 @@ void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id) { } void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, - void* arg) { - ctx->PromptUser(std::string_view(public_key, len), arg); + void* opaque) { + ctx->PromptUser(std::string_view(public_key, len), opaque); +} + +uint64_t adbd_auth_tls_device_connected(AdbdAuthContext* ctx, + AdbTransportType type, + const char* public_key, + size_t len) { + return ctx->NotifyTlsDeviceConnected(type, std::string_view(public_key, len)); +} + +void adbd_auth_tls_device_disconnected(AdbdAuthContext* ctx, + AdbTransportType type, + uint64_t id) { + ctx->NotifyTlsDeviceDisconnected(type, id); +} + +uint32_t adbd_auth_get_max_version() { + return kAuthVersion; } -bool adbd_auth_supports_feature(AdbdAuthFeature) { +bool adbd_auth_supports_feature(AdbdAuthFeature f) { + UNUSED(f); return false; } diff --git a/libs/adbd_auth/include/adbd_auth.h b/libs/adbd_auth/include/adbd_auth.h index b7c1cb88cc..6ee3166e3a 100644 --- a/libs/adbd_auth/include/adbd_auth.h +++ b/libs/adbd_auth/include/adbd_auth.h @@ -18,48 +18,159 @@ #include <stdbool.h> #include <stdint.h> +#include <sys/cdefs.h> #include <sys/types.h> -extern "C" { +#if !defined(__INTRODUCED_IN) +#define __INTRODUCED_IN(__api_level) /* nothing */ +#endif -struct AdbdAuthCallbacksV1 { - // Callback for a successful user authorization. - void (*key_authorized)(void* arg, uint64_t id); +__BEGIN_DECLS +#if !defined(__ANDROID__) || __ANDROID_API__ >= 30 + +// The transport type of the device connection. +enum AdbTransportType : int32_t { + kAdbTransportTypeUsb = 0, + kAdbTransportTypeWifi, }; +static_assert(sizeof(AdbTransportType) == sizeof(int32_t), "Unexpected AdbTransportType size"); struct AdbdAuthCallbacks { uint32_t version; - union { - AdbdAuthCallbacksV1 v1; - } callbacks; +}; + +struct AdbdAuthCallbacksV1 : AdbdAuthCallbacks { + // Callback for a successful user authorization. + void (*key_authorized)(void* opaque, uint64_t id); + // The framework removed the key from the keystore. This callback notifies + // adbd so it can take the appropriate actions (e.g. disconnect all devices + // using that key). + void (*key_removed)(const char* public_key, size_t length); }; struct AdbdAuthContext; +typedef struct AdbdAuthContext AdbdAuthContext; -AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks); -void adbd_auth_delete(AdbdAuthContext* ctx); +/** + * Creates a new AdbdAuthContext. + * + * @param callbacks a set of user-provided callbacks used internally (see + * #AdbdAuthCallbacksV1 + * @return a new AdbdAuthContext instance. Caller is responsible for destroying + * the context with #adbd_auth_delete. + */ +AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) __INTRODUCED_IN(30); -void adbd_auth_run(AdbdAuthContext* ctx); +/** + * Destroys the AdbdAuthContext. + * + * @param ctx the AdbdAuthContext to destroy. + */ +void adbd_auth_delete(AdbdAuthContext* ctx) __INTRODUCED_IN(30); -// Iterate through the list of authorized public keys. -// Return false from the callback to stop iteration. +/** + * Starts the AdbdAuthContext. + * + * The caller may want to run this on a different thread, as this + * runs indefinitely. + * + * @param ctx the AdbdAuthContext + */ +void adbd_auth_run(AdbdAuthContext* ctx) __INTRODUCED_IN(30); + +/** + * Iterate through the list of authorized public keys. + * + * @param ctx the AdbdAuthContext + * @param callback a callback which will get called for every known adb public + * key in its keystore. To stop iteration of the keys, return false in the + * callback. Otherwise, return true to continue the iteration. + * @param opaque an opaque userdata argument + */ void adbd_auth_get_public_keys(AdbdAuthContext* ctx, - bool (*callback)(const char* public_key, size_t len, void* arg), - void* arg); + bool (*callback)(void* opaque, const char* public_key, size_t len), + void* opaque) __INTRODUCED_IN(30); + +/** + * Let system_server know that a key has been successfully used for authentication. + * + * @param ctx the AdbdAuthContext + * @param public_key the RSA key that was authorized using the AUTH protocol + * @param len the length of the public_key argument + * @return an id corresponding to the new connection + */ +uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, + const char* public_key, + size_t len) __INTRODUCED_IN(30); -// Let system_server know that a key has been successfully used for authentication. -uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len); +/** + * Let system_server know that an AUTH connection has been closed. + * + * @param ctx the AdbdAuthContext + * @param id the id of the disconnected device + */ +void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, + uint64_t id) __INTRODUCED_IN(30); -// Let system_server know that a connection has been closed. -void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id); +/** + * Prompt the user to authorize a public key. + * + * When this happens, a callback will be run on the auth thread with the result. + * + * @param ctx the AdbdAuthContext + * @param public_key the RSA public key to prompt user with + * @param len the length of the public_key argument + * @param arg an opaque userdata argument + */ +void adbd_auth_prompt_user(AdbdAuthContext* ctx, + const char* public_key, + size_t len, void* opaque) __INTRODUCED_IN(30); -// Prompt the user to authorize a public key. -// When this happens, a callback will be run on the auth thread with the result. -void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, void* arg); +/** + * Let system_server know that a TLS device has connected. + * + * @param ctx the AdbdAuthContext + * @param type the transport type of the connection (see #AdbTransportType) + * @param public_key the RSA public key used to establish the connection + * @param len the length of the public_key argument + * @return an id corresponding to the new connection + */ +uint64_t adbd_auth_tls_device_connected(AdbdAuthContext* ctx, + AdbTransportType type, + const char* public_key, + size_t len) __INTRODUCED_IN(30); -enum AdbdAuthFeature { +/** + * Let system_server know that a TLS device has disconnected. + * + * @param ctx the AdbdAuthContext + * @param type the transport type of the connection (see #AdbTransportType) + * @param the id of the disconnected device (see #adbd_tls_device_connected) + */ +void adbd_auth_tls_device_disconnected(AdbdAuthContext* ctx, + AdbTransportType type, + uint64_t id) __INTRODUCED_IN(30); + +/** + * Returns the max #AdbdAuthCallbacks version. + * + * The version starts at 1, with version 1 corresponding to the + * #AdbdAuthCallbacksV1 struct. + * + * @return the max #AdbdAuthCallbacks version. + */ +uint32_t adbd_auth_get_max_version(void) __INTRODUCED_IN(30); + +enum AdbdAuthFeature : int32_t { }; -bool adbd_auth_supports_feature(AdbdAuthFeature f); +/** + * Checks if a feature is supported by the framework. See #AdbdAuthFeature. + * + * @param feature the feature to check for support + * @return true if the feature is supported + */ +bool adbd_auth_supports_feature(AdbdAuthFeature feature); -} +#endif //!__ANDROID__ || __ANDROID_API__ >= 30 +__END_DECLS diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt index d01233c960..5857ecb98e 100644 --- a/libs/adbd_auth/libadbd_auth.map.txt +++ b/libs/adbd_auth/libadbd_auth.map.txt @@ -1,13 +1,16 @@ LIBADBD_AUTH { global: - adbd_auth_new; # apex - adbd_auth_delete; # apex - adbd_auth_run; # apex - adbd_auth_get_public_keys; #apex - adbd_auth_notify_auth; # apex - adbd_auth_notify_disconnect; # apex - adbd_auth_prompt_user; # apex - adbd_auth_supports_feature; # apex + adbd_auth_new; # apex introduced=30 + adbd_auth_delete; # apex introduced=30 + adbd_auth_run; # apex introduced=30 + adbd_auth_get_public_keys; #apex introduced=30 + adbd_auth_notify_auth; # apex introduced=30 + adbd_auth_notify_disconnect; # apex introduced=30 + adbd_auth_prompt_user; # apex introduced=30 + adbd_auth_tls_device_connected; # apex introduced=30 + adbd_auth_tls_device_disconnected; # apex introduced=30 + adbd_auth_get_max_version; # apex introduced=30 + adbd_auth_supports_feature; # apex introduced=30 local: *; }; diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 5f9d4004f4..bc541f4d31 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -170,7 +170,6 @@ aidl_interface { name: "libbinder_aidl_test_stub", local_include_dir: "aidl", srcs: [":libbinder_aidl"], - visibility: [":__subpackages__"], vendor_available: true, backend: { java: { diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index 222b32c921..84805ff3d6 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -82,10 +82,10 @@ public: explicit BpMemoryHeap(const sp<IBinder>& impl); virtual ~BpMemoryHeap(); - virtual int getHeapID() const; - virtual void* getBase() const; - virtual size_t getSize() const; - virtual uint32_t getFlags() const; + int getHeapID() const override; + void* getBase() const override; + size_t getSize() const override; + uint32_t getFlags() const override; off_t getOffset() const override; private: diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 4dcd07a776..9e89c57da3 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -1230,6 +1230,11 @@ status_t IPCThreadState::executeCommand(int32_t cmd) if (error < NO_ERROR) reply.setError(error); sendReply(reply, 0); } else { + if (error != OK || reply.dataSize() != 0) { + alog << "oneway function results will be dropped but finished with status " + << statusToString(error) + << " and parcel size " << reply.dataSize() << endl; + } LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); } diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp index f064bd77ce..71d8130df9 100644 --- a/libs/binder/LazyServiceRegistrar.cpp +++ b/libs/binder/LazyServiceRegistrar.cpp @@ -64,8 +64,7 @@ private: bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name, bool allowIsolated, int dumpFlags) { - auto manager = interface_cast<AidlServiceManager>( - ProcessState::self()->getContextObject(nullptr)); + auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager())); bool reRegister = mRegisteredServices.count(name) > 0; std::string regStr = (reRegister) ? "Re-registering" : "Registering"; @@ -114,9 +113,7 @@ Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients void ClientCounterCallback::tryShutdown() { ALOGI("Trying to shut down the service. No clients in use for any service in process."); - // This makes the same assumption as IServiceManager.cpp. Could dedupe if used in more places. - auto manager = interface_cast<AidlServiceManager>( - ProcessState::self()->getContextObject(nullptr)); + auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager())); auto unRegisterIt = mRegisteredServices.begin(); for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) { diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 822247f561..f1077ae140 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -558,6 +558,13 @@ bool Parcel::checkInterface(IBinder* binder) const bool Parcel::enforceInterface(const String16& interface, IPCThreadState* threadState) const { + return enforceInterface(interface.string(), interface.size(), threadState); +} + +bool Parcel::enforceInterface(const char16_t* interface, + size_t len, + IPCThreadState* threadState) const +{ // StrictModePolicy. int32_t strictPolicy = readInt32(); if (threadState == nullptr) { @@ -584,12 +591,15 @@ bool Parcel::enforceInterface(const String16& interface, return false; } // Interface descriptor. - const String16 str(readString16()); - if (str == interface) { + size_t parcel_interface_len; + const char16_t* parcel_interface = readString16Inplace(&parcel_interface_len); + if (len == parcel_interface_len && + (!len || !memcmp(parcel_interface, interface, len * sizeof (char16_t)))) { return true; } else { ALOGW("**** enforceInterface() expected '%s' but read '%s'", - String8(interface).string(), String8(str).string()); + String8(interface, len).string(), + String8(parcel_interface, parcel_interface_len).string()); return false; } } diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index c0f5e31d72..631e8c5cb2 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -112,6 +112,10 @@ sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/) { sp<IBinder> context = getStrongProxyForHandle(0); + if (context == nullptr) { + ALOGW("Not able to get context object on %s.", mDriverName.c_str()); + } + // The root object is special since we get it directly from the driver, it is never // written by Parcell::writeStrongBinder. internal::Stability::tryMarkCompilationUnit(context.get()); diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp index 7a77f6de54..779ed412ba 100644 --- a/libs/binder/Static.cpp +++ b/libs/binder/Static.cpp @@ -64,13 +64,9 @@ private: int mFD; }; -static LogTextOutput gLogTextOutput; -static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); -static FdTextOutput gStderrTextOutput(STDERR_FILENO); - -TextOutput& alog(gLogTextOutput); -TextOutput& aout(gStdoutTextOutput); -TextOutput& aerr(gStderrTextOutput); +TextOutput& alog(*new LogTextOutput()); +TextOutput& aout(*new FdTextOutput(STDOUT_FILENO)); +TextOutput& aerr(*new FdTextOutput(STDERR_FILENO)); // ------------ ProcessState.cpp diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h index 3fccddcc59..edada3d1b5 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h @@ -57,14 +57,14 @@ public: virtual ~MemoryHeapBase(); /* implement IMemoryHeap interface */ - virtual int getHeapID() const; + int getHeapID() const override; /* virtual address of the heap. returns MAP_FAILED in case of error */ - virtual void* getBase() const; + void* getBase() const override; - virtual size_t getSize() const; - virtual uint32_t getFlags() const; - off_t getOffset() const override; + size_t getSize() const override; + uint32_t getFlags() const override; + off_t getOffset() const override; const char* getDevice() const; diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index d4bb85b102..4b1a758f38 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -96,6 +96,9 @@ public: // passed in. bool enforceInterface(const String16& interface, IPCThreadState* threadState = nullptr) const; + bool enforceInterface(const char16_t* interface, + size_t len, + IPCThreadState* threadState = nullptr) const; bool checkInterface(IBinder*) const; void freeData(); diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index e752c45d60..75dcdc8389 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -24,10 +24,13 @@ #include <android-base/logging.h> #include <binder/IPCThreadState.h> +#include <binder/IResultReceiver.h> +#include <private/android_filesystem_config.h> using DeathRecipient = ::android::IBinder::DeathRecipient; using ::android::IBinder; +using ::android::IResultReceiver; using ::android::Parcel; using ::android::sp; using ::android::status_t; @@ -158,6 +161,45 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce binder_status_t status = getClass()->onTransact(this, code, &in, &out); return PruneStatusT(status); + } else if (code == SHELL_COMMAND_TRANSACTION) { + int in = data.readFileDescriptor(); + int out = data.readFileDescriptor(); + int err = data.readFileDescriptor(); + + int argc = data.readInt32(); + std::vector<String8> utf8Args; // owns memory of utf8s + std::vector<const char*> utf8Pointers; // what can be passed over NDK API + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + utf8Args.push_back(String8(data.readString16())); + utf8Pointers.push_back(utf8Args[i].c_str()); + } + + data.readStrongBinder(); // skip over the IShellCallback + sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(data.readStrongBinder()); + + // Shell commands should only be callable by ADB. + uid_t uid = AIBinder_getCallingUid(); + if (uid != AID_ROOT && uid != AID_SHELL) { + if (resultReceiver != nullptr) { + resultReceiver->send(-1); + } + return STATUS_PERMISSION_DENIED; + } + + // Check that the file descriptors are valid. + if (in == STATUS_BAD_TYPE || out == STATUS_BAD_TYPE || err == STATUS_BAD_TYPE) { + if (resultReceiver != nullptr) { + resultReceiver->send(-1); + } + return STATUS_BAD_VALUE; + } + + binder_status_t status = getClass()->handleShellCommand( + this, in, out, err, utf8Pointers.data(), utf8Pointers.size()); + if (resultReceiver != nullptr) { + resultReceiver->send(status); + } + return status; } else { return BBinder::onTransact(code, data, reply, flags); } @@ -266,6 +308,13 @@ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) { clazz->onDump = onDump; } +void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz, + AIBinder_handleShellCommand handleShellCommand) { + CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz"; + + clazz->handleShellCommand = handleShellCommand; +} + void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) { CHECK(who == mWho); diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 5cb68c291b..57794279f2 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -17,6 +17,7 @@ #pragma once #include <android/binder_ibinder.h> +#include <android/binder_shell.h> #include "ibinder_internal.h" #include <atomic> @@ -115,6 +116,7 @@ struct AIBinder_Class { // optional methods for a class AIBinder_onDump onDump; + AIBinder_handleShellCommand handleShellCommand; private: // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h index 83a10488e0..33e4586137 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> @@ -81,9 +86,15 @@ class SharedRefBase { return t->template ref<T>(); } + static void operator delete(void* p) { std::free(p); } + private: std::once_flag mFlagThis; std::weak_ptr<SharedRefBase> mThis; + + // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit + // ownership. Making this operator private to avoid double-ownership. + static void* operator new(size_t s) { return std::malloc(s); } }; /** @@ -108,7 +119,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 +155,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 +215,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 +234,14 @@ 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 + if (AIBinder_Class_setHandleShellCommand != nullptr) { + AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand); + } +#endif return clazz; } @@ -234,6 +270,15 @@ binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, c return interface->dump(fd, args, numArgs); } +#ifdef HAS_BINDER_SHELL_COMMAND +binder_status_t ICInterface::ICInterfaceData::handleShellCommand(AIBinder* binder, int in, int out, + int err, const char** argv, + uint32_t argc) { + std::shared_ptr<ICInterface> interface = getInterface(binder); + return interface->handleShellCommand(in, out, err, argv, argc); +} +#endif + template <typename INTERFACE> SpAIBinder BnCInterface<INTERFACE>::asBinder() { std::lock_guard<std::mutex> l(mMutex); diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h new file mode 100644 index 0000000000..ac46cb80f4 --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h @@ -0,0 +1,32 @@ +/* + * 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_parcel.h> + +__BEGIN_DECLS + +/** + * Gets whether or not FDs are allowed by this AParcel + * + * \return true if FDs are allowed, false if they are not. That is + * if this returns false then AParcel_writeParcelFileDescriptor will + * return STATUS_FDS_NOT_ALLOWED. + */ +bool AParcel_getAllowFds(const AParcel*); + +__END_DECLS
\ No newline at end of file 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..07d89e67fc --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_shell.h @@ -0,0 +1,54 @@ +/* + * 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 + */ +__attribute__((weak)) void AIBinder_Class_setHandleShellCommand( + AIBinder_Class* clazz, AIBinder_handleShellCommand handleShellCommand) __INTRODUCED_IN(30); + +__END_DECLS diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index f3158d7e18..a9eba47380 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -110,6 +110,12 @@ LIBBINDER_NDK30 { # introduced=30 AIBinder_markSystemStability; # apex AIBinder_markVendorStability; # llndk AIBinder_markVintfStability; # apex llndk + AIBinder_Class_setHandleShellCommand; # apex llndk local: *; }; + +LIBBINDER_NDK_PLATFORM { + global: + AParcel_getAllowFds; +}; diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp index f18e118bc9..c33c44f8a8 100644 --- a/libs/binder/ndk/parcel.cpp +++ b/libs/binder/ndk/parcel.cpp @@ -15,6 +15,7 @@ */ #include <android/binder_parcel.h> +#include <android/binder_parcel_platform.h> #include "parcel_internal.h" #include "ibinder_internal.h" @@ -242,24 +243,16 @@ binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binde } binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd) { - std::unique_ptr<ParcelFileDescriptor> parcelFd; - if (fd < 0) { if (fd != -1) { return STATUS_UNKNOWN_ERROR; } - // parcelFd = nullptr - } else { // fd >= 0 - parcelFd = std::make_unique<ParcelFileDescriptor>(unique_fd(fd)); - } - - status_t status = parcel->get()->writeNullableParcelable(parcelFd); - - // ownership is retained by caller - if (parcelFd != nullptr) { - (void)parcelFd->release().release(); + return PruneStatusT(parcel->get()->writeInt32(0)); // null } + status_t status = parcel->get()->writeInt32(1); // not-null + if (status != STATUS_OK) return PruneStatusT(status); + status = parcel->get()->writeDupParcelFileDescriptor(fd); return PruneStatusT(status); } @@ -650,4 +643,8 @@ binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, return ReadArray<int8_t>(parcel, arrayData, allocator); } +bool AParcel_getAllowFds(const AParcel* parcel) { + return parcel->get()->allowFds(); +} + // @END diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp index 513d8c2eba..cb4b20ff9d 100644 --- a/libs/binder/ndk/test/Android.bp +++ b/libs/binder/ndk/test/Android.bp @@ -60,6 +60,7 @@ cc_test { defaults: ["test_libbinder_ndk_test_defaults"], srcs: ["libbinder_ndk_unit_test.cpp"], static_libs: [ + "IBinderNdkUnitTest-cpp", "IBinderNdkUnitTest-ndk_platform", ], test_suites: ["general-tests"], diff --git a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp index 51dd169ec0..fd30d87c76 100644 --- a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <IBinderNdkUnitTest.h> #include <aidl/BnBinderNdkUnitTest.h> #include <aidl/BnEmpty.h> #include <android-base/logging.h> @@ -26,13 +27,16 @@ // warning: this is assuming that libbinder_ndk is using the same copy // of libbinder that we are. #include <binder/IPCThreadState.h> +#include <binder/IResultReceiver.h> +#include <binder/IServiceManager.h> +#include <binder/IShellCallback.h> #include <sys/prctl.h> #include <chrono> #include <condition_variable> #include <mutex> -using ::android::sp; +using namespace android; constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest"; @@ -48,6 +52,14 @@ class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { android::IPCThreadState::self()->flushCommands(); return ndk::ScopedAStatus::ok(); } + binder_status_t handleShellCommand(int /*in*/, int out, int /*err*/, const char** args, + uint32_t numArgs) override { + for (uint32_t i = 0; i < numArgs; i++) { + dprintf(out, "%s", args[i]); + } + fsync(out); + return STATUS_OK; + } }; int generatedService() { @@ -296,6 +308,92 @@ TEST(NdkBinder, SentAidlBinderCanBeDestroyed) { EXPECT_TRUE(destroyed); } +class MyResultReceiver : public BnResultReceiver { + public: + Mutex mMutex; + Condition mCondition; + bool mHaveResult = false; + int32_t mResult = 0; + + virtual void send(int32_t resultCode) { + AutoMutex _l(mMutex); + mResult = resultCode; + mHaveResult = true; + mCondition.signal(); + } + + int32_t waitForResult() { + AutoMutex _l(mMutex); + while (!mHaveResult) { + mCondition.wait(mMutex); + } + return mResult; + } +}; + +class MyShellCallback : public BnShellCallback { + public: + virtual int openFile(const String16& /*path*/, const String16& /*seLinuxContext*/, + const String16& /*mode*/) { + // Empty implementation. + return 0; + } +}; + +bool ReadFdToString(int fd, std::string* content) { + char buf[64]; + ssize_t n; + while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) { + content->append(buf, n); + } + return (n == 0) ? true : false; +} + +std::string shellCmdToString(sp<IBinder> unitTestService, const std::vector<const char*>& args) { + int inFd[2] = {-1, -1}; + int outFd[2] = {-1, -1}; + int errFd[2] = {-1, -1}; + + EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, inFd)); + EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, outFd)); + EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, errFd)); + + sp<MyShellCallback> cb = new MyShellCallback(); + sp<MyResultReceiver> resultReceiver = new MyResultReceiver(); + + Vector<String16> argsVec; + for (int i = 0; i < args.size(); i++) { + argsVec.add(String16(args[i])); + } + status_t error = IBinder::shellCommand(unitTestService, inFd[0], outFd[0], errFd[0], argsVec, + cb, resultReceiver); + EXPECT_EQ(error, android::OK); + + status_t res = resultReceiver->waitForResult(); + EXPECT_EQ(res, android::OK); + + close(inFd[0]); + close(inFd[1]); + close(outFd[0]); + close(errFd[0]); + close(errFd[1]); + + std::string ret; + EXPECT_TRUE(ReadFdToString(outFd[1], &ret)); + close(outFd[1]); + return ret; +} + +TEST(NdkBinder, UseHandleShellCommand) { + static const sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<IBinder> testService = sm->getService(String16(kBinderNdkUnitTestService)); + + EXPECT_EQ("", shellCmdToString(testService, {})); + EXPECT_EQ("", shellCmdToString(testService, {"", ""})); + EXPECT_EQ("Hello world!", shellCmdToString(testService, {"Hello ", "world!"})); + EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"})); +} + int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 5a7f9a97fa..3ee818734a 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -40,7 +40,7 @@ cc_test { }, srcs: ["binderDriverInterfaceTest.cpp"], - test_suites: ["device-tests"], + test_suites: ["device-tests", "vts-core"], } cc_test { @@ -69,7 +69,7 @@ cc_test { "libbinder", "libutils", ], - test_suites: ["device-tests"], + test_suites: ["device-tests", "vts-core"], require_root: true, } @@ -131,7 +131,7 @@ cc_test { "liblog", "libutils", ], - test_suites: ["device-tests"], + test_suites: ["device-tests", "vts-core"], require_root: true, } diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 5e0574ad8a..8cb06e17e9 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -30,6 +30,7 @@ #include <private/binder/binder_module.h> #include <sys/epoll.h> +#include <sys/prctl.h> #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) @@ -106,6 +107,7 @@ pid_t start_server_process(int arg2, bool usePoll = false) if (pid == -1) return pid; if (pid == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); close(pipefd[0]); execv(binderservername, childargv); status = -errno; diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp index 4655e1d8f4..c1861104d3 100644 --- a/libs/binderthreadstate/Android.bp +++ b/libs/binderthreadstate/Android.bp @@ -63,15 +63,3 @@ cc_test { ], 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/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 1465296bac..58126dcf84 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -58,6 +58,7 @@ static std::vector<std::vector<uint32_t>> gPolicyCpus; static std::set<uint32_t> gAllFreqs; static unique_fd gTisMapFd; static unique_fd gConcurrentMapFd; +static unique_fd gUidLastUpdateMapFd; static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) { std::string data; @@ -144,6 +145,10 @@ static bool initGlobals() { unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; if (gConcurrentMapFd < 0) return false; + gUidLastUpdateMapFd = + unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_last_update_map")}; + if (gUidLastUpdateMapFd < 0) return false; + gInitialized = true; return true; } @@ -151,11 +156,22 @@ static bool initGlobals() { static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) { std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s", eventType.c_str(), eventName.c_str()); - int prog_fd = bpf_obj_get(path.c_str()); + int prog_fd = bpfFdGet(path.c_str(), BPF_F_RDONLY); if (prog_fd < 0) return false; return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0; } +static std::optional<uint32_t> getPolicyFreqIdx(uint32_t policy) { + auto path = StringPrintf("/sys/devices/system/cpu/cpufreq/policy%u/scaling_cur_freq", + gPolicyCpus[policy][0]); + auto freqVec = readNumbersFromFile(path); + if (!freqVec.has_value() || freqVec->size() != 1) return {}; + for (uint32_t idx = 0; idx < gPolicyFreqs[policy].size(); ++idx) { + if ((*freqVec)[0] == gPolicyFreqs[policy][idx]) return idx + 1; + } + return {}; +} + // Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes. // Returns true on success, false otherwise. // Tracking is active only once a live process has successfully called this function; if the calling @@ -210,7 +226,9 @@ bool startTrackingUidTimes() { 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; + auto freqIdx = getPolicyFreqIdx(i); + if (!freqIdx.has_value()) return false; + if (writeToMapEntry(policyFreqIdxFd, &i, &(*freqIdx), BPF_ANY)) return false; } gTracking = attachTracepointProgram("sched", "sched_switch") && @@ -263,6 +281,18 @@ std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t ui return out; } +static std::optional<bool> uidUpdatedSince(uint32_t uid, uint64_t lastUpdate, + uint64_t *newLastUpdate) { + uint64_t uidLastUpdate; + if (findMapEntry(gUidLastUpdateMapFd, &uid, &uidLastUpdate)) return {}; + // Updates that occurred during the previous read may have been missed. To mitigate + // this, don't ignore entries updated up to 1s before *lastUpdate + constexpr uint64_t NSEC_PER_SEC = 1000000000; + if (uidLastUpdate + NSEC_PER_SEC < lastUpdate) return false; + if (uidLastUpdate > *newLastUpdate) *newLastUpdate = uidLastUpdate; + return true; +} + // Retrieve the times in ns that each uid spent running at each CPU freq. // Return contains no value on error, otherwise it contains a map from uids to vectors of vectors // using the format: @@ -271,6 +301,14 @@ std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t ui // where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq. std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> getUidsCpuFreqTimes() { + return getUidsUpdatedCpuFreqTimes(nullptr); +} + +// Retrieve the times in ns that each uid spent running at each CPU freq, excluding UIDs that have +// not run since before lastUpdate. +// Return format is the same as getUidsCpuFreqTimes() +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> +getUidsUpdatedCpuFreqTimes(uint64_t *lastUpdate) { if (!gInitialized && !initGlobals()) return {}; time_key_t key, prevKey; std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map; @@ -282,8 +320,14 @@ getUidsCpuFreqTimes() { std::vector<std::vector<uint64_t>> mapFormat; for (const auto &freqList : gPolicyFreqs) mapFormat.emplace_back(freqList.size(), 0); + uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0; std::vector<tis_val_t> vals(gNCpus); do { + if (lastUpdate) { + auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate); + if (!uidUpdated.has_value()) return {}; + if (!*uidUpdated) continue; + } if (findMapEntry(gTisMapFd, &key, vals.data())) return {}; if (map.find(key.uid) == map.end()) map.emplace(key.uid, mapFormat); @@ -299,8 +343,9 @@ getUidsCpuFreqTimes() { } } prevKey = key; - } while (!getNextMapKey(gTisMapFd, &prevKey, &key)); + } while (prevKey = key, !getNextMapKey(gTisMapFd, &prevKey, &key)); if (errno != ENOENT) return {}; + if (lastUpdate && newLastUpdate > *lastUpdate) *lastUpdate = newLastUpdate; return map; } @@ -365,6 +410,15 @@ std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) // where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent // running on the ith cluster, concurrently with tasks on j other cpus in the same cluster. std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes() { + return getUidsUpdatedConcurrentTimes(nullptr); +} + +// Retrieve the times in ns that each uid spent running concurrently with each possible number of +// other tasks on each cluster (policy times) and overall (active times), excluding UIDs that have +// not run since before lastUpdate. +// Return format is the same as getUidsConcurrentTimes() +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsUpdatedConcurrentTimes( + uint64_t *lastUpdate) { if (!gInitialized && !initGlobals()) return {}; time_key_t key, prevKey; std::unordered_map<uint32_t, concurrent_time_t> ret; @@ -379,7 +433,13 @@ std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrent std::vector<concurrent_val_t> vals(gNCpus); std::vector<uint64_t>::iterator activeBegin, activeEnd, policyBegin, policyEnd; + uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0; do { + if (lastUpdate) { + auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate); + if (!uidUpdated.has_value()) return {}; + if (!*uidUpdated) continue; + } if (findMapEntry(gConcurrentMapFd, &key, vals.data())) return {}; if (ret.find(key.uid) == ret.end()) ret.emplace(key.uid, retFormat); @@ -405,8 +465,7 @@ std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrent std::plus<uint64_t>()); } } - prevKey = key; - } while (!getNextMapKey(gConcurrentMapFd, &prevKey, &key)); + } while (prevKey = key, !getNextMapKey(gConcurrentMapFd, &prevKey, &key)); if (errno != ENOENT) return {}; for (const auto &[key, value] : ret) { if (!verifyConcurrentTimes(value)) { @@ -414,6 +473,7 @@ std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrent if (val.has_value()) ret[key] = val.value(); } } + if (lastUpdate && newLastUpdate > *lastUpdate) *lastUpdate = newLastUpdate; return ret; } @@ -446,6 +506,8 @@ bool clearUidTimes(uint32_t uid) { return false; if (deleteMapEntry(gConcurrentMapFd, &key) && errno != ENOENT) return false; } + + if (deleteMapEntry(gUidLastUpdateMapFd, &uid) && errno != ENOENT) return false; return true; } diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h index 49469d8e04..b7600f59e0 100644 --- a/libs/cputimeinstate/cputimeinstate.h +++ b/libs/cputimeinstate/cputimeinstate.h @@ -26,6 +26,8 @@ bool startTrackingUidTimes(); std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid); std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> getUidsCpuFreqTimes(); +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> + getUidsUpdatedCpuFreqTimes(uint64_t *lastUpdate); std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs(); struct concurrent_time_t { @@ -35,6 +37,8 @@ struct concurrent_time_t { std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry = true); std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes(); +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> + getUidsUpdatedConcurrentTimes(uint64_t *lastUpdate); bool clearUidTimes(unsigned int uid); } // namespace bpf diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 23d87fd646..ea2a2008b7 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -115,71 +115,169 @@ TEST(TimeInStateTest, SingleUidTimesConsistent) { } TEST(TimeInStateTest, AllUidTimeInState) { - vector<size_t> sizes; - auto map = getUidsCpuFreqTimes(); - ASSERT_TRUE(map.has_value()); + uint64_t zero = 0; + auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); - ASSERT_FALSE(map->empty()); + ASSERT_FALSE(map->empty()); - auto firstEntry = map->begin()->second; - for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); + vector<size_t> sizes; + auto firstEntry = map->begin()->second; + for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); - for (const auto &vec : *map) { - ASSERT_EQ(vec.second.size(), sizes.size()); - for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]); + for (const auto &vec : *map) { + ASSERT_EQ(vec.second.size(), sizes.size()); + for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]); + } } } -TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { - auto map = getUidsCpuFreqTimes(); - ASSERT_TRUE(map.has_value()); - ASSERT_FALSE(map->empty()); +void TestCheckUpdate(const std::vector<std::vector<uint64_t>> &before, + const std::vector<std::vector<uint64_t>> &after) { + ASSERT_EQ(before.size(), after.size()); + uint64_t sumBefore = 0, sumAfter = 0; + for (size_t i = 0; i < before.size(); ++i) { + ASSERT_EQ(before[i].size(), after[i].size()); + for (size_t j = 0; j < before[i].size(); ++j) { + // Times should never decrease + ASSERT_LE(before[i][j], after[i][j]); + } + sumBefore += std::accumulate(before[i].begin(), before[i].end(), (uint64_t)0); + sumAfter += std::accumulate(after[i].begin(), after[i].end(), (uint64_t)0); + } + ASSERT_LE(sumBefore, sumAfter); + ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC); +} - for (const auto &kv : *map) { - uint32_t uid = kv.first; - auto times1 = kv.second; - auto times2 = getUidCpuFreqTimes(uid); - ASSERT_TRUE(times2.has_value()); - - ASSERT_EQ(times1.size(), times2->size()); - for (uint32_t i = 0; i < times1.size(); ++i) { - ASSERT_EQ(times1[i].size(), (*times2)[i].size()); - for (uint32_t j = 0; j < times1[i].size(); ++j) { - ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC); +TEST(TimeInStateTest, AllUidUpdatedTimeInState) { + uint64_t lastUpdate = 0; + auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate); + ASSERT_TRUE(map1.has_value()); + ASSERT_FALSE(map1->empty()); + ASSERT_NE(lastUpdate, (uint64_t)0); + uint64_t oldLastUpdate = lastUpdate; + + // Sleep briefly to trigger a context switch, ensuring we see at least one update. + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep (&ts, NULL); + + auto map2 = getUidsUpdatedCpuFreqTimes(&lastUpdate); + ASSERT_TRUE(map2.has_value()); + ASSERT_FALSE(map2->empty()); + ASSERT_NE(lastUpdate, oldLastUpdate); + + bool someUidsExcluded = false; + for (const auto &[uid, v] : *map1) { + if (map2->find(uid) == map2->end()) { + someUidsExcluded = true; + break; + } + } + ASSERT_TRUE(someUidsExcluded); + + for (const auto &[uid, newTimes] : *map2) { + ASSERT_NE(map1->find(uid), map1->end()); + ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid], newTimes)); + } +} + +TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { + uint64_t zero = 0; + auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + ASSERT_FALSE(map->empty()); + + for (const auto &kv : *map) { + uint32_t uid = kv.first; + auto times1 = kv.second; + auto times2 = getUidCpuFreqTimes(uid); + ASSERT_TRUE(times2.has_value()); + + ASSERT_EQ(times1.size(), times2->size()); + for (uint32_t i = 0; i < times1.size(); ++i) { + ASSERT_EQ(times1[i].size(), (*times2)[i].size()); + for (uint32_t j = 0; j < times1[i].size(); ++j) { + ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC); + } } } } } TEST(TimeInStateTest, AllUidConcurrentTimes) { - auto map = getUidsConcurrentTimes(); - ASSERT_TRUE(map.has_value()); - ASSERT_FALSE(map->empty()); - - auto firstEntry = map->begin()->second; - for (const auto &kv : *map) { - ASSERT_EQ(kv.second.active.size(), firstEntry.active.size()); - ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size()); - for (size_t i = 0; i < kv.second.policy.size(); ++i) { - ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size()); + uint64_t zero = 0; + auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + ASSERT_FALSE(map->empty()); + + auto firstEntry = map->begin()->second; + for (const auto &kv : *map) { + ASSERT_EQ(kv.second.active.size(), firstEntry.active.size()); + ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size()); + for (size_t i = 0; i < kv.second.policy.size(); ++i) { + ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size()); + } } } } -TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) { - auto map = getUidsConcurrentTimes(); - ASSERT_TRUE(map.has_value()); - for (const auto &kv : *map) { - uint32_t uid = kv.first; - auto times1 = kv.second; - auto times2 = getUidConcurrentTimes(uid); - ASSERT_TRUE(times2.has_value()); - for (uint32_t i = 0; i < times1.active.size(); ++i) { - ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC); +TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) { + uint64_t lastUpdate = 0; + auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate); + ASSERT_TRUE(map1.has_value()); + ASSERT_FALSE(map1->empty()); + ASSERT_NE(lastUpdate, (uint64_t)0); + + // Sleep briefly to trigger a context switch, ensuring we see at least one update. + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep (&ts, NULL); + + uint64_t oldLastUpdate = lastUpdate; + auto map2 = getUidsUpdatedConcurrentTimes(&lastUpdate); + ASSERT_TRUE(map2.has_value()); + ASSERT_FALSE(map2->empty()); + ASSERT_NE(lastUpdate, oldLastUpdate); + + bool someUidsExcluded = false; + for (const auto &[uid, v] : *map1) { + if (map2->find(uid) == map2->end()) { + someUidsExcluded = true; + break; } - for (uint32_t i = 0; i < times1.policy.size(); ++i) { - for (uint32_t j = 0; j < times1.policy[i].size(); ++j) { - ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC); + } + ASSERT_TRUE(someUidsExcluded); + + for (const auto &[uid, newTimes] : *map2) { + ASSERT_NE(map1->find(uid), map1->end()); + ASSERT_NO_FATAL_FAILURE(TestCheckUpdate({(*map1)[uid].active},{newTimes.active})); + ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid].policy, newTimes.policy)); + } +} + +TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) { + uint64_t zero = 0; + auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + for (const auto &kv : *map) { + uint32_t uid = kv.first; + auto times1 = kv.second; + auto times2 = getUidConcurrentTimes(uid); + ASSERT_TRUE(times2.has_value()); + for (uint32_t i = 0; i < times1.active.size(); ++i) { + ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC); + } + for (uint32_t i = 0; i < times1.policy.size(); ++i) { + for (uint32_t j = 0; j < times1.policy[i].size(); ++j) { + ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC); + } } } } @@ -242,45 +340,51 @@ TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) { } TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) { - auto map = getUidsCpuFreqTimes(); - ASSERT_TRUE(map.has_value()); - - bool foundLargeValue = false; - for (const auto &kv : *map) { - for (const auto &timeVec : kv.second) { - for (const auto &time : timeVec) { - ASSERT_LE(time, NSEC_PER_YEAR); - if (time > UINT32_MAX) foundLargeValue = true; + uint64_t zero = 0; + auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + + bool foundLargeValue = false; + for (const auto &kv : *map) { + for (const auto &timeVec : kv.second) { + for (const auto &time : timeVec) { + ASSERT_LE(time, NSEC_PER_YEAR); + if (time > UINT32_MAX) foundLargeValue = true; + } } } + // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using + // uint64_t as expected, we should have some times higher than that. + ASSERT_TRUE(foundLargeValue); } - // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using - // uint64_t as expected, we should have some times higher than that. - ASSERT_TRUE(foundLargeValue); } TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { - auto concurrentMap = getUidsConcurrentTimes(); - ASSERT_TRUE(concurrentMap); - - bool activeFoundLargeValue = false; - bool policyFoundLargeValue = false; - for (const auto &kv : *concurrentMap) { - for (const auto &time : kv.second.active) { - ASSERT_LE(time, NSEC_PER_YEAR); - if (time > UINT32_MAX) activeFoundLargeValue = true; - } - for (const auto &policyTimeVec : kv.second.policy) { - for (const auto &time : policyTimeVec) { + uint64_t zero = 0; + auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; + for (const auto &concurrentMap : maps) { + ASSERT_TRUE(concurrentMap); + + bool activeFoundLargeValue = false; + bool policyFoundLargeValue = false; + for (const auto &kv : *concurrentMap) { + for (const auto &time : kv.second.active) { ASSERT_LE(time, NSEC_PER_YEAR); - if (time > UINT32_MAX) policyFoundLargeValue = true; + if (time > UINT32_MAX) activeFoundLargeValue = true; + } + for (const auto &policyTimeVec : kv.second.policy) { + for (const auto &time : policyTimeVec) { + ASSERT_LE(time, NSEC_PER_YEAR); + if (time > UINT32_MAX) policyFoundLargeValue = true; + } } } + // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using + // uint64_t as expected, we should have some times higher than that. + ASSERT_TRUE(activeFoundLargeValue); + ASSERT_TRUE(policyFoundLargeValue); } - // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using - // uint64_t as expected, we should have some times higher than that. - ASSERT_TRUE(activeFoundLargeValue); - ASSERT_TRUE(policyFoundLargeValue); } TEST(TimeInStateTest, AllUidTimesConsistent) { diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index ba3195a38e..f3d5aab089 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -166,6 +166,10 @@ cc_defaults { "bufferqueue/2.0/types.cpp", ], + whole_static_libs: [ + "LibGuiProperties", + ], + shared_libs: [ "android.hardware.graphics.bufferqueue@1.0", "android.hardware.graphics.bufferqueue@2.0", diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp index c04d9072bb..3215eca50f 100644 --- a/libs/gui/FrameTimestamps.cpp +++ b/libs/gui/FrameTimestamps.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "FrameEvents" +#include <LibGuiProperties.sysprop.h> #include <android-base/stringprintf.h> #include <cutils/compiler.h> // For CC_[UN]LIKELY #include <inttypes.h> @@ -167,6 +168,11 @@ struct FrameNumberEqual { } // namespace +const size_t FrameEventHistory::MAX_FRAME_HISTORY = + sysprop::LibGuiProperties::frame_event_history_size().value_or(8); + +FrameEventHistory::FrameEventHistory() : mFrames(std::vector<FrameEvents>(MAX_FRAME_HISTORY)) {} + FrameEventHistory::~FrameEventHistory() = default; FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) { @@ -348,6 +354,9 @@ std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime( // ConsumerFrameEventHistory // ============================================================================ +ConsumerFrameEventHistory::ConsumerFrameEventHistory() + : mFramesDirty(std::vector<FrameEventDirtyFields>(MAX_FRAME_HISTORY)) {} + ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default; void ConsumerFrameEventHistory::onDisconnect() { @@ -443,9 +452,8 @@ void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber, mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>(); } -void ConsumerFrameEventHistory::getFrameDelta( - FrameEventHistoryDelta* delta, - const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame) { +void ConsumerFrameEventHistory::getFrameDelta(FrameEventHistoryDelta* delta, + const std::vector<FrameEvents>::iterator& frame) { mProducerWantsEvents = true; size_t i = static_cast<size_t>(std::distance(mFrames.begin(), frame)); if (mFramesDirty[i].anyDirty()) { diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h index df02494bf4..4670edda99 100644 --- a/libs/gui/include/gui/FrameTimestamps.h +++ b/libs/gui/include/gui/FrameTimestamps.h @@ -106,6 +106,7 @@ struct CompositorTiming { // producer via deltas. class FrameEventHistory { public: + FrameEventHistory(); virtual ~FrameEventHistory(); FrameEvents* getFrame(uint64_t frameNumber); @@ -113,10 +114,10 @@ public: void checkFencesForCompletion(); void dump(std::string& outString) const; - static constexpr size_t MAX_FRAME_HISTORY = 8; + static const size_t MAX_FRAME_HISTORY; protected: - std::array<FrameEvents, MAX_FRAME_HISTORY> mFrames; + std::vector<FrameEvents> mFrames; CompositorTiming mCompositorTiming; }; @@ -204,6 +205,7 @@ private: // The consumer's interface to FrameEventHistory class ConsumerFrameEventHistory : public FrameEventHistory { public: + ConsumerFrameEventHistory(); ~ConsumerFrameEventHistory() override; void onDisconnect(); @@ -224,9 +226,9 @@ public: private: void getFrameDelta(FrameEventHistoryDelta* delta, - const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame); + const std::vector<FrameEvents>::iterator& frame); - std::array<FrameEventDirtyFields, MAX_FRAME_HISTORY> mFramesDirty; + std::vector<FrameEventDirtyFields> mFramesDirty; size_t mQueueOffset{0}; size_t mCompositionOffset{0}; diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp new file mode 100644 index 0000000000..e7f7c1fc86 --- /dev/null +++ b/libs/gui/sysprop/Android.bp @@ -0,0 +1,7 @@ +sysprop_library { + name: "LibGuiProperties", + srcs: ["*.sysprop"], + api_packages: ["android.sysprop"], + property_owner: "Platform", + vendor_available: true, +} diff --git a/libs/gui/sysprop/LibGuiProperties.sysprop b/libs/gui/sysprop/LibGuiProperties.sysprop new file mode 100644 index 0000000000..0d54711095 --- /dev/null +++ b/libs/gui/sysprop/LibGuiProperties.sysprop @@ -0,0 +1,25 @@ +# 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. + +module: "android.sysprop.LibGuiProperties" +owner: Platform + +# Indicates how many elements should be present in the frame event histories. +prop { + api_name: "frame_event_history_size" + type: Integer + scope: Public + access: Readonly + prop_name: "ro.lib_gui.frame_event_history_size" +} diff --git a/libs/gui/sysprop/api/LibGuiProperties-current.txt b/libs/gui/sysprop/api/LibGuiProperties-current.txt new file mode 100644 index 0000000000..5b7f74e03e --- /dev/null +++ b/libs/gui/sysprop/api/LibGuiProperties-current.txt @@ -0,0 +1,8 @@ +props { + module: "android.sysprop.LibGuiProperties" + prop { + api_name: "frame_event_history_size" + type: Integer + prop_name: "ro.lib_gui.frame_event_history_size" + } +} diff --git a/libs/gui/sysprop/api/LibGuiProperties-latest.txt b/libs/gui/sysprop/api/LibGuiProperties-latest.txt new file mode 100644 index 0000000000..5b7f74e03e --- /dev/null +++ b/libs/gui/sysprop/api/LibGuiProperties-latest.txt @@ -0,0 +1,8 @@ +props { + module: "android.sysprop.LibGuiProperties" + prop { + api_name: "frame_event_history_size" + type: Integer + prop_name: "ro.lib_gui.frame_event_history_size" + } +} |