Support <system-sdk> in fwk manifest and dev matrix.

<system-sdk> is a tag in framework manifest and
device matrix. It states a list of System SDK levels
that the framework supports for vendor apps / vendor
apps requires.

Format:
<system-sdk>
    <version>14</version>
    <version>15</version>
</system-sdk>

Versions must not be duplicated.

It is compatible when the set specified in framework
manifest is a superset of the set specified in the
device compatibility matrix.

Test: libvintf_test
Test: vintf_object_tests
Bug: 69088799
Change-Id: I802f055ea60666995202438a770d1e440517ec5c
diff --git a/Android.bp b/Android.bp
index fea4d38..a2f647e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -24,10 +24,30 @@
     ],
 }
 
+cc_defaults {
+    name: "libvintf-common-srcs",
+    srcs: [
+        "parse_string.cpp",
+        "parse_xml.cpp",
+        "CompatibilityMatrix.cpp",
+        "HalManifest.cpp",
+        "HalInterface.cpp",
+        "KernelConfigTypedValue.cpp",
+        "RuntimeInfo.cpp",
+        "ManifestHal.cpp",
+        "MatrixHal.cpp",
+        "MatrixKernel.cpp",
+        "SystemSdk.cpp",
+        "TransportArch.cpp",
+        "VintfObject.cpp",
+        "XmlFile.cpp",
+    ]
+}
+
 cc_library {
     name: "libvintf",
     host_supported: true,
-    defaults: ["libvintf-defaults"],
+    defaults: ["libvintf-defaults", "libvintf-common-srcs"],
     shared_libs: [
         "libbase",
         "liblog",
@@ -38,20 +58,7 @@
     local_include_dirs: ["include/vintf"],
 
     srcs: [
-        "parse_string.cpp",
-        "parse_xml.cpp",
-        "CompatibilityMatrix.cpp",
-        "HalManifest.cpp",
-        "HalInterface.cpp",
         "KernelConfigParser.cpp",
-        "KernelConfigTypedValue.cpp",
-        "RuntimeInfo.cpp",
-        "ManifestHal.cpp",
-        "MatrixHal.cpp",
-        "MatrixKernel.cpp",
-        "TransportArch.cpp",
-        "VintfObject.cpp",
-        "XmlFile.cpp",
         "utils.cpp",
     ],
 
@@ -136,7 +143,7 @@
 
 cc_library_static {
     name: "libvintftest",
-    defaults: ["libvintf-defaults"],
+    defaults: ["libvintf-defaults", "libvintf-common-srcs"],
     host_supported: true,
     shared_libs: [
         "libbase",
@@ -148,19 +155,6 @@
     local_include_dirs: ["include/vintf", "test", "."],
 
     srcs: [
-        "parse_string.cpp",
-        "parse_xml.cpp",
-        "CompatibilityMatrix.cpp",
-        "HalManifest.cpp",
-        "HalInterface.cpp",
-        "KernelConfigTypedValue.cpp",
-        "RuntimeInfo.cpp",
-        "ManifestHal.cpp",
-        "MatrixHal.cpp",
-        "MatrixKernel.cpp",
-        "TransportArch.cpp",
-        "VintfObject.cpp",
-        "XmlFile.cpp",
         "test/RuntimeInfo-fake.cpp",
         "test/utils-fake.cpp",
     ],
diff --git a/AssembleVintf.cpp b/AssembleVintf.cpp
index e19096e..f7f1fb4 100644
--- a/AssembleVintf.cpp
+++ b/AssembleVintf.cpp
@@ -76,6 +76,18 @@
         return envValue != nullptr ? std::string(envValue) : std::string();
     }
 
+    // Get environment variable and split with space.
+    std::vector<std::string> getEnvList(const std::string& key) const {
+        std::vector<std::string> ret;
+        for (auto&& v : base::Split(getEnv(key), " ")) {
+            v = base::Trim(v);
+            if (!v.empty()) {
+                ret.push_back(v);
+            }
+        }
+        return ret;
+    }
+
     template <typename T>
     bool getFlag(const std::string& key, T* value) const {
         std::string envValue = getEnv(key);
@@ -290,11 +302,12 @@
         }
 
         if (halManifest->mType == SchemaType::FRAMEWORK) {
-            for (auto&& v : base::Split(getEnv("PROVIDED_VNDK_VERSIONS"), " ")) {
-                v = base::Trim(v);
-                if (!v.empty()) {
-                    halManifest->framework.mVendorNdks.emplace_back(std::move(v));
-                }
+            for (auto&& v : getEnvList("PROVIDED_VNDK_VERSIONS")) {
+                halManifest->framework.mVendorNdks.emplace_back(std::move(v));
+            }
+
+            for (auto&& v : getEnvList("PRODUCT_SYSTEMSDK_VERSIONS")) {
+                halManifest->framework.mSystemSdk.mVersions.emplace(std::move(v));
             }
         }
 
@@ -416,6 +429,10 @@
                 }
                 valueInMatrix = VendorNdk{std::move(vndkVersion)};
             }
+
+            for (auto&& v : getEnvList("BOARD_SYSTEMSDK_VERSIONS")) {
+                matrix->device.mSystemSdk.mVersions.emplace(std::move(v));
+            }
         }
 
         if (matrices->front().object.mType == SchemaType::FRAMEWORK) {
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp
index 8331f78..d3c4ebf 100644
--- a/CompatibilityMatrix.cpp
+++ b/CompatibilityMatrix.cpp
@@ -168,7 +168,8 @@
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
                 lft.device.mVndk == rgt.device.mVndk &&
 #pragma clang diagnostic pop
-                lft.device.mVendorNdk == rgt.device.mVendorNdk)) &&
+                lft.device.mVendorNdk == rgt.device.mVendorNdk &&
+                lft.device.mSystemSdk == rgt.device.mSystemSdk)) &&
            (lft.mType != SchemaType::FRAMEWORK ||
             (lft.framework.mKernels == rgt.framework.mKernels &&
              lft.framework.mSepolicy == rgt.framework.mSepolicy &&
diff --git a/HalManifest.cpp b/HalManifest.cpp
index 22ff292..20ed7eb 100644
--- a/HalManifest.cpp
+++ b/HalManifest.cpp
@@ -24,6 +24,8 @@
 #include <mutex>
 #include <set>
 
+#include <android-base/strings.h>
+
 #include "parse_string.h"
 #include "parse_xml.h"
 #include "utils.h"
@@ -299,6 +301,22 @@
     return false;
 }
 
+static bool checkSystemSdkCompatibility(const SystemSdk& matSystemSdk,
+                                        const SystemSdk& manifestSystemSdk, std::string* error) {
+    SystemSdk notSupported = matSystemSdk.removeVersions(manifestSystemSdk);
+    if (!notSupported.empty()) {
+        if (error) {
+            *error =
+                "The following System SDK versions are required by device "
+                "compatibility matrix but not supported by the framework manifest: [" +
+                base::Join(notSupported.versions(), ", ") + "]. Supported versions are: [" +
+                base::Join(manifestSystemSdk.versions(), ", ") + "].";
+        }
+        return false;
+    }
+    return true;
+}
+
 bool HalManifest::checkCompatibility(const CompatibilityMatrix &mat, std::string *error) const {
     if (mType == mat.mType) {
         if (error != nullptr) {
@@ -322,6 +340,10 @@
         if (!checkVendorNdkCompatibility(mat.device.mVendorNdk, framework.mVendorNdks, error)) {
             return false;
         }
+
+        if (!checkSystemSdkCompatibility(mat.device.mSystemSdk, framework.mSystemSdk, error)) {
+            return false;
+        }
     } else if (mType == SchemaType::DEVICE) {
         bool match = false;
         for (const auto &range : mat.framework.mSepolicy.sepolicyVersions()) {
@@ -424,7 +446,8 @@
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
                 lft.framework.mVndks == rgt.framework.mVndks &&
 #pragma clang diagnostic pop
-                lft.framework.mVendorNdks == rgt.framework.mVendorNdks));
+                lft.framework.mVendorNdks == rgt.framework.mVendorNdks &&
+                lft.framework.mSystemSdk == rgt.framework.mSystemSdk));
 }
 
 } // namespace vintf
