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
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 5047b19..b7037e9 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -29,6 +29,7 @@
"libc_malloc_debug_backtrace",
],
shared_libs: [
+ "libaudioutils", // for clock.h
"libbinder",
"libcutils",
"liblog",
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index a77311e..a33ed97 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -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 @@
}
}
+// 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
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index e467058..4925cdb 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -28,6 +28,7 @@
#include <map>
#include <optional>
#include <string>
+#include <unordered_map>
#include <vector>
namespace android {
@@ -118,6 +119,40 @@
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
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index 0840f31..20f346c 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -12,6 +12,7 @@
"libbinder",
"liblog",
"libmediametricsservice",
+ "libmediautils",
"libutils",
],
@@ -52,6 +53,7 @@
"libbinder",
"liblog",
"libmediametrics",
+ "libmediautils",
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index bbd13fa..b5bdd6f 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -57,6 +57,25 @@
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 @@
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 @@
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
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index b736c30..74a114a 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -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 @@
*/
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 @@
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;
diff --git a/services/mediametrics/tests/Android.bp b/services/mediametrics/tests/Android.bp
index 9eb2d89..bdeda30 100644
--- a/services/mediametrics/tests/Android.bp
+++ b/services/mediametrics/tests/Android.bp
@@ -17,6 +17,7 @@
"liblog",
"libmediametrics",
"libmediametricsservice",
+ "libmediautils",
"libutils",
],
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index ccf2d63..90a8565 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -89,6 +89,32 @@
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");