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