diff options
Diffstat (limited to 'cmds/statsd/src/packages/UidMap.cpp')
-rw-r--r-- | cmds/statsd/src/packages/UidMap.cpp | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp new file mode 100644 index 000000000000..6e7a613b05a3 --- /dev/null +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, versionCode 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include "guardrail/StatsdStats.h" +#include "packages/UidMap.h" + +#include <android/os/IStatsCompanionService.h> +#include <binder/IServiceManager.h> +#include <utils/Errors.h> + +#include <inttypes.h> + +using namespace android; + +namespace android { +namespace os { +namespace statsd { + +UidMap::UidMap() : mBytesUsed(0) { +} +UidMap::~UidMap() { +} + +bool UidMap::hasApp(int uid, const string& packageName) const { + lock_guard<mutex> lock(mMutex); + + auto range = mMap.equal_range(uid); + for (auto it = range.first; it != range.second; ++it) { + if (it->second.packageName == packageName) { + return true; + } + } + return false; +} + +int64_t UidMap::getAppVersion(int uid, const string& packageName) const { + lock_guard<mutex> lock(mMutex); + + auto range = mMap.equal_range(uid); + for (auto it = range.first; it != range.second; ++it) { + if (it->second.packageName == packageName) { + return it->second.versionCode; + } + } + return 0; +} + +void UidMap::updateMap(const vector<int32_t>& uid, const vector<int64_t>& versionCode, + const vector<String16>& packageName) { + updateMap(time(nullptr) * NS_PER_SEC, uid, versionCode, packageName); +} + +void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, + const vector<int64_t>& versionCode, const vector<String16>& packageName) { + lock_guard<mutex> lock(mMutex); // Exclusively lock for updates. + + mMap.clear(); + for (size_t j = 0; j < uid.size(); j++) { + mMap.insert(make_pair(uid[j], + AppData(string(String8(packageName[j]).string()), versionCode[j]))); + } + + auto snapshot = mOutput.add_snapshots(); + snapshot->set_timestamp_nanos(timestamp); + for (size_t j = 0; j < uid.size(); j++) { + auto t = snapshot->add_package_info(); + t->set_name(string(String8(packageName[j]).string())); + t->set_version(int(versionCode[j])); + t->set_uid(uid[j]); + } + mBytesUsed += snapshot->ByteSize(); + StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); + StatsdStats::getInstance().setUidMapSnapshots(mOutput.snapshots_size()); + ensureBytesUsedBelowLimit(); +} + +void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int64_t& versionCode) { + updateApp(time(nullptr) * NS_PER_SEC, app_16, uid, versionCode); +} + +void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid, + const int64_t& versionCode) { + lock_guard<mutex> lock(mMutex); + + string app = string(String8(app_16).string()); + + // Notify any interested producers that this app has updated + for (auto it : mSubscribers) { + it->notifyAppUpgrade(app, uid, versionCode); + } + + auto log = mOutput.add_changes(); + log->set_deletion(false); + log->set_timestamp_nanos(timestamp); + log->set_app(app); + log->set_uid(uid); + log->set_version(versionCode); + mBytesUsed += log->ByteSize(); + StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); + StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size()); + ensureBytesUsedBelowLimit(); + + auto range = mMap.equal_range(int(uid)); + for (auto it = range.first; it != range.second; ++it) { + if (it->second.packageName == app) { + it->second.versionCode = versionCode; + return; + } + VLOG("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid); + return; + } + + // Otherwise, we need to add an app at this uid. + mMap.insert(make_pair(uid, AppData(app, versionCode))); +} + +void UidMap::ensureBytesUsedBelowLimit() { + size_t limit; + if (maxBytesOverride <= 0) { + limit = StatsdStats::kMaxBytesUsedUidMap; + } else { + limit = maxBytesOverride; + } + while (mBytesUsed > limit) { + VLOG("Bytes used %zu is above limit %zu, need to delete something", mBytesUsed, limit); + if (mOutput.snapshots_size() > 0) { + auto snapshots = mOutput.mutable_snapshots(); + snapshots->erase(snapshots->begin()); // Remove first snapshot. + StatsdStats::getInstance().noteUidMapDropped(1, 0); + } else if (mOutput.changes_size() > 0) { + auto changes = mOutput.mutable_changes(); + changes->DeleteSubrange(0, 1); + StatsdStats::getInstance().noteUidMapDropped(0, 1); + } + mBytesUsed = mOutput.ByteSize(); + } +} + +void UidMap::removeApp(const String16& app_16, const int32_t& uid) { + removeApp(time(nullptr) * NS_PER_SEC, app_16, uid); +} +void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) { + lock_guard<mutex> lock(mMutex); + + string app = string(String8(app_16).string()); + + for (auto it : mSubscribers) { + it->notifyAppRemoved(app, uid); + } + + auto log = mOutput.add_changes(); + log->set_deletion(true); + log->set_timestamp_nanos(timestamp); + log->set_app(app); + log->set_uid(uid); + mBytesUsed += log->ByteSize(); + StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); + StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size()); + ensureBytesUsedBelowLimit(); + + auto range = mMap.equal_range(int(uid)); + for (auto it = range.first; it != range.second; ++it) { + if (it->second.packageName == app) { + mMap.erase(it); + return; + } + } + VLOG("removeApp failed to find the app %s with uid %i to remove", app.c_str(), uid); + return; +} + +void UidMap::addListener(sp<PackageInfoListener> producer) { + lock_guard<mutex> lock(mMutex); // Lock for updates + mSubscribers.insert(producer); +} + +void UidMap::removeListener(sp<PackageInfoListener> producer) { + lock_guard<mutex> lock(mMutex); // Lock for updates + mSubscribers.erase(producer); +} + +void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) { + lock_guard<mutex> lock(mIsolatedMutex); + + mIsolatedUidMap[isolatedUid] = parentUid; +} + +void UidMap::removeIsolatedUid(int isolatedUid, int parentUid) { + lock_guard<mutex> lock(mIsolatedMutex); + + auto it = mIsolatedUidMap.find(isolatedUid); + if (it != mIsolatedUidMap.end()) { + mIsolatedUidMap.erase(it); + } +} + +int UidMap::getParentUidOrSelf(int uid) { + lock_guard<mutex> lock(mIsolatedMutex); + + auto it = mIsolatedUidMap.find(uid); + if (it != mIsolatedUidMap.end()) { + return it->second; + } + return uid; +} + +void UidMap::clearOutput() { + mOutput.Clear(); + // Re-initialize the initial state for the outputs. This results in extra data being uploaded + // but helps ensure we can re-construct the UID->app name, versionCode mapping in server. + auto snapshot = mOutput.add_snapshots(); + for (auto it : mMap) { + auto t = snapshot->add_package_info(); + t->set_name(it.second.packageName); + t->set_version(it.second.versionCode); + t->set_uid(it.first); + } + + // Also update the guardrail trackers. + StatsdStats::getInstance().setUidMapChanges(0); + StatsdStats::getInstance().setUidMapSnapshots(1); + mBytesUsed = snapshot->ByteSize(); + StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); +} + +int64_t UidMap::getMinimumTimestampNs() { + int64_t m = 0; + for (auto it : mLastUpdatePerConfigKey) { + if (m == 0) { + m = it.second; + } else if (it.second < m) { + m = it.second; + } + } + return m; +} + +size_t UidMap::getBytesUsed() { + return mBytesUsed; +} + +UidMapping UidMap::getOutput(const ConfigKey& key) { + return getOutput(time(nullptr) * NS_PER_SEC, key); +} + +UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) { + lock_guard<mutex> lock(mMutex); // Lock for updates + + auto ret = UidMapping(mOutput); // Copy that will be returned. + int64_t prevMin = getMinimumTimestampNs(); + mLastUpdatePerConfigKey[key] = timestamp; + int64_t newMin = getMinimumTimestampNs(); + + if (newMin > prevMin) { + int64_t cutoff_nanos = newMin; + auto snapshots = mOutput.mutable_snapshots(); + auto it_snapshots = snapshots->cbegin(); + while (it_snapshots != snapshots->cend()) { + if (it_snapshots->timestamp_nanos() < cutoff_nanos) { + // it_snapshots now points to the following element. + it_snapshots = snapshots->erase(it_snapshots); + } else { + ++it_snapshots; + } + } + auto deltas = mOutput.mutable_changes(); + auto it_deltas = deltas->cbegin(); + while (it_deltas != deltas->cend()) { + if (it_deltas->timestamp_nanos() < cutoff_nanos) { + // it_deltas now points to the following element. + it_deltas = deltas->erase(it_deltas); + } else { + ++it_deltas; + } + } + } + mBytesUsed = mOutput.ByteSize(); // Compute actual size after potential deletions. + StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); + StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size()); + StatsdStats::getInstance().setUidMapSnapshots(mOutput.snapshots_size()); + return ret; +} + +void UidMap::printUidMap(FILE* out) { + lock_guard<mutex> lock(mMutex); + + for (auto it : mMap) { + fprintf(out, "%s, v%" PRId64 " (%i)\n", it.second.packageName.c_str(), + it.second.versionCode, it.first); + } +} + +void UidMap::OnConfigUpdated(const ConfigKey& key) { + mLastUpdatePerConfigKey[key] = -1; + + // Ensure there is at least one snapshot available since this configuration also needs to know + // what all the uid's represent. + if (mOutput.snapshots_size() == 0) { + sp<IStatsCompanionService> statsCompanion = nullptr; + // Get statscompanion service from service manager + const sp<IServiceManager> sm(defaultServiceManager()); + if (sm != nullptr) { + const String16 name("statscompanion"); + statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name)); + if (statsCompanion == nullptr) { + ALOGW("statscompanion service unavailable!"); + return; + } + statsCompanion->triggerUidSnapshot(); + } + } +} + +void UidMap::OnConfigRemoved(const ConfigKey& key) { + mLastUpdatePerConfigKey.erase(key); +} + +} // namespace statsd +} // namespace os +} // namespace android |