Add API for runtime collectible kernel / sepolicy info.

Also makes a nit change to VendorManifest; clear it
immediately when fetchAllInformation fails.

Test: /system/bin/vintf produces output for
/proc/version and kernel sepolicy version.

Change-Id: If24a5d7de92c674bd2668c5f5f42f61e894335c4
diff --git a/Android.bp b/Android.bp
index c9a3850..9a55f31 100644
--- a/Android.bp
+++ b/Android.bp
@@ -21,8 +21,10 @@
     shared_libs: [
         "libbase",
         "liblog",
+        "libselinux",
         "libtinyxml2",
         "libutils",
+        "libz",
     ],
     export_include_dirs: ["include"],
     local_include_dirs: ["include/vintf"],
@@ -31,6 +33,7 @@
         "parse_string.cpp",
         "parse_xml.cpp",
         "CompatibilityMatrix.cpp",
+        "KernelInfo.cpp",
         "ManifestHal.cpp",
         "MatrixHal.cpp",
         "MatrixKernel.cpp",
@@ -42,6 +45,7 @@
     name: "vintf",
     shared_libs: [
         "libvintf",
+        "libselinux",
     ],
     srcs: [
         "main.cpp"
diff --git a/KernelInfo.cpp b/KernelInfo.cpp
new file mode 100644
index 0000000..c3aa8b6
--- /dev/null
+++ b/KernelInfo.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#define LOG_TAG "libvintf"
+
+#include "KernelInfo.h"
+
+#include <errno.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <selinux/selinux.h>
+#include <zlib.h>
+
+#define PROC_CONFIG "/proc/config.gz"
+#define BUFFER_SIZE sysconf(_SC_PAGESIZE)
+
+namespace android {
+namespace vintf {
+
+static void removeTrailingComments(std::string *s) {
+    size_t sharpPos = s->find('#');
+    if (sharpPos != std::string::npos) {
+        s->erase(sharpPos);
+    }
+}
+static void trim(std::string *s) {
+    auto l = s->begin();
+    for (; l != s->end() && std::isspace(*l); ++l);
+    s->erase(s->begin(), l);
+    auto r = s->rbegin();
+    for (; r != s->rend() && std::isspace(*r); ++r);
+    s->erase(r.base(), s->end());
+}
+
+struct KernelInfoFetcher {
+    KernelInfoFetcher(KernelInfo *ki) : mKernelInfo(ki) { }
+    status_t fetchAllInformation();
+private:
+    void streamConfig(const char *buf, size_t len);
+    void parseConfig(std::string *s);
+    status_t fetchVersion();
+    status_t fetchKernelConfigs();
+    status_t fetchCpuInfo();
+    status_t fetchKernelSepolicyVers();
+    status_t fetchSepolicyFiles();
+    KernelInfo *mKernelInfo;
+    std::string mRemaining;
+};
+
+// decompress /proc/config.gz and read its contents.
+status_t KernelInfoFetcher::fetchKernelConfigs() {
+    gzFile f = gzopen(PROC_CONFIG, "rb");
+    if (f == NULL) {
+        LOG(ERROR) << "Could not open /proc/config.gz: " << errno;
+        return -errno;
+    }
+
+    char buf[BUFFER_SIZE];
+    int len;
+    while ((len = gzread(f, buf, sizeof buf)) > 0) {
+        streamConfig(buf, len);
+    }
+    status_t err = OK;
+    if (len < 0) {
+        int errnum;
+        const char *errmsg = gzerror(f, &errnum);
+        LOG(ERROR) << "Could not read /proc/config.gz: " << errmsg;
+        err = (errnum == Z_ERRNO ? -errno : errnum);
+    }
+
+    // stream a "\n" to end the stream to finish the last line.
+    streamConfig("\n", 1 /* sizeof "\n" */);
+
+    gzclose(f);
+    return err;
+}
+
+void KernelInfoFetcher::parseConfig(std::string *s) {
+    removeTrailingComments(s);
+    trim(s);
+    if (s->empty()) {
+        return;
+    }
+    size_t equalPos = s->find('=');
+    if (equalPos == std::string::npos) {
+        LOG(WARNING) << "Unrecognized line in /proc/config.gz: " << *s;
+        return;
+    }
+    std::string key = s->substr(0, equalPos);
+    std::string value = s->substr(equalPos + 1);
+    if (!mKernelInfo->kernelConfigs.emplace(std::move(key), std::move(value)).second) {
+        LOG(WARNING) << "Duplicated key in /proc/config.gz: " << s->substr(0, equalPos);
+        return;
+    }
+}
+
+void KernelInfoFetcher::streamConfig(const char *buf, size_t len) {
+    const char *begin = buf;
+    const char *end = buf;
+    const char *stop = buf + len;
+    while (end < stop) {
+        if (*end == '\n') {
+            mRemaining.insert(mRemaining.size(), begin, end - begin);
+            parseConfig(&mRemaining);
+            mRemaining.clear();
+            begin = end + 1;
+        }
+        end++;
+    }
+    mRemaining.insert(mRemaining.size(), begin, end - begin);
+}
+
+status_t KernelInfoFetcher::fetchCpuInfo() {
+    // TODO implement this; 32-bit and 64-bit has different format.
+    return OK;
+}
+
+status_t KernelInfoFetcher::fetchKernelSepolicyVers() {
+    int pv = security_policyvers();
+    if (pv < 0) {
+        return pv;
+    }
+    mKernelInfo->mKernelSepolicyVersion = pv;
+    return OK;
+}
+
+status_t KernelInfoFetcher::fetchVersion() {
+    struct utsname buf;
+    if (uname(&buf)) {
+        return -errno;
+    }
+    mKernelInfo->mOsName = buf.sysname;
+    mKernelInfo->mNodeName = buf.nodename;
+    mKernelInfo->mOsRelease = buf.release;
+    mKernelInfo->mOsVersion = buf.version;
+    mKernelInfo->mHardwareId = buf.machine;
+    return OK;
+}
+
+// Grab sepolicy files.
+status_t KernelInfoFetcher::fetchSepolicyFiles() {
+    // TODO implement this
+    return OK;
+}
+
+status_t KernelInfoFetcher::fetchAllInformation() {
+    status_t err;
+    if ((err = fetchVersion()) != OK) {
+        return err;
+    }
+    if ((err = fetchKernelConfigs()) != OK) {
+        return err;
+    }
+    if ((err = fetchCpuInfo()) != OK) {
+        return err;
+    }
+    if ((err = fetchKernelSepolicyVers()) != OK) {
+        return err;
+    }
+    if ((err = fetchSepolicyFiles()) != OK) {
+        return err;
+    }
+    return OK;
+}
+
+
+const std::string &KernelInfo::osName() const {
+    return mOsName;
+}
+
+const std::string &KernelInfo::nodeName() const {
+    return mNodeName;
+}
+
+const std::string &KernelInfo::osRelease() const {
+    return mOsRelease;
+}
+
+const std::string &KernelInfo::osVersion() const {
+    return mOsVersion;
+}
+
+const std::string &KernelInfo::hardwareId() const {
+    return mHardwareId;
+}
+
+size_t KernelInfo::kernelSepolicyVersion() const {
+    return mKernelSepolicyVersion;
+}
+
+void KernelInfo::clear() {
+    kernelConfigs.clear();
+    mOsName.clear();
+    mNodeName.clear();
+    mOsRelease.clear();
+    mOsVersion.clear();
+    mHardwareId.clear();
+}
+
+const KernelInfo *KernelInfo::Get() {
+    static KernelInfo ki{};
+    static KernelInfo *kip = nullptr;
+    static std::mutex mutex{};
+
+    std::lock_guard<std::mutex> lock(mutex);
+    if (kip == nullptr) {
+        if (KernelInfoFetcher(&ki).fetchAllInformation() == OK) {
+            kip = &ki;
+        } else {
+            ki.clear();
+            return nullptr;
+        }
+    }
+
+    return kip;
+}
+
+} // namespace vintf
+} // namespace android
diff --git a/VendorManifest.cpp b/VendorManifest.cpp
index ee6025b..f759e2c 100644
--- a/VendorManifest.cpp
+++ b/VendorManifest.cpp
@@ -130,9 +130,11 @@
 
     std::lock_guard<std::mutex> lock(mutex);
     if (vmp == nullptr) {
-        vm.clear();
         if (vm.fetchAllInformation() == OK) {
             vmp = &vm;
+        } else {
+            vm.clear();
+            return nullptr;
         }
     }
 
diff --git a/include/vintf/KernelInfo.h b/include/vintf/KernelInfo.h
new file mode 100644
index 0000000..ea9a09a
--- /dev/null
+++ b/include/vintf/KernelInfo.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VINTF_KERNEL_INFO_H
+#define ANDROID_VINTF_KERNEL_INFO_H
+
+#include <map>
+#include <string>
+
+#include <utils/Errors.h>
+
+namespace android {
+namespace vintf {
+
+// Kernel Info sent to OTA server
+struct KernelInfo {
+
+    // Get the object that contains all kernel information. If any error,
+    // nullptr is returned.
+    // Note: this is not thread-safe.
+    static const KernelInfo *Get();
+
+    // /proc/version
+    // utsname.sysname
+    const std::string &osName() const;
+    // utsname.nodename
+    const std::string &nodeName() const;
+    // utsname.release
+    const std::string &osRelease() const;
+    // utsname.version
+    const std::string &osVersion() const;
+    // utsname.machine
+    const std::string &hardwareId() const;
+
+    // /sys/fs/selinux/policyvers
+    size_t kernelSepolicyVersion() const;
+
+private:
+
+    friend struct KernelInfoFetcher;
+    friend std::string dump(const KernelInfo &ki);
+
+    void clear();
+
+    // /proc/config.gz
+    // Key: CONFIG_xxx; Value: the value after = sign.
+    std::map<std::string, std::string> kernelConfigs;
+    std::string mOsName;
+    std::string mNodeName;
+    std::string mOsRelease;
+    std::string mOsVersion;
+    std::string mHardwareId;
+
+    size_t mKernelSepolicyVersion;
+
+};
+
+} // namespace vintf
+} // namespace android
+
+#endif // ANDROID_VINTF_KERNEL_INFO_H
diff --git a/include/vintf/parse_string.h b/include/vintf/parse_string.h
index 6449d8a..40311a3 100644
--- a/include/vintf/parse_string.h
+++ b/include/vintf/parse_string.h
@@ -22,6 +22,7 @@
 #include <string>
 
 #include "CompatibilityMatrix.h"
+#include "KernelInfo.h"
 #include "VendorManifest.h"
 
 namespace android {
@@ -57,6 +58,8 @@
 // the XML string.
 std::string dump(const VendorManifest &vm);
 
+std::string dump(const KernelInfo &ki);
+
 } // namespace vintf
 } // namespace android
 
