Make getRuntimeInfo more lenient.

Reorder the logic to retrieve kernel level. The old logic is:

if (no device manifest) error;
kernel level = device manifest.<kernel>.level

The new logic is:

Attempt to read from uname() // implemented in follow up CL
if (device manifest specifies kernel level)
  if (uname() did not specify kernel level)
    use value from device manifest.<kernel>.level
  else
    log warning if there is a conflict
    use value from uname()

On devices that does not use GKI, uname() does not specify
kernel level, and libvintf will retrieve from device manifest.

On devices that uses GKI, uname() specifies kernel level, and its value
has a higher priority.

Test: libvintf_test
Test: vintf_object_test
Bug: 161317193

Change-Id: I1830750b39af51c675513dee067e02b8d492a3a9
diff --git a/VintfObject.cpp b/VintfObject.cpp
index 69427aa..4ab5fa1 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -542,20 +542,39 @@
         mDeviceRuntimeInfo.object = getRuntimeInfoFactory()->make_shared();
     }
 
-    // Fetch kernel FCM version from device HAL manifest and store it in RuntimeInfo too.
-    if ((flags & RuntimeInfo::FetchFlag::KERNEL_FCM) != 0) {
-        auto manifest = getDeviceHalManifest();
-        if (!manifest) {
-            mDeviceRuntimeInfo.fetchedFlags &= ~RuntimeInfo::FetchFlag::KERNEL_FCM;
-            return nullptr;
-        }
-        mDeviceRuntimeInfo.object->setKernelLevel(manifest->inferredKernelLevel());
-    }
-
     status_t status = mDeviceRuntimeInfo.object->fetchAllInformation(flags);
     if (status != OK) {
-        mDeviceRuntimeInfo.fetchedFlags &= (~flags);  // mark the fields as "not fetched"
-        return nullptr;
+        // If only kernel FCM is needed, ignore errors when fetching RuntimeInfo because RuntimeInfo
+        // is not available on host. On host, the kernel level can still be inferred from device
+        // manifest.
+        // If other information is needed, flag the error by returning nullptr.
+        auto allExceptKernelFcm = RuntimeInfo::FetchFlag::ALL & ~RuntimeInfo::FetchFlag::KERNEL_FCM;
+        bool needDeviceRuntimeInfo = flags & allExceptKernelFcm;
+        if (needDeviceRuntimeInfo) {
+            mDeviceRuntimeInfo.fetchedFlags &= (~flags);  // mark the fields as "not fetched"
+            return nullptr;
+        }
+    }
+
+    // To support devices without GKI, RuntimeInfo::fetchAllInformation does not report errors
+    // if kernel level cannot be retrieved. If so, fetch kernel FCM version from device HAL
+    // manifest and store it in RuntimeInfo too.
+    if (flags & RuntimeInfo::FetchFlag::KERNEL_FCM) {
+        Level deviceManifestKernelLevel = Level::UNSPECIFIED;
+        auto manifest = getDeviceHalManifest();
+        if (manifest) {
+            deviceManifestKernelLevel = manifest->inferredKernelLevel();
+        }
+        if (deviceManifestKernelLevel != Level::UNSPECIFIED) {
+            Level kernelLevel = mDeviceRuntimeInfo.object->kernelLevel();
+            if (kernelLevel == Level::UNSPECIFIED) {
+                mDeviceRuntimeInfo.object->setKernelLevel(deviceManifestKernelLevel);
+            } else if (kernelLevel != deviceManifestKernelLevel) {
+                LOG(WARNING) << "uname() reports kernel level " << kernelLevel
+                             << " but device manifest sets kernel level "
+                             << deviceManifestKernelLevel << ". Using kernel level " << kernelLevel;
+            }
+        }
     }
 
     mDeviceRuntimeInfo.fetchedFlags |= flags;