blob: 3018be145ad8c5ef726b1f0a10d184c82b0b0fb8 [file] [log] [blame]
/*
* 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;
}
string UidMap::normalizeAppName(const string& appName) const {
string normalizedName = appName;
std::transform(normalizedName.begin(), normalizedName.end(), normalizedName.begin(), ::tolower);
return normalizedName;
}
std::set<string> UidMap::getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const {
lock_guard<mutex> lock(mMutex);
return getAppNamesFromUidLocked(uid,returnNormalized);
}
std::set<string> UidMap::getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const {
std::set<string> names;
auto range = mMap.equal_range(uid);
for (auto it = range.first; it != range.second; ++it) {
names.insert(returnNormalized ?
normalizeAppName(it->second.packageName) : it->second.packageName);
}
return names;
}
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 appName = string(String8(app_16).string());
// Notify any interested producers that this app has updated
for (auto it : mSubscribers) {
it->notifyAppUpgrade(appName, uid, versionCode);
}
auto log = mOutput.add_changes();
log->set_deletion(false);
log->set_timestamp_nanos(timestamp);
log->set_app(appName);
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 we find the exact same app name and uid, update the app version directly.
if (it->second.packageName == appName) {
it->second.versionCode = versionCode;
return;
}
}
// Otherwise, we need to add an app at this uid.
mMap.insert(make_pair(uid, AppData(appName, 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