Allow RuntimeInfo::fetch to fetch conditionally

... to avoid unnecessary denials if the caller process does
not have enough permissions.

Test: CtsDeviceInfo
Bug: 66960848
Change-Id: I5555307ff086fbc10cfa677c530dc3de673e18f6
diff --git a/RuntimeInfo-host.cpp b/RuntimeInfo-host.cpp
index 96eff42..bd46a9f 100644
--- a/RuntimeInfo-host.cpp
+++ b/RuntimeInfo-host.cpp
@@ -23,7 +23,7 @@
 namespace android {
 namespace vintf {
 
-status_t RuntimeInfo::fetchAllInformation() {
+status_t RuntimeInfo::fetchAllInformation(RuntimeInfo::FetchFlags /* flags */) {
     LOG(WARNING) << "Should not run fetchAllInformation on host.";
     return OK;
 }
diff --git a/RuntimeInfo-target.cpp b/RuntimeInfo-target.cpp
index 9518a0c..1bdf2c3 100644
--- a/RuntimeInfo-target.cpp
+++ b/RuntimeInfo-target.cpp
@@ -45,8 +45,9 @@
 
 struct RuntimeInfoFetcher {
     RuntimeInfoFetcher(RuntimeInfo *ki) : mRuntimeInfo(ki) { }
-    status_t fetchAllInformation();
-private:
+    status_t fetchAllInformation(RuntimeInfo::FetchFlags flags);
+
+   private:
     status_t fetchVersion();
     status_t fetchKernelConfigs();
     status_t fetchCpuInfo();
@@ -159,28 +160,31 @@
     return OK;
 }
 
-status_t RuntimeInfoFetcher::fetchAllInformation() {
+status_t RuntimeInfoFetcher::fetchAllInformation(RuntimeInfo::FetchFlags flags) {
+
+    using F = RuntimeInfo::FetchFlag;
+    using RF = RuntimeInfoFetcher;
+    using FetchFunction = status_t(RF::*)();
+    const static std::vector<std::tuple<F, FetchFunction, std::string>> gFetchFunctions({
+        // flag          fetch function                 description
+        {F::CPU_VERSION, &RF::fetchVersion,             "/proc/version"},
+        {F::CONFIG_GZ,   &RF::fetchKernelConfigs,       "/proc/config.gz"},
+        {F::CPU_INFO,    &RF::fetchCpuInfo,             "/proc/cpuinfo"},
+        {F::POLICYVERS,  &RF::fetchKernelSepolicyVers,  "kernel sepolicy version"},
+        {F::AVB,         &RF::fetchAvb,                 "avb version"},
+    });
+
     status_t err;
-    if ((err = fetchVersion()) != OK) {
-        LOG(WARNING) << "Cannot fetch or parse /proc/version: " << strerror(-err);
-    }
-    if ((err = fetchKernelConfigs()) != OK) {
-        LOG(WARNING) << "Cannot fetch or parse /proc/config.gz: " << strerror(-err);
-    }
-    if ((err = fetchCpuInfo()) != OK) {
-        LOG(WARNING) << "Cannot fetch /proc/cpuinfo: " << strerror(-err);
-    }
-    if ((err = fetchKernelSepolicyVers()) != OK) {
-        LOG(WARNING) << "Cannot fetch kernel sepolicy version: " << strerror(-err);
-    }
-    if ((err = fetchAvb()) != OK) {
-        LOG(WARNING) << "Cannot fetch sepolicy avb version: " << strerror(-err);
-    }
+    for (const auto& tuple : gFetchFunctions)
+        if ((flags & std::get<0>(tuple)) && (err = (*this.*std::get<1>(tuple))()) != OK)
+            LOG(WARNING) << "Cannot fetch or parse " << std::get<2>(tuple) << ": "
+                         << strerror(-err);
+
     return OK;
 }
 
-status_t RuntimeInfo::fetchAllInformation() {
-    return RuntimeInfoFetcher(this).fetchAllInformation();
+status_t RuntimeInfo::fetchAllInformation(RuntimeInfo::FetchFlags flags) {
+    return RuntimeInfoFetcher(this).fetchAllInformation(flags);
 }
 
 } // namespace vintf
diff --git a/VintfObject.cpp b/VintfObject.cpp
index 92fa0b0..e87dd05 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -33,6 +33,12 @@
     std::mutex mutex;
 };
 
+struct LockedRuntimeInfoCache {
+    std::shared_ptr<RuntimeInfo> object;
+    std::mutex mutex;
+    RuntimeInfo::FetchFlags fetchedFlags = RuntimeInfo::FetchFlag::NONE;
+};
+
 template <typename T, typename F>
 static std::shared_ptr<const T> Get(
         LockedSharedPtr<T> *ptr,
@@ -82,10 +88,27 @@
 }
 
 // static
-std::shared_ptr<const RuntimeInfo> VintfObject::GetRuntimeInfo(bool skipCache) {
-    static LockedSharedPtr<RuntimeInfo> gDeviceRuntimeInfo;
-    return Get(&gDeviceRuntimeInfo, skipCache,
-            std::bind(&RuntimeInfo::fetchAllInformation, std::placeholders::_1));
+std::shared_ptr<const RuntimeInfo> VintfObject::GetRuntimeInfo(bool skipCache,
+                                                               RuntimeInfo::FetchFlags flags) {
+    static LockedRuntimeInfoCache gDeviceRuntimeInfo;
+    std::unique_lock<std::mutex> _lock(gDeviceRuntimeInfo.mutex);
+
+    if (!skipCache) {
+        flags &= (~gDeviceRuntimeInfo.fetchedFlags);
+    }
+
+    if (gDeviceRuntimeInfo.object == nullptr) {
+        gDeviceRuntimeInfo.object = std::make_shared<RuntimeInfo>();
+    }
+
+    status_t status = gDeviceRuntimeInfo.object->fetchAllInformation(flags);
+    if (status != OK) {
+        gDeviceRuntimeInfo.fetchedFlags &= (~flags);  // mark the fields as "not fetched"
+        return nullptr;
+    }
+
+    gDeviceRuntimeInfo.fetchedFlags |= flags;
+    return gDeviceRuntimeInfo.object;
 }
 
 namespace details {
diff --git a/include/vintf/RuntimeInfo.h b/include/vintf/RuntimeInfo.h
index 1505c15..390e3ed 100644
--- a/include/vintf/RuntimeInfo.h
+++ b/include/vintf/RuntimeInfo.h
@@ -74,13 +74,26 @@
     bool checkCompatibility(const CompatibilityMatrix& mat, std::string* error = nullptr,
                             DisabledChecks disabledChecks = ENABLE_ALL_CHECKS) const;
 
+    using FetchFlags = uint32_t;
+    enum FetchFlag : FetchFlags {
+        CPU_VERSION     = 1 << 0,
+        CONFIG_GZ       = 1 << 1,
+        CPU_INFO        = 1 << 2,
+        POLICYVERS      = 1 << 3,
+        AVB             = 1 << 4,
+        LAST_PLUS_ONE,
+
+        NONE = 0,
+        ALL = ((LAST_PLUS_ONE - 1) << 1) - 1,
+    };
+
    private:
     friend struct RuntimeInfoFetcher;
     friend class VintfObject;
     friend struct LibVintfTest;
     friend std::string dump(const RuntimeInfo &ki);
 
-    status_t fetchAllInformation();
+    status_t fetchAllInformation(FetchFlags flags);
 
     // mKernelVersion = x'.y'.z', minLts = x.y.z,
     // match if x == x' , y == y' , and z <= z'.
diff --git a/include/vintf/VintfObject.h b/include/vintf/VintfObject.h
index fe70784..5593da5 100644
--- a/include/vintf/VintfObject.h
+++ b/include/vintf/VintfObject.h
@@ -80,8 +80,19 @@
 
     /*
      * Return the API that access device runtime info.
+     *
+     * {skipCache == true, flags == ALL}: re-fetch everything
+     * {skipCache == false, flags == ALL}: fetch everything if not previously fetched
+     * {skipCache == true, flags == selected info}: re-fetch selected information
+     *                                if not previously fetched.
+     * {skipCache == false, flags == selected info}: fetch selected information
+     *                                if not previously fetched.
+     *
+     * @param skipCache do not fetch if previously fetched
+     * @param flags bitwise-or of RuntimeInfo::FetchFlag
      */
-    static std::shared_ptr<const RuntimeInfo> GetRuntimeInfo(bool skipCache = false);
+    static std::shared_ptr<const RuntimeInfo> GetRuntimeInfo(
+        bool skipCache = false, RuntimeInfo::FetchFlags flags = RuntimeInfo::FetchFlag::ALL);
 
     /**
      * Check compatibility, given a set of manifests / matrices in packageInfo.