MediaMetrics: Move package name from uid to service utilities

Allows sharing class with AudioFlinger.

Bug: 129355845
Bug: 138583596
Test: atest mediametrics_tests
Test: adb shell dumpsys media.metrics
Change-Id: I6654dc9456dcd7c93821a8d83dd75d96b658f254
gugelfrei
Andy Hung 5 years ago
parent 370e94071d
commit a85efab79e

@ -29,6 +29,7 @@ cc_library {
"libc_malloc_debug_backtrace",
],
shared_libs: [
"libaudioutils", // for clock.h
"libbinder",
"libcutils",
"liblog",

@ -16,6 +16,7 @@
#define LOG_TAG "ServiceUtilities"
#include <audio_utils/clock.h>
#include <binder/AppOpsManager.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@ -24,6 +25,7 @@
#include <iterator>
#include <algorithm>
#include <pwd.h>
/* When performing permission checks we do not use permission cache for
* runtime permissions (protection level dangerous) as they may change at
@ -329,4 +331,128 @@ void MediaPackageManager::dump(int fd, int spaces) const {
}
}
// How long we hold info before we re-fetch it (24 hours) if we found it previously.
static constexpr nsecs_t INFO_EXPIRATION_NS = 24 * 60 * 60 * NANOS_PER_SECOND;
// Maximum info records we retain before clearing everything.
static constexpr size_t INFO_CACHE_MAX = 1000;
// The original code is from MediaMetricsService.cpp.
mediautils::UidInfo::Info mediautils::UidInfo::getInfo(uid_t uid)
{
const nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
struct mediautils::UidInfo::Info info;
{
std::lock_guard _l(mLock);
auto it = mInfoMap.find(uid);
if (it != mInfoMap.end()) {
info = it->second;
ALOGV("%s: uid %d expiration %lld now %lld",
__func__, uid, (long long)info.expirationNs, (long long)now);
if (info.expirationNs <= now) {
// purge the stale entry and fall into re-fetching
ALOGV("%s: entry for uid %d expired, now %lld",
__func__, uid, (long long)now);
mInfoMap.erase(it);
info.uid = (uid_t)-1; // this is always fully overwritten
}
}
}
// if we did not find it in our map, look it up
if (info.uid == (uid_t)(-1)) {
sp<IServiceManager> sm = defaultServiceManager();
sp<content::pm::IPackageManagerNative> package_mgr;
if (sm.get() == nullptr) {
ALOGE("%s: Cannot find service manager", __func__);
} else {
sp<IBinder> binder = sm->getService(String16("package_native"));
if (binder.get() == nullptr) {
ALOGE("%s: Cannot find package_native", __func__);
} else {
package_mgr = interface_cast<content::pm::IPackageManagerNative>(binder);
}
}
// find package name
std::string pkg;
if (package_mgr != nullptr) {
std::vector<std::string> names;
binder::Status status = package_mgr->getNamesForUids({(int)uid}, &names);
if (!status.isOk()) {
ALOGE("%s: getNamesForUids failed: %s",
__func__, status.exceptionMessage().c_str());
} else {
if (!names[0].empty()) {
pkg = names[0].c_str();
}
}
}
if (pkg.empty()) {
struct passwd pw{}, *result;
char buf[8192]; // extra buffer space - should exceed what is
// required in struct passwd_pw (tested),
// and even then this is only used in backup
// when the package manager is unavailable.
if (getpwuid_r(uid, &pw, buf, sizeof(buf), &result) == 0
&& result != nullptr
&& result->pw_name != nullptr) {
pkg = result->pw_name;
}
}
// strip any leading "shared:" strings that came back
if (pkg.compare(0, 7, "shared:") == 0) {
pkg.erase(0, 7);
}
// determine how pkg was installed and the versionCode
std::string installer;
int64_t versionCode = 0;
bool notFound = false;
if (pkg.empty()) {
pkg = std::to_string(uid); // not found
notFound = true;
} else if (strchr(pkg.c_str(), '.') == nullptr) {
// not of form 'com.whatever...'; assume internal
// so we don't need to look it up in package manager.
} else if (package_mgr.get() != nullptr) {
String16 pkgName16(pkg.c_str());
binder::Status status = package_mgr->getInstallerForPackage(pkgName16, &installer);
if (!status.isOk()) {
ALOGE("%s: getInstallerForPackage failed: %s",
__func__, status.exceptionMessage().c_str());
}
// skip if we didn't get an installer
if (status.isOk()) {
status = package_mgr->getVersionCodeForPackage(pkgName16, &versionCode);
if (!status.isOk()) {
ALOGE("%s: getVersionCodeForPackage failed: %s",
__func__, status.exceptionMessage().c_str());
}
}
ALOGV("%s: package '%s' installed by '%s' versioncode %lld",
__func__, pkg.c_str(), installer.c_str(), (long long)versionCode);
}
// add it to the map, to save a subsequent lookup
std::lock_guard _l(mLock);
// first clear if we have too many cached elements. This would be rare.
if (mInfoMap.size() >= INFO_CACHE_MAX) mInfoMap.clear();
// always overwrite
info.uid = uid;
info.package = std::move(pkg);
info.installer = std::move(installer);
info.versionCode = versionCode;
info.expirationNs = now + (notFound ? 0 : INFO_EXPIRATION_NS);
ALOGV("%s: adding uid %d package '%s' expirationNs: %lld",
__func__, uid, info.package.c_str(), (long long)info.expirationNs);
mInfoMap[uid] = info;
}
return info;
}
} // namespace android

@ -28,6 +28,7 @@
#include <map>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
namespace android {
@ -118,6 +119,40 @@ private:
using Packages = std::vector<Package>;
std::map<uid_t, Packages> mDebugLog;
};
}
namespace mediautils {
/**
* This class is used to retrieve (and cache) package information
* for a given uid.
*/
class UidInfo {
public:
struct Info {
uid_t uid = -1; // uid used for lookup.
std::string package; // package name.
std::string installer; // installer for the package (e.g. preload, play store).
int64_t versionCode = 0; // reported version code.
int64_t expirationNs = 0; // after this time in SYSTEM_TIME_REALTIME we refetch.
};
/**
* Returns the package information for a UID.
*
* The package name will be the uid if we cannot find the associated name.
*
* \param uid is the uid of the app or service.
*/
Info getInfo(uid_t uid);
private:
std::mutex mLock;
// TODO: use concurrent hashmap with striped lock.
std::unordered_map<uid_t, Info> mInfoMap; // GUARDED_BY(mLock)
};
} // namespace mediautils
} // namespace android
#endif // ANDROID_MEDIAUTILS_SERVICEUTILITIES_H