diff --git a/SystemSdk.cpp b/SystemSdk.cpp
new file mode 100644
index 0000000..d2ee123
--- /dev/null
+++ b/SystemSdk.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "SystemSdk.h"
+
+#include <algorithm>
+
+namespace android {
+namespace vintf {
+
+SystemSdk SystemSdk::removeVersions(const SystemSdk& other) const {
+    SystemSdk ret;
+    std::set_difference(versions().begin(), versions().end(), other.versions().begin(),
+                        other.versions().end(), std::inserter(ret.mVersions, ret.mVersions.end()));
+    return ret;
+}
+
+bool SystemSdk::operator==(const SystemSdk& other) const {
+    return versions() == other.versions();
+}
+
+}  // namespace vintf
+}  // namespace android
diff --git a/include/vintf/CompatibilityMatrix.h b/include/vintf/CompatibilityMatrix.h
index b7a5541..696a0bf 100644
--- a/include/vintf/CompatibilityMatrix.h
+++ b/include/vintf/CompatibilityMatrix.h
@@ -30,6 +30,7 @@
 #include "Named.h"
 #include "SchemaType.h"
 #include "Sepolicy.h"
+#include "SystemSdk.h"
 #include "VendorNdk.h"
 #include "Vndk.h"
 #include "XmlFileGroup.h"