diff --git a/main.cpp b/main.cpp
index 29886c6..2dd8633 100644
--- a/main.cpp
+++ b/main.cpp
@@ -16,6 +16,8 @@
 
 #include <iostream>
 #include <vintf/parse_xml.h>
+#include <vintf/parse_string.h>
+#include <vintf/KernelInfo.h>
 #include <vintf/VendorManifest.h>
 
 int main(int, char **) {
@@ -24,4 +26,10 @@
     const VendorManifest *vm = VendorManifest::Get();
     if (vm != nullptr)
         std::cout << gVendorManifestConverter(*vm);
+
+    std::cout << std::endl;
+    const KernelInfo *ki = KernelInfo::Get();
+    if (ki != nullptr)
+        std::cout << dump(*ki);
+    std::cout << std::endl;
 }
diff --git a/parse_string.cpp b/parse_string.cpp
index f8f4b12..d0e655b 100644
--- a/parse_string.cpp
+++ b/parse_string.cpp
@@ -233,5 +233,23 @@
     return oss.str();
 }
 
+std::string dump(const KernelInfo &ki) {
+    std::ostringstream oss;
+
+    oss << "kernel = "
+        << ki.osName() << "/"
+        << ki.nodeName() << "/"
+        << ki.osRelease() << "/"
+        << ki.osVersion() << "/"
+        << ki.hardwareId() << ";"
+        << "kernelSepolicyVersion = " << ki.kernelSepolicyVersion() << ";"
+        << "#CONFIG's loaded = " << ki.kernelConfigs.size() << ";\n";
+    for (const auto &pair : ki.kernelConfigs) {
+        oss << pair.first << "=" << pair.second << "\n";
+    }
+
+    return oss.str();
+}
+
 } // namespace vintf
 } // namespace android