/* * Copyright (c) 2020, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "carwatchdogd" #define DEBUG false // STOPSHIP if true. #include "IoOveruseMonitor.h" #include "PackageInfoResolver.h" #include "ServiceManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // NOLINT(build/c++11) namespace android { namespace automotive { namespace watchdog { namespace { using ::aidl::android::automotive::watchdog::IoOveruseStats; using ::aidl::android::automotive::watchdog::IResourceOveruseListener; using ::aidl::android::automotive::watchdog::PerStateBytes; using ::aidl::android::automotive::watchdog::internal::ComponentType; using ::aidl::android::automotive::watchdog::internal::IoOveruseConfiguration; using ::aidl::android::automotive::watchdog::internal::IoUsageStats; using ::aidl::android::automotive::watchdog::internal::PackageIdentifier; using ::aidl::android::automotive::watchdog::internal::PackageInfo; using ::aidl::android::automotive::watchdog::internal::PackageIoOveruseStats; using ::aidl::android::automotive::watchdog::internal::ResourceOveruseConfiguration; using ::aidl::android::automotive::watchdog::internal::ResourceOveruseStats; using ::aidl::android::automotive::watchdog::internal::ResourceStats; using ::aidl::android::automotive::watchdog::internal::UidType; using ::aidl::android::automotive::watchdog::internal::UserPackageIoUsageStats; using ::android::IPCThreadState; using ::android::sp; using ::android::base::EndsWith; using ::android::base::Error; using ::android::base::Result; using ::android::base::StringPrintf; using ::android::base::WriteStringToFd; using ::android::util::ProtoOutputStream; using ::ndk::ScopedAIBinder_DeathRecipient; using ::ndk::SpAIBinder; constexpr int64_t kMaxInt32 = std::numeric_limits::max(); constexpr int64_t kMaxInt64 = std::numeric_limits::max(); // Minimum written bytes to sync the stats with the Watchdog service. constexpr int64_t kMinSyncWrittenBytes = 100 * 1024; // Minimum percentage of threshold to warn killable applications. constexpr double kDefaultIoOveruseWarnPercentage = 80; // Maximum numer of system-wide stats (from periodic monitoring) to cache. constexpr size_t kMaxPeriodicMonitorBufferSize = 1000; constexpr const char* kHelpText = "\n%s dump options:\n" "%s , ,...: Reset resource overuse stats for the given package " "names. Value for this flag is a comma-separated value containing package names.\n"; std::string uniquePackageIdStr(const std::string& name, userid_t userId) { return StringPrintf("%s:%" PRId32, name.c_str(), userId); } std::string uniquePackageIdStr(const PackageIdentifier& id) { return uniquePackageIdStr(id.name, multiuser_get_user_id(id.uid)); } PerStateBytes sum(const PerStateBytes& lhs, const PerStateBytes& rhs) { const auto sum = [](const int64_t& l, const int64_t& r) -> int64_t { return (kMaxInt64 - l) > r ? (l + r) : kMaxInt64; }; PerStateBytes result; result.foregroundBytes = sum(lhs.foregroundBytes, rhs.foregroundBytes); result.backgroundBytes = sum(lhs.backgroundBytes, rhs.backgroundBytes); result.garageModeBytes = sum(lhs.garageModeBytes, rhs.garageModeBytes); return result; } PerStateBytes diff(const PerStateBytes& lhs, const PerStateBytes& rhs) { const auto sub = [](const int64_t& l, const int64_t& r) -> int64_t { return l >= r ? (l - r) : 0; }; PerStateBytes result; result.foregroundBytes = sub(lhs.foregroundBytes, rhs.foregroundBytes); result.backgroundBytes = sub(lhs.backgroundBytes, rhs.backgroundBytes); result.garageModeBytes = sub(lhs.garageModeBytes, rhs.garageModeBytes); return result; } std::tuple calculateStartAndDuration(struct tm currentTm) { // The stats are stored per-day so the start time is always the beginning of the day. auto startTm = currentTm; startTm.tm_sec = 0; startTm.tm_min = 0; startTm.tm_hour = 0; int64_t startTime = static_cast(timegm(&startTm)); int64_t currentEpochSeconds = static_cast(timegm(¤tTm)); return std::make_tuple(startTime, currentEpochSeconds - startTime); } int64_t totalPerStateBytes(PerStateBytes perStateBytes) { const auto sum = [](const int64_t& l, const int64_t& r) -> int64_t { return kMaxInt64 - l > r ? (l + r) : kMaxInt64; }; return sum(perStateBytes.foregroundBytes, sum(perStateBytes.backgroundBytes, perStateBytes.garageModeBytes)); } std::tuple calculateOveruseAndForgivenBytes(PerStateBytes writtenBytes, PerStateBytes threshold) { const auto div = [](const int64_t& l, const int64_t& r) -> int32_t { return r > 0 ? (l / r) : 1; }; const auto mul = [](const int32_t& l, const int32_t& r) -> int32_t { if (l == 0 || r == 0) { return 0; } return (kMaxInt32 / r) > l ? (l * r) : kMaxInt32; }; const auto sum = [](const int32_t& l, const int32_t& r) -> int32_t { return (kMaxInt32 - l) > r ? (l + r) : kMaxInt32; }; int32_t foregroundOveruses = div(writtenBytes.foregroundBytes, threshold.foregroundBytes); int32_t backgroundOveruses = div(writtenBytes.backgroundBytes, threshold.backgroundBytes); int32_t garageModeOveruses = div(writtenBytes.garageModeBytes, threshold.garageModeBytes); int32_t totalOveruses = sum(foregroundOveruses, sum(backgroundOveruses, garageModeOveruses)); PerStateBytes forgivenWriteBytes; forgivenWriteBytes.foregroundBytes = mul(foregroundOveruses, threshold.foregroundBytes); forgivenWriteBytes.backgroundBytes = mul(backgroundOveruses, threshold.backgroundBytes); forgivenWriteBytes.garageModeBytes = mul(garageModeOveruses, threshold.garageModeBytes); return std::make_tuple(totalOveruses, forgivenWriteBytes); } void onBinderDied(void* cookie) { const auto& thiz = ServiceManager::getInstance()->getIoOveruseMonitor(); if (thiz == nullptr) { return; } thiz->handleBinderDeath(cookie); } } // namespace std::tuple calculateStartAndDuration(const time_point_millis& currentTime) { auto timeInSeconds = std::chrono::system_clock::to_time_t(currentTime); struct tm currentGmt; gmtime_r(&timeInSeconds, ¤tGmt); return calculateStartAndDuration(currentGmt); } IoOveruseMonitor::IoOveruseMonitor( const android::sp& watchdogServiceHelper) : mMinSyncWrittenBytes(kMinSyncWrittenBytes), mWatchdogServiceHelper(watchdogServiceHelper), mDeathRegistrationWrapper(sp::make()), mDidReadTodayPrevBootStats(false), mSystemWideWrittenBytes({}), mPeriodicMonitorBufferSize(0), mLastSystemWideIoMonitorTime(0), mUserPackageDailyIoUsageById({}), mIoOveruseWarnPercentage(0), mLastUserPackageIoMonitorTime(time_point_millis::min()), mOveruseListenersByUid({}), mBinderDeathRecipient( ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(onBinderDied))) {} Result IoOveruseMonitor::init() { std::unique_lock writeLock(mRwMutex); if (isInitializedLocked()) { return Error() << "Cannot initialize " << name() << " more than once"; } mPeriodicMonitorBufferSize = static_cast( sysprop::periodicMonitorBufferSize().value_or(kDefaultPeriodicMonitorBufferSize)); if (mPeriodicMonitorBufferSize == 0 || mPeriodicMonitorBufferSize > kMaxPeriodicMonitorBufferSize) { return Error() << "Periodic monitor buffer size cannot be zero or above " << kDefaultPeriodicMonitorBufferSize << ". Received " << mPeriodicMonitorBufferSize; } mIoOveruseWarnPercentage = static_cast( sysprop::ioOveruseWarnPercentage().value_or(kDefaultIoOveruseWarnPercentage)); mIoOveruseConfigs = sp::make(); mPackageInfoResolver = PackageInfoResolver::getInstance(); mPackageInfoResolver->setPackageConfigurations(mIoOveruseConfigs->vendorPackagePrefixes(), mIoOveruseConfigs->packagesToAppCategories()); if (DEBUG) { ALOGD("Initialized %s data processor", name().c_str()); } return {}; } void IoOveruseMonitor::terminate() { ALOGW("Terminating %s", name().c_str()); if (mWriteToDiskThread.joinable()) { mWriteToDiskThread.join(); ALOGI("Write to disk has completed. Proceeding with termination"); } std::unique_lock writeLock(mRwMutex); mWatchdogServiceHelper.clear(); mIoOveruseConfigs.clear(); mSystemWideWrittenBytes.clear(); mUserPackageDailyIoUsageById.clear(); for (const auto& [_, listener] : mOveruseListenersByUid) { AIBinder* aiBinder = listener->asBinder().get(); mDeathRegistrationWrapper->unlinkToDeath(aiBinder, mBinderDeathRecipient.get(), static_cast(aiBinder)); } mOveruseListenersByUid.clear(); if (DEBUG) { ALOGD("Terminated %s data processor", name().c_str()); } return; } void IoOveruseMonitor::onCarWatchdogServiceRegistered() { std::unique_lock writeLock(mRwMutex); if (!mDidReadTodayPrevBootStats) { requestTodayIoUsageStatsLocked(); } } Result IoOveruseMonitor::onPeriodicCollection( time_point_millis time, SystemState systemState, const android::wp& uidStatsCollector, [[maybe_unused]] const android::wp& procStatCollector, ResourceStats* resourceStats) { android::sp uidStatsCollectorSp = uidStatsCollector.promote(); if (uidStatsCollectorSp == nullptr) { return Error() << "Per-UID I/O stats collector must not be null"; } auto timeInSeconds = std::chrono::system_clock::to_time_t(time); std::unique_lock writeLock(mRwMutex); if (!mDidReadTodayPrevBootStats) { requestTodayIoUsageStatsLocked(); } struct tm prevGmt, curGmt; auto mLastUserPackageIoMonitorTimeInSeconds = std::chrono::system_clock::to_time_t(mLastUserPackageIoMonitorTime); gmtime_r(&mLastUserPackageIoMonitorTimeInSeconds, &prevGmt); gmtime_r(&timeInSeconds, &curGmt); if (prevGmt.tm_yday != curGmt.tm_yday || prevGmt.tm_year != curGmt.tm_year) { /* * Date changed so reset the daily I/O usage cache. CarWatchdogService automatically handles * date change on |CarWatchdogService.latestIoOveruseStats| call. */ mUserPackageDailyIoUsageById.clear(); } mLastUserPackageIoMonitorTime = time; const auto [startTime, durationInSeconds] = calculateStartAndDuration(curGmt); auto uidStats = uidStatsCollectorSp->deltaStats(); if (uidStats.empty()) { return {}; } std::unordered_map overusingNativeStats; bool isGarageModeActive = systemState == SystemState::GARAGE_MODE; for (const auto& curUidStats : uidStats) { if (curUidStats.ioStats.sumWriteBytes() == 0 || !curUidStats.hasPackageInfo()) { /* 1. Ignore UIDs with zero written bytes since the last collection because they are * either already accounted for or no writes made since system start. * * 2. UID stats without package info is not useful because the stats isn't attributed to * any package/service. */ continue; } UserPackageIoUsage curUsage(curUidStats.packageInfo, curUidStats.ioStats, isGarageModeActive); if (!mPrevBootIoUsageStatsById.empty()) { if (auto prevBootStats = mPrevBootIoUsageStatsById.find(curUsage.id()); prevBootStats != mPrevBootIoUsageStatsById.end()) { curUsage += prevBootStats->second; mPrevBootIoUsageStatsById.erase(prevBootStats); } } UserPackageIoUsage* dailyIoUsage; if (auto cachedUsage = mUserPackageDailyIoUsageById.find(curUsage.id()); cachedUsage != mUserPackageDailyIoUsageById.end()) { cachedUsage->second += curUsage; dailyIoUsage = &cachedUsage->second; } else { const auto& [it, wasInserted] = mUserPackageDailyIoUsageById.insert( std::pair(curUsage.id(), std::move(curUsage))); dailyIoUsage = &it->second; } const auto threshold = mIoOveruseConfigs->fetchThreshold(dailyIoUsage->packageInfo); const auto deltaWrittenBytes = diff(dailyIoUsage->writtenBytes, dailyIoUsage->forgivenWriteBytes); const auto [currentOveruses, forgivenWriteBytes] = calculateOveruseAndForgivenBytes(deltaWrittenBytes, threshold); dailyIoUsage->totalOveruses += currentOveruses; dailyIoUsage->forgivenWriteBytes = sum(dailyIoUsage->forgivenWriteBytes, forgivenWriteBytes); PackageIoOveruseStats stats; stats.uid = curUidStats.packageInfo.packageIdentifier.uid; stats.shouldNotify = false; stats.forgivenWriteBytes = dailyIoUsage->forgivenWriteBytes; stats.ioOveruseStats.startTime = startTime; stats.ioOveruseStats.durationInSeconds = durationInSeconds; stats.ioOveruseStats.writtenBytes = dailyIoUsage->writtenBytes; stats.ioOveruseStats.totalOveruses = dailyIoUsage->totalOveruses; stats.ioOveruseStats.remainingWriteBytes = diff(threshold, deltaWrittenBytes); stats.ioOveruseStats.killableOnOveruse = mIoOveruseConfigs->isSafeToKill(dailyIoUsage->packageInfo); const auto& remainingWriteBytes = stats.ioOveruseStats.remainingWriteBytes; const auto exceedsWarnThreshold = [&](double remaining, double threshold) { if (threshold == 0) { return true; } double usedPercent = (100 - (remaining / threshold) * 100); return usedPercent > mIoOveruseWarnPercentage; }; bool shouldSyncWatchdogService = (totalPerStateBytes(dailyIoUsage->writtenBytes) - dailyIoUsage->lastSyncedWrittenBytes) >= mMinSyncWrittenBytes; if (currentOveruses > 0) { dailyIoUsage->isPackageWarned = false; /* * Send notifications for native service I/O overuses as well because system listeners * need to be notified of all I/O overuses. */ stats.shouldNotify = true; if (dailyIoUsage->packageInfo.uidType == UidType::NATIVE) { overusingNativeStats[stats.uid] = stats.ioOveruseStats; } shouldSyncWatchdogService = true; } else if (dailyIoUsage->packageInfo.uidType == UidType::APPLICATION && stats.ioOveruseStats.killableOnOveruse && !dailyIoUsage->isPackageWarned && (exceedsWarnThreshold(remainingWriteBytes.foregroundBytes, threshold.foregroundBytes) || exceedsWarnThreshold(remainingWriteBytes.backgroundBytes, threshold.backgroundBytes) || exceedsWarnThreshold(remainingWriteBytes.garageModeBytes, threshold.garageModeBytes))) { /* * No need to warn native services or applications that won't be killed on I/O overuse * as they will be sent a notification when they exceed their daily threshold. */ stats.shouldNotify = true; // Avoid duplicate warning before the daily threshold exceeded notification is sent. dailyIoUsage->isPackageWarned = true; shouldSyncWatchdogService = true; } if (shouldSyncWatchdogService) { dailyIoUsage->lastSyncedWrittenBytes = totalPerStateBytes(dailyIoUsage->writtenBytes); mLatestIoOveruseStats.emplace_back(std::move(stats)); } } if (!overusingNativeStats.empty()) { notifyNativePackagesLocked(overusingNativeStats); } if (mLatestIoOveruseStats.empty()) { return {}; } if (!(resourceStats->resourceOveruseStats).has_value()) { resourceStats->resourceOveruseStats = std::make_optional({}); } resourceStats->resourceOveruseStats->packageIoOveruseStats = mLatestIoOveruseStats; // Clear the cache mLatestIoOveruseStats.clear(); return {}; } Result IoOveruseMonitor::onCustomCollection( time_point_millis time, SystemState systemState, [[maybe_unused]] const std::unordered_set& filterPackages, const android::wp& uidStatsCollector, const android::wp& procStatCollector, ResourceStats* resourceStats) { // Nothing special for custom collection. return onPeriodicCollection(time, systemState, uidStatsCollector, procStatCollector, resourceStats); } Result IoOveruseMonitor::onPeriodicMonitor( time_t time, const android::wp& procDiskStatsCollector, const std::function& alertHandler) { if (procDiskStatsCollector == nullptr) { return Error() << "Proc disk stats collector must not be null"; } std::unique_lock writeLock(mRwMutex); if (mLastSystemWideIoMonitorTime == 0) { /* * Do not record the first disk stats as it reflects the aggregated disks stats since the * system boot up and is not in sync with the polling period. This will lead to spurious * I/O overuse alerting. */ mLastSystemWideIoMonitorTime = time; return {}; } const auto diskStats = procDiskStatsCollector.promote()->deltaSystemWideDiskStats(); mSystemWideWrittenBytes.push_back( {.pollDurationInSecs = difftime(time, mLastSystemWideIoMonitorTime), .bytesInKib = diskStats.numKibWritten}); for (const auto& threshold : mIoOveruseConfigs->systemWideAlertThresholds()) { int64_t accountedWrittenKib = 0; double accountedDurationInSecs = 0; size_t accountedPolls = 0; for (auto rit = mSystemWideWrittenBytes.rbegin(); rit != mSystemWideWrittenBytes.rend(); ++rit) { accountedWrittenKib += rit->bytesInKib; accountedDurationInSecs += rit->pollDurationInSecs; ++accountedPolls; if (accountedDurationInSecs >= threshold.durationInSeconds) { break; } } // Heuristic to handle spurious alerting when the buffer is partially filled. if (const size_t bufferSize = mSystemWideWrittenBytes.size(); accountedPolls == bufferSize && bufferSize < mPeriodicMonitorBufferSize + 1 && threshold.durationInSeconds > accountedDurationInSecs) { continue; } const double thresholdKbps = threshold.writtenBytesPerSecond / 1024.0; if (const auto kbps = accountedWrittenKib / accountedDurationInSecs; kbps >= thresholdKbps) { alertHandler(); break; } } if (mSystemWideWrittenBytes.size() > mPeriodicMonitorBufferSize) { mSystemWideWrittenBytes.erase(mSystemWideWrittenBytes.begin()); // Erase the oldest entry. } mLastSystemWideIoMonitorTime = time; return {}; } Result IoOveruseMonitor::onDump([[maybe_unused]] int fd) const { // TODO(b/183436216): Dump the list of killed/disabled packages. Dump the list of packages that // exceed xx% of their threshold. return {}; } Result IoOveruseMonitor::onDumpProto( [[maybe_unused]] const CollectionIntervals& collectionIntervals, [[maybe_unused]] ProtoOutputStream& outProto) const { // TODO(b/296123577): Dump the list of killed/disabled packages in proto format. return {}; } bool IoOveruseMonitor::dumpHelpText(int fd) const { return WriteStringToFd(StringPrintf(kHelpText, name().c_str(), kResetResourceOveruseStatsFlag), fd); } void IoOveruseMonitor::requestTodayIoUsageStatsLocked() { if (const auto status = mWatchdogServiceHelper->requestTodayIoUsageStats(); !status.isOk()) { // Request made only after CarWatchdogService connection is established. Logging the error // is enough in this case. ALOGE("Failed to request today I/O usage stats collected during previous boot: %s", status.getMessage()); return; } if (DEBUG) { ALOGD("Requested today's I/O usage stats collected during previous boot."); } } Result IoOveruseMonitor::onTodayIoUsageStatsFetched( const std::vector& userPackageIoUsageStats) { std::unique_lock writeLock(mRwMutex); if (mDidReadTodayPrevBootStats) { return {}; } for (const auto& statsEntry : userPackageIoUsageStats) { std::string uniqueId = uniquePackageIdStr(statsEntry.packageName, static_cast(statsEntry.userId)); if (auto it = mUserPackageDailyIoUsageById.find(uniqueId); it != mUserPackageDailyIoUsageById.end()) { it->second += statsEntry.ioUsageStats; continue; } mPrevBootIoUsageStatsById.insert(std::pair(uniqueId, statsEntry.ioUsageStats)); } mDidReadTodayPrevBootStats = true; return {}; } void IoOveruseMonitor::notifyNativePackagesLocked( const std::unordered_map& statsByUid) { for (const auto& [uid, ioOveruseStats] : statsByUid) { IResourceOveruseListener* listener; if (const auto it = mOveruseListenersByUid.find(uid); it == mOveruseListenersByUid.end()) { continue; } else { listener = it->second.get(); } aidl::android::automotive::watchdog::ResourceOveruseStats stats; stats.set( ioOveruseStats); listener->onOveruse(stats); } if (DEBUG) { ALOGD("Notified native packages on I/O overuse"); } } Result IoOveruseMonitor::updateResourceOveruseConfigurations( const std::vector& configs) { std::unique_lock writeLock(mRwMutex); if (!isInitializedLocked()) { return Error(EX_ILLEGAL_STATE) << name() << " is not initialized"; } if (const auto result = mIoOveruseConfigs->update(configs); !result.ok()) { return result; } // When mWriteToDiskThread is already active, don't create a new thread to perform the same // work. This thread writes to disk only after acquiring the mRwMutex write lock and the below // check is performed after acquiring the same write lock. Thus, if the thread is still active // and mIsWriteToDiskPending is true at this point, it indicates the thread hasn't performed // the write and will write the latest updated configs when it executes. if (bool isJoinable = mWriteToDiskThread.joinable(); isJoinable && mIsWriteToDiskPending) { ALOGW("Skipping resource overuse configs write to disk due to ongoing write"); return {}; } else if (isJoinable) { // At this point we know the thread has completed execution. Join the thread before // creating a new one. Failure to join can lead to a crash since std::thread cannot // destruct a thread object without first calling join. mWriteToDiskThread.join(); } mIsWriteToDiskPending = true; mWriteToDiskThread = std::thread([&]() { ALOGI("Writing resource overuse configs to disk"); if (set_sched_policy(0, SP_BACKGROUND) != 0) { ALOGW("Failed to set background scheduling priority for writing resource overuse " "configs to disk"); } if (int result = pthread_setname_np(pthread_self(), "ResOveruseCfgWr"); result != 0) { ALOGE("Failed to set thread name to 'ResOveruseCfgWr'"); } std::unique_lock writeLock(mRwMutex); if (mIoOveruseConfigs == nullptr) { ALOGE("IoOveruseConfigs instance is null"); } else if (const auto result = mIoOveruseConfigs->writeToDisk(); !result.ok()) { ALOGE("Failed to write resource overuse configs to disk: %s", result.error().message().c_str()); } else { ALOGI("Successfully wrote resource overuse configs to disk"); } mIsWriteToDiskPending = false; }); return {}; } Result IoOveruseMonitor::getResourceOveruseConfigurations( std::vector* configs) const { std::shared_lock readLock(mRwMutex); if (!isInitializedLocked()) { return Error(EX_ILLEGAL_STATE) << name() << " is not initialized"; } mIoOveruseConfigs->get(configs); return {}; } Result IoOveruseMonitor::addIoOveruseListener( const std::shared_ptr& listener) { if (listener == nullptr) { return Error(EX_ILLEGAL_ARGUMENT) << "Must provide non-null listener"; } auto binder = listener->asBinder(); pid_t callingPid = IPCThreadState::self()->getCallingPid(); uid_t callingUid = IPCThreadState::self()->getCallingUid(); { std::unique_lock writeLock(mRwMutex); if (!isInitializedLocked()) { // mBinderDeathRecipient is initialized inside init. return Error(EX_ILLEGAL_STATE) << "Service is not initialized"; } if (findListenerAndProcessLocked(reinterpret_cast(binder.get()), nullptr)) { ALOGW("Failed to register the I/O overuse listener (pid: %d, uid: %d) as it is already " "registered", callingPid, callingUid); return {}; } mOveruseListenersByUid[callingUid] = listener; } AIBinder* aiBinder = binder.get(); auto status = mDeathRegistrationWrapper->linkToDeath(aiBinder, mBinderDeathRecipient.get(), static_cast(aiBinder)); if (!status.isOk()) { std::unique_lock writeLock(mRwMutex); if (const auto& it = mOveruseListenersByUid.find(callingUid); it != mOveruseListenersByUid.end() && it->second->asBinder() == binder) { mOveruseListenersByUid.erase(it); } return Error(EX_ILLEGAL_STATE) << "Failed to add I/O overuse listener: (pid " << callingPid << ", uid: " << callingUid << ") is dead"; } if (DEBUG) { ALOGD("Added I/O overuse listener for uid: %d", callingUid); } return {}; } Result IoOveruseMonitor::removeIoOveruseListener( const std::shared_ptr& listener) { if (listener == nullptr) { return Error(EX_ILLEGAL_ARGUMENT) << "Must provide non-null listener"; } std::unique_lock writeLock(mRwMutex); if (!isInitializedLocked()) { // mBinderDeathRecipient is initialized inside init. return Error(EX_ILLEGAL_STATE) << "Service is not initialized"; } const auto processor = [&](ListenersByUidMap& listeners, ListenersByUidMap::const_iterator it) { AIBinder* aiBinder = it->second->asBinder().get(); mDeathRegistrationWrapper->unlinkToDeath(aiBinder, mBinderDeathRecipient.get(), static_cast(aiBinder)); listeners.erase(it); }; if (!findListenerAndProcessLocked(reinterpret_cast(listener->asBinder().get()), processor)) { return Error(EX_ILLEGAL_ARGUMENT) << "Listener is not previously registered"; } if (DEBUG) { ALOGD("Removed I/O overuse listener for uid: %d", IPCThreadState::self()->getCallingUid()); } return {}; } Result IoOveruseMonitor::getIoOveruseStats(IoOveruseStats* ioOveruseStats) const { if (!isInitialized()) { return Error(EX_ILLEGAL_STATE) << "I/O overuse monitor is not initialized"; } uid_t callingUid = IPCThreadState::self()->getCallingUid(); const auto packageInfosByUid = mPackageInfoResolver->getPackageInfosForUids({callingUid}); const PackageInfo* packageInfo; if (const auto it = packageInfosByUid.find(callingUid); it == packageInfosByUid.end()) { return Error(EX_ILLEGAL_ARGUMENT) << "Package information not available for calling UID(" << callingUid << ")"; } else { packageInfo = &it->second; } std::shared_lock readLock(mRwMutex); const UserPackageIoUsage* dailyIoUsage; if (const auto it = mUserPackageDailyIoUsageById.find( uniquePackageIdStr(packageInfo->packageIdentifier)); it == mUserPackageDailyIoUsageById.end()) { return Error(EX_ILLEGAL_ARGUMENT) << "Calling UID " << callingUid << " doesn't have I/O overuse stats"; } else { dailyIoUsage = &it->second; } ioOveruseStats->killableOnOveruse = mIoOveruseConfigs->isSafeToKill(*packageInfo); const auto thresholdBytes = mIoOveruseConfigs->fetchThreshold(*packageInfo); ioOveruseStats->remainingWriteBytes = diff(thresholdBytes, diff(dailyIoUsage->writtenBytes, dailyIoUsage->forgivenWriteBytes)); ioOveruseStats->totalOveruses = dailyIoUsage->totalOveruses; ioOveruseStats->writtenBytes = dailyIoUsage->writtenBytes; const auto [startTime, durationInSeconds] = calculateStartAndDuration(mLastUserPackageIoMonitorTime); ioOveruseStats->startTime = startTime; ioOveruseStats->durationInSeconds = durationInSeconds; if (DEBUG) { ALOGD("Returning I/O overuse stats for uid: %d", callingUid); } return {}; } Result IoOveruseMonitor::resetIoOveruseStats(const std::vector& packageNames) { if (const auto status = mWatchdogServiceHelper->resetResourceOveruseStats(packageNames); !status.isOk()) { return Error() << "Failed to reset stats in watchdog service: " << status.getDescription(); } std::unordered_set uniquePackageNames; std::copy(packageNames.begin(), packageNames.end(), std::inserter(uniquePackageNames, uniquePackageNames.end())); std::unique_lock writeLock(mRwMutex); for (auto& [key, usage] : mUserPackageDailyIoUsageById) { if (uniquePackageNames.find(usage.packageInfo.packageIdentifier.name) != uniquePackageNames.end()) { usage.resetStats(); } } return {}; } void IoOveruseMonitor::removeStatsForUser(userid_t userId) { std::unique_lock writeLock(mRwMutex); for (auto it = mUserPackageDailyIoUsageById.begin(); it != mUserPackageDailyIoUsageById.end();) { if (multiuser_get_user_id(it->second.packageInfo.packageIdentifier.uid) == userId) { it = mUserPackageDailyIoUsageById.erase(it); } else { ++it; } } // |mPrevBootIoUsageStatsById| keys are constructed using |uniquePackageIdStr| method. Thus, the // key suffix would contain the userId. The value in this map is |IoUsageStats|, which doesn't // contain the userId, so this is the only way to delete cached previous boot stats for // the removed user. std::string keySuffix = StringPrintf(":%" PRId32, userId); for (auto it = mPrevBootIoUsageStatsById.begin(); it != mPrevBootIoUsageStatsById.end();) { if (EndsWith(it->first, keySuffix)) { it = mPrevBootIoUsageStatsById.erase(it); } else { ++it; } } for (auto it = mLatestIoOveruseStats.begin(); it != mLatestIoOveruseStats.end();) { if (multiuser_get_user_id(it->uid) == userId) { it = mLatestIoOveruseStats.erase(it); } else { ++it; } } } void IoOveruseMonitor::handleBinderDeath(void* cookie) { uintptr_t cookieId = reinterpret_cast(cookie); std::unique_lock writeLock(mRwMutex); findListenerAndProcessLocked(cookieId, [&](ListenersByUidMap& listeners, ListenersByUidMap::const_iterator it) { ALOGW("Resource overuse notification handler died for uid(%d)", it->first); listeners.erase(it); }); } bool IoOveruseMonitor::findListenerAndProcessLocked(uintptr_t binderPtrId, const Processor& processor) { for (auto it = mOveruseListenersByUid.begin(); it != mOveruseListenersByUid.end(); ++it) { uintptr_t curBinderPtrId = reinterpret_cast(it->second->asBinder().get()); if (curBinderPtrId != binderPtrId) { continue; } if (processor != nullptr) { processor(mOveruseListenersByUid, it); } return true; } return false; } IoOveruseMonitor::UserPackageIoUsage::UserPackageIoUsage(const PackageInfo& pkgInfo, const UidIoStats& uidIoStats, const bool isGarageModeActive) { packageInfo = pkgInfo; if (isGarageModeActive) { writtenBytes.garageModeBytes = uidIoStats.sumWriteBytes(); } else { writtenBytes.foregroundBytes = uidIoStats.metrics[WRITE_BYTES][FOREGROUND]; writtenBytes.backgroundBytes = uidIoStats.metrics[WRITE_BYTES][BACKGROUND]; } } IoOveruseMonitor::UserPackageIoUsage& IoOveruseMonitor::UserPackageIoUsage::operator+=( const UserPackageIoUsage& r) { if (id() == r.id()) { packageInfo = r.packageInfo; } writtenBytes = sum(writtenBytes, r.writtenBytes); return *this; } IoOveruseMonitor::UserPackageIoUsage& IoOveruseMonitor::UserPackageIoUsage::operator+=( const IoUsageStats& ioUsageStats) { writtenBytes = sum(writtenBytes, ioUsageStats.writtenBytes); forgivenWriteBytes = sum(forgivenWriteBytes, ioUsageStats.forgivenWriteBytes); totalOveruses += ioUsageStats.totalOveruses; return *this; } const std::string IoOveruseMonitor::UserPackageIoUsage::id() const { return uniquePackageIdStr(packageInfo.packageIdentifier); } void IoOveruseMonitor::UserPackageIoUsage::resetStats() { writtenBytes = {}; forgivenWriteBytes = {}; totalOveruses = 0; isPackageWarned = false; lastSyncedWrittenBytes = 0; } } // namespace watchdog } // namespace automotive } // namespace android