@@ -115,6 +116,7 @@
 #pragma clang diagnostic pop
 
         VendorNdk mVendorNdk;
+        SystemSdk mSystemSdk;
     } device;
 };
 
diff --git a/include/vintf/HalManifest.h b/include/vintf/HalManifest.h
index 655335e..84bd770 100644
--- a/include/vintf/HalManifest.h
+++ b/include/vintf/HalManifest.h
@@ -28,6 +28,7 @@
 #include "ManifestHal.h"
 #include "MapValueIterator.h"
 #include "SchemaType.h"
+#include "SystemSdk.h"
 #include "VendorNdk.h"
 #include "Version.h"
 #include "Vndk.h"
@@ -158,6 +159,7 @@
 #pragma clang diagnostic pop
 
         std::vector<VendorNdk> mVendorNdks;
+        SystemSdk mSystemSdk;
     } framework;
 };
 
diff --git a/include/vintf/SystemSdk.h b/include/vintf/SystemSdk.h
new file mode 100644
index 0000000..1828e4a
--- /dev/null
+++ b/include/vintf/SystemSdk.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 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_SYSTEM_SDK_H
+#define ANDROID_VINTF_SYSTEM_SDK_H
+
+#include <stdint.h>
+
+#include <set>
+#include <string>
+
+namespace android {
+namespace vintf {
+
+// System SDK versions provided for vendor apps.
+class SystemSdk {
+   public:
+    SystemSdk() = default;
+    SystemSdk(std::set<std::string>&& versions) : mVersions(std::move(versions)) {}
+    SystemSdk(const std::set<std::string>& versions) : mVersions(versions) {}
+    const std::set<std::string>& versions() const { return mVersions; }
+    bool empty() const { return versions().empty(); }
+
+    bool operator==(const SystemSdk& other) const;
+    // return {v : v in this and not in other}
+    SystemSdk removeVersions(const SystemSdk& other) const;
+
+   private:
+    friend class AssembleVintfImpl;
+    friend struct SystemSdkConverter;
+    std::set<std::string> mVersions;
+};
+
+}  // namespace vintf
+}  // namespace android
+
+#endif  // ANDROID_VINTF_SYSTEM_SDK_H
diff --git a/include/vintf/parse_string.h b/include/vintf/parse_string.h
index 0bb554a..be0c070 100644
--- a/include/vintf/parse_string.h
+++ b/include/vintf/parse_string.h
@@ -36,6 +36,7 @@
 std::ostream &operator<<(std::ostream &os, SchemaType ksv);
 std::ostream& operator<<(std::ostream& os, XmlSchemaFormat f);
 std::ostream& operator<<(std::ostream& os, Level l);
+std::ostream& operator<<(std::ostream& os, KernelSepolicyVersion v);
 std::ostream &operator<<(std::ostream &os, const ManifestHal &hal);
 std::ostream &operator<<(std::ostream &os, const Version &ver);
 std::ostream &operator<<(std::ostream &os, const VersionRange &vr);
diff --git a/include/vintf/parse_xml.h b/include/vintf/parse_xml.h
index a92facf..314fb72 100644
--- a/include/vintf/parse_xml.h
+++ b/include/vintf/parse_xml.h
@@ -30,6 +30,7 @@
     NO_VNDK = 1 << 3,
     NO_KERNEL = 1 << 4,
     NO_XMLFILES = 1 << 5,