@ -12,6 +12,7 @@ cc_binary {
"libbinder",
"liblog",
"libmediametricsservice",
"libmediautils",
"libutils",
],
@ -52,6 +53,7 @@ cc_library_shared {
"libbinder",
"liblog",
"libmediametrics",
"libmediautils",
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",

@ -57,6 +57,25 @@ nsecs_t MediaMetricsService::roundTime(nsecs_t timeNs)
return (timeNs + NANOS_PER_SECOND / 2) / NANOS_PER_SECOND * NANOS_PER_SECOND;
}
/* static */
bool MediaMetricsService::useUidForPackage(
const std::string& package, const std::string& installer)
{
if (strchr(package.c_str(), '.') == NULL) {
return false; // not of form 'com.whatever...'; assume internal and ok
} else if (strncmp(package.c_str(), "android.", 8) == 0) {
return false; // android.* packages are assumed fine
} else if (strncmp(installer.c_str(), "com.android.", 12) == 0) {
return false; // from play store
} else if (strncmp(installer.c_str(), "com.google.", 11) == 0) {
return false; // some google source
} else if (strcmp(installer.c_str(), "preload") == 0) {
return false; // preloads
} else {
return true; // we're not sure where it came from, use uid only.
}
}
MediaMetricsService::MediaMetricsService()
: mMaxRecords(kMaxRecords),
mMaxRecordAgeNs(kMaxRecordAgeNs),
@ -112,14 +131,19 @@ status_t MediaMetricsService::submitInternal(mediametrics::Item *item, bool rele
break;
}
// Overwrite package name and version if the caller was untrusted.
if (!isTrusted) {
mUidInfo.setPkgInfo(item, item->getUid(), true, true);
} else if (item->getPkgName().empty()) {
// empty, so fill out both parts
mUidInfo.setPkgInfo(item, item->getUid(), true, true);
} else {
// trusted, provided a package, do nothing
// Overwrite package name and version if the caller was untrusted or empty
if (!isTrusted || item->getPkgName().empty()) {
const uid_t uid = item->getUid();
mediautils::UidInfo::Info info = mUidInfo.getInfo(uid);
if (useUidForPackage(info.package, info.installer)) {
// remove uid information of unknown installed packages.
// TODO: perhaps this can be done just before uploading to Westworld.
item->setPkgName(std::to_string(uid));
item->setPkgVersionCode(0);
} else {
item->setPkgName(info.package);
item->setPkgVersionCode(info.versionCode);
}
}
ALOGV("%s: given uid %d; sanitized uid: %d sanitized pkg: %s "
@ -450,150 +474,4 @@ bool MediaMetricsService::isRateLimited(mediametrics::Item *) const
return false;
}
// How long we hold package info before we re-fetch it
constexpr nsecs_t PKG_EXPIRATION_NS = 30 * 60 * NANOS_PER_SECOND; // 30 minutes
// give me the package name, perhaps going to find it
// manages its own mutex operations internally
void MediaMetricsService::UidInfo::setPkgInfo(
mediametrics::Item *item, uid_t uid, bool setName, bool setVersion)
{
ALOGV("%s: uid=%d", __func__, uid);
if (!setName && !setVersion) {
return; // setting nothing? strange
}
const nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
struct UidToPkgInfo mapping;
{
std::lock_guard _l(mUidInfoLock);
auto it = mPkgMappings.find(uid);
if (it != mPkgMappings.end()) {
mapping = it->second;
ALOGV("%s: uid %d expiration %lld now %lld",
__func__, uid, (long long)mapping.expiration, (long long)now);
if (mapping.expiration <= now) {
// purge the stale entry and fall into re-fetching
ALOGV("%s: entry for uid %d expired, now %lld",
__func__, uid, (long long)now);
mPkgMappings.erase(it);
mapping.uid = (uid_t)-1; // this is always fully overwritten
}
}
}
// if we did not find it
if (mapping.uid == (uid_t)(-1)) {
std::string pkg;
std::string installer;
int64_t versionCode = 0;
const struct passwd *pw = getpwuid(uid);
if (pw) {
pkg = pw->pw_name;
}
sp<IServiceManager> sm = defaultServiceManager();
sp<content::pm::IPackageManagerNative> package_mgr;
if (sm.get() == nullptr) {
ALOGE("%s: Cannot find service manager", __func__);
} else {
sp<IBinder> binder = sm->getService(String16("package_native"));
if (binder.get() == nullptr) {
ALOGE("%s: Cannot find package_native", __func__);
} else {
package_mgr = interface_cast<content::pm::IPackageManagerNative>(binder);
}
}
if (package_mgr != nullptr) {
std::vector<int> uids;
std::vector<std::string> names;
uids.push_back(uid);
binder::Status status = package_mgr->getNamesForUids(uids, &names);
if (!status.isOk()) {
ALOGE("%s: getNamesForUids failed: %s",
__func__, status.exceptionMessage().c_str());
} else {
if (!names[0].empty()) {
pkg = names[0].c_str();
}
}
}
// strip any leading "shared:" strings that came back
if (pkg.compare(0, 7, "shared:") == 0) {
pkg.erase(0, 7);
}
// determine how pkg was installed and the versionCode
if (pkg.empty()) {
pkg = std::to_string(uid); // no name for us to manage
} else if (strchr(pkg.c_str(), '.') == NULL) {
// not of form 'com.whatever...'; assume internal and ok
} else if (strncmp(pkg.c_str(), "android.", 8) == 0) {
// android.* packages are assumed fine
} else if (package_mgr.get() != nullptr) {
String16 pkgName16(pkg.c_str());
binder::Status status = package_mgr->getInstallerForPackage(pkgName16, &installer);
if (!status.isOk()) {
ALOGE("%s: getInstallerForPackage failed: %s",
__func__, status.exceptionMessage().c_str());
}
// skip if we didn't get an installer
if (status.isOk()) {
status = package_mgr->getVersionCodeForPackage(pkgName16, &versionCode);
if (!status.isOk()) {
ALOGE("%s: getVersionCodeForPackage failed: %s",
__func__, status.exceptionMessage().c_str());
}
}
ALOGV("%s: package '%s' installed by '%s' versioncode %lld",
__func__, pkg.c_str(), installer.c_str(), (long long)versionCode);
if (strncmp(installer.c_str(), "com.android.", 12) == 0) {
// from play store, we keep info
} else if (strncmp(installer.c_str(), "com.google.", 11) == 0) {
// some google source, we keep info
} else if (strcmp(installer.c_str(), "preload") == 0) {
// preloads, we keep the info
} else if (installer.c_str()[0] == '\0') {
// sideload (no installer); report UID only
pkg = std::to_string(uid);
versionCode = 0;
} else {
// unknown installer; report UID only
pkg = std::to_string(uid);
versionCode = 0;
}
} else {
// unvalidated by package_mgr just send uid.
pkg = std::to_string(uid);
}
// add it to the map, to save a subsequent lookup
std::lock_guard _l(mUidInfoLock);
// always overwrite
mapping.uid = uid;
mapping.pkg = std::move(pkg);
mapping.installer = std::move(installer);
mapping.versionCode = versionCode;
mapping.expiration = now + PKG_EXPIRATION_NS;
ALOGV("%s: adding uid %d pkg '%s' expiration: %lld",
__func__, uid, mapping.pkg.c_str(), (long long)mapping.expiration);
mPkgMappings[uid] = mapping;
}
if (mapping.uid != (uid_t)(-1)) {
if (setName) {
item->setPkgName(mapping.pkg);
}
if (setVersion) {
item->setPkgVersionCode(mapping.versionCode);
}
}
}
} // namespace android

@ -24,6 +24,7 @@
// IMediaMetricsService must include Vector, String16, Errors
#include <media/IMediaMetricsService.h>
#include <mediautils/ServiceUtilities.h>
#include <utils/String8.h>
#include "AudioAnalytics.h"
@ -62,6 +63,11 @@ public:
*/
static nsecs_t roundTime(nsecs_t timeNs);
/**
* Returns true if we should use uid for package name when uploading to WestWorld.
*/
static bool useUidForPackage(const std::string& package, const std::string& installer);
protected:
// Internal call where release is true if ownership of item is transferred
@ -96,27 +102,10 @@ private:
const size_t mMaxRecordsExpiredAtOnce;
const int mDumpProtoDefault;
class UidInfo {
public:
void setPkgInfo(mediametrics::Item *item, uid_t uid, bool setName, bool setVersion);
private:
std::mutex mUidInfoLock;
struct UidToPkgInfo {
uid_t uid = -1;
std::string pkg;
std::string installer;
int64_t versionCode = 0;
nsecs_t expiration = 0; // TODO: remove expiration.
};
// TODO: use concurrent hashmap with striped lock.
std::unordered_map<uid_t, struct UidToPkgInfo> mPkgMappings; // GUARDED_BY(mUidInfoLock)
} mUidInfo; // mUidInfo can be accessed without lock (locked internally)
std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
mediautils::UidInfo mUidInfo; // mUidInfo can be accessed without lock (locked internally)
mediametrics::AudioAnalytics mAudioAnalytics;
std::mutex mLock;

@ -17,6 +17,7 @@ cc_test {
"liblog",
"libmediametrics",
"libmediametricsservice",
"libmediautils",
"libutils",
],

@ -89,6 +89,32 @@ TEST(mediametrics_tests, instantiate) {
mediaMetrics->dump(fileno(stdout), {} /* args */);
}
TEST(mediametrics_tests, package_installer_check) {
ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
"abcd", "installer")); // ok, package name has no dot.
ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
"android.com", "installer")); // ok, package name starts with android
ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
"abc.def", "com.android.foo")); // ok, installer name starts with com.android
ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
"123.456", "com.google.bar")); // ok, installer name starts with com.google
ASSERT_EQ(false, MediaMetricsService::useUidForPackage(
"r2.d2", "preload")); // ok, installer name is preload
ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
"abc.def", "installer")); // unknown installer
ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
"123.456", "installer")); // unknown installer
ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
"r2.d2", "preload23")); // unknown installer
ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
"com.android.foo", "abc.def")); // unknown installer
ASSERT_EQ(true, MediaMetricsService::useUidForPackage(
"com.google.bar", "123.456")); // unknown installer
}
TEST(mediametrics_tests, item_manipulation) {
mediametrics::Item item("audiorecord");

Loading…
Cancel
Save