Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a85efab7 authored by Andy Hung's avatar Andy Hung
Browse files

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
parent 370e9407
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ cc_library {
        "libc_malloc_debug_backtrace",
    ],
    shared_libs: [
        "libaudioutils", // for clock.h
        "libbinder",
        "libcutils",
        "liblog",
+126 −0
Original line number Diff line number Diff line
@@ -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
+36 −1
Original line number Diff line number Diff line
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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",
+32 −154
Original line number Diff line number Diff line
@@ -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);
    // 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 {
        // trusted, provided a package, do nothing
            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
Loading