+    NO_SSDK = 1 << 6,
 
     EVERYTHING = 0,
     HALS_ONLY = ~NO_HALS,
diff --git a/parse_xml.cpp b/parse_xml.cpp
index 62ba763..74ba880 100644
--- a/parse_xml.cpp
+++ b/parse_xml.cpp
@@ -741,6 +741,20 @@
 
 const VendorNdkConverter vendorNdkConverter{};
 
+const XmlTextConverter<std::string> systemSdkVersionConverter{"version"};
+
+struct SystemSdkConverter : public XmlNodeConverter<SystemSdk> {
+    std::string elementName() const override { return "system-sdk"; }
+    void mutateNode(const SystemSdk& object, NodeType* root, DocType* d) const override {
+        appendChildren(root, systemSdkVersionConverter, object.versions(), d);
+    }
+    bool buildObject(SystemSdk* object, NodeType* root) const override {
+        return parseChildren(root, systemSdkVersionConverter, &object->mVersions);
+    }
+};
+
+const SystemSdkConverter systemSdkConverter{};
+
 struct HalManifestSepolicyConverter : public XmlNodeConverter<Version> {
     std::string elementName() const override { return "sepolicy"; }
     void mutateNode(const Version &m, NodeType *root, DocType *d) const override {
@@ -801,6 +815,11 @@
 
                 appendChildren(root, vendorNdkConverter, m.framework.mVendorNdks, d);
             }
+            if (!(flags & SerializeFlag::NO_SSDK)) {
+                if (!m.framework.mSystemSdk.empty()) {
+                    appendChild(root, systemSdkConverter(m.framework.mSystemSdk, d));
+                }
+            }
         }
 
         if (!(flags & SerializeFlag::NO_XMLFILES)) {
@@ -846,6 +865,10 @@
             if (!parseChildren(root, vendorNdkConverter, &object->framework.mVendorNdks)) {
                 return false;
             }
+
+            if (!parseOptionalChild(root, systemSdkConverter, {}, &object->framework.mSystemSdk)) {
+                return false;
+            }
         }
         for (auto &&hal : hals) {
             std::string description{hal.name};
@@ -949,6 +972,12 @@
                     appendChild(root, vendorNdkConverter(m.device.mVendorNdk, d));
                 }
             }
+
+            if (!(flags & SerializeFlag::NO_SSDK)) {
+                if (!m.device.mSystemSdk.empty()) {
+                    appendChild(root, systemSdkConverter(m.device.mSystemSdk, d));
+                }
+            }
         }
 
         if (!(flags & SerializeFlag::NO_XMLFILES)) {
@@ -1000,6 +1029,10 @@
             if (!parseOptionalChild(root, vendorNdkConverter, {}, &object->device.mVendorNdk)) {
                 return false;
             }
+
+            if (!parseOptionalChild(root, systemSdkConverter, {}, &object->device.mSystemSdk)) {
+                return false;
+            }
         }
 
         if (!kMetaVersion.minorAtLeast(version)) {
diff --git a/test/AssembleVintfTest.cpp b/test/AssembleVintfTest.cpp
index 53a1d0d..4e481ad 100644
--- a/test/AssembleVintfTest.cpp
+++ b/test/AssembleVintfTest.cpp
@@ -356,5 +356,36 @@
     EXPECT_TRUE(getInstance()->assemble());
 }
 
+TEST_F(AssembleVintfTest, MatrixSystemSdk) {
+    addInput("compatibility_matrix.xml",
+             "<compatibility-matrix version=\"1.0\" type=\"device\"/>\n");
+    getInstance()->setFakeEnv("BOARD_SYSTEMSDK_VERSIONS", "P 1 2 ");
+    EXPECT_TRUE(getInstance()->assemble());
+    EXPECT_IN(
+        "<compatibility-matrix version=\"1.0\" type=\"device\">\n"
+        "    <system-sdk>\n"
+        "        <version>1</version>\n"
+        "        <version>2</version>\n"
+        "        <version>P</version>\n"
+        "    </system-sdk>\n"
+        "</compatibility-matrix>\n",
+        getOutput());
+}
+
+TEST_F(AssembleVintfTest, ManifestSystemSdk) {
+    addInput("manifest.xml", "<manifest version=\"1.0\" type=\"framework\"/>\n");
+    getInstance()->setFakeEnv("PRODUCT_SYSTEMSDK_VERSIONS", "P 1 2 ");
+    EXPECT_TRUE(getInstance()->assemble());
+    EXPECT_IN(
+        "<manifest version=\"1.0\" type=\"framework\">\n"
+        "    <system-sdk>\n"
+        "        <version>1</version>\n"
+        "        <version>2</version>\n"
+        "        <version>P</version>\n"
+        "    </system-sdk>\n"
+        "</manifest>\n",
+        getOutput());
+}
+
 }  // namespace vintf
 }  // namespace android
diff --git a/test/LibVintfTest.cpp b/test/LibVintfTest.cpp
index 481897a..e934360 100644
--- a/test/LibVintfTest.cpp
+++ b/test/LibVintfTest.cpp
@@ -2635,6 +2635,82 @@
         gHalManifestConverter(manifest, SerializeFlag::HALS_ONLY));
 }
 
+// Make sure missing tags in old VINTF files does not cause incompatibilities.
+TEST_F(LibVintfTest, Empty) {
+    CompatibilityMatrix cm;
+    HalManifest manifest;
+    std::string xml;
+    std::string error;
+
+    xml = "<compatibility-matrix version=\"1.0\" type=\"device\"/>\n";
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&cm, xml))
+        << gCompatibilityMatrixConverter.lastError();
+
+    xml = "<manifest version=\"1.0\" type=\"framework\"/>\n";
+    EXPECT_TRUE(gHalManifestConverter(&manifest, xml)) << gHalManifestConverter.lastError();
+
+    EXPECT_TRUE(manifest.checkCompatibility(cm, &error)) << error;
+}
+
+TEST_F(LibVintfTest, SystemSdk) {
+    CompatibilityMatrix cm;
+    std::string xml;
+    std::string error;
+
+    xml =
+        "<compatibility-matrix version=\"1.0\" type=\"device\">\n"
+        "    <system-sdk>\n"
+        "        <version>1</version>\n"
+        "        <version>P</version>\n"
+        "    </system-sdk>\n"
+        "</compatibility-matrix>\n";
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&cm, xml))
+        << gCompatibilityMatrixConverter.lastError();
+    EXPECT_EQ(xml, gCompatibilityMatrixConverter(cm, ~SerializeFlag::NO_SSDK));
+
+    {
+        HalManifest manifest;
+        xml =
+            "<manifest version=\"1.0\" type=\"framework\">\n"
+            "    <system-sdk>\n"
+            "        <version>1</version>\n"
+            "        <version>P</version>\n"
+            "    </system-sdk>\n"
+            "</manifest>\n";
+        EXPECT_TRUE(gHalManifestConverter(&manifest, xml)) << gHalManifestConverter.lastError();
+        EXPECT_EQ(xml, gHalManifestConverter(manifest, ~SerializeFlag::NO_SSDK));
+
+        EXPECT_TRUE(manifest.checkCompatibility(cm, &error)) << error;
+    }
+
+    {
+        HalManifest manifest;
+        xml =
+            "<manifest version=\"1.0\" type=\"framework\">\n"
+            "    <system-sdk>\n"
+            "        <version>1</version>\n"
+            "        <version>3</version>\n"
+            "        <version>P</version>\n"
+            "    </system-sdk>\n"
+            "</manifest>\n";
+        EXPECT_TRUE(gHalManifestConverter(&manifest, xml)) << gHalManifestConverter.lastError();
+        EXPECT_TRUE(manifest.checkCompatibility(cm, &error));
+    }
+
+    {
+        HalManifest manifest;
+        xml =
+            "<manifest version=\"1.0\" type=\"framework\">\n"
+            "    <system-sdk>\n"
+            "        <version>1</version>\n"
+            "    </system-sdk>\n"
+            "</manifest>\n";
+        EXPECT_TRUE(gHalManifestConverter(&manifest, xml)) << gHalManifestConverter.lastError();
+        EXPECT_FALSE(manifest.checkCompatibility(cm, &error));
+        EXPECT_TRUE(error.find("System SDK") != std::string::npos) << error;
+    }
+}
+
 } // namespace vintf
 } // namespace android