Implement FCM Version in matrices / manifests.

"level" is an attribute on compatibility matrices / manifests
to specify the FCM Version they declare / implement. Value can
be "legacy" or a positive number, or empty (for old files).

Test: libvintf_test
Test: Built manifests / matrices has not changed (because
      value is "unspecified").

Bug: 69854976 device manfiest must specify FCM Version
Bug: 69636193 all matrices should be installed to system image
Bug: 64720381 deprecation schedule

Change-Id: I15d34343fae4ad79d86bd50e9de8c4f6ac09fdfd
Merged-In: I15d34343fae4ad79d86bd50e9de8c4f6ac09fdfd
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp
index 73d0384..f788569 100644
--- a/CompatibilityMatrix.cpp
+++ b/CompatibilityMatrix.cpp
@@ -38,6 +38,10 @@
     return mType;
 }
 
+Level CompatibilityMatrix::level() const {
+    return mLevel;
+}
+
 Version CompatibilityMatrix::getMinimumMetaVersion() const {
     // TODO(b/62801658): this needs to depend on whether there are 1.1 requirements
     // (e.g. required <xmlfile> entry)
@@ -68,7 +72,8 @@
 }
 
 bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) {
-    return lft.mType == rgt.mType && lft.mHals == rgt.mHals && lft.mXmlFiles == rgt.mXmlFiles &&
+    return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
+           lft.mXmlFiles == rgt.mXmlFiles &&
            (lft.mType != SchemaType::DEVICE || (lft.device.mVndk == rgt.device.mVndk)) &&
            (lft.mType != SchemaType::FRAMEWORK ||
             (lft.framework.mKernels == rgt.framework.mKernels &&
diff --git a/HalManifest.cpp b/HalManifest.cpp
index c633a7a..9683729 100644
--- a/HalManifest.cpp
+++ b/HalManifest.cpp
@@ -319,6 +319,10 @@
     return mType;
 }
 
+Level HalManifest::level() const {
+    return mLevel;
+}
+
 Version HalManifest::getMetaVersion() const {
     return mMetaVersion;
 }
@@ -352,7 +356,8 @@
 }
 
 bool operator==(const HalManifest &lft, const HalManifest &rgt) {
-    return lft.mType == rgt.mType && lft.mHals == rgt.mHals && lft.mXmlFiles == rgt.mXmlFiles &&
+    return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals &&
+           lft.mXmlFiles == rgt.mXmlFiles &&
            (lft.mType != SchemaType::DEVICE ||
             (lft.device.mSepolicyVersion == rgt.device.mSepolicyVersion)) &&
            (lft.mType != SchemaType::FRAMEWORK || (lft.framework.mVndks == rgt.framework.mVndks));
diff --git a/include/vintf/CompatibilityMatrix.h b/include/vintf/CompatibilityMatrix.h
index bc3fbf4..c9a8a9f 100644
--- a/include/vintf/CompatibilityMatrix.h
+++ b/include/vintf/CompatibilityMatrix.h
@@ -23,6 +23,7 @@
 #include <utils/Errors.h>
 
 #include "HalGroup.h"
+#include "Level.h"
 #include "MapValueIterator.h"
 #include "MatrixHal.h"
 #include "MatrixKernel.h"
@@ -40,6 +41,7 @@
     CompatibilityMatrix() : mType(SchemaType::FRAMEWORK) {};
 
     SchemaType type() const;
+    Level level() const;
     Version getMinimumMetaVersion() const;
 
     // If the corresponding <xmlfile> with the given version exists, for the first match,
@@ -68,6 +70,7 @@
     friend bool operator==(const CompatibilityMatrix &, const CompatibilityMatrix &);
 
     SchemaType mType;
+    Level mLevel = Level::UNSPECIFIED;
 
     // entries only for framework compatibility matrix.
     struct {
diff --git a/include/vintf/HalManifest.h b/include/vintf/HalManifest.h
index b553371..46657de 100644
--- a/include/vintf/HalManifest.h
+++ b/include/vintf/HalManifest.h
@@ -24,6 +24,7 @@
 #include <vector>
 
 #include "HalGroup.h"
+#include "Level.h"
 #include "ManifestHal.h"
 #include "MapValueIterator.h"
 #include "SchemaType.h"
@@ -93,6 +94,9 @@
     // Type of the manifest. FRAMEWORK or DEVICE.
     SchemaType type() const;
 
+    // FCM version that it implements.
+    Level level() const;
+
     // device.mSepolicyVersion. Assume type == device.
     // Abort if type != device.
     const Version &sepolicyVersion() const;
@@ -132,6 +136,7 @@
                                                        bool includeOptional = true) const;
 
     SchemaType mType;
+    Level mLevel = Level::UNSPECIFIED;
     // version attribute. Default is 1.0 for manifests created programatically.
     Version mMetaVersion{1, 0};
 
diff --git a/include/vintf/Level.h b/include/vintf/Level.h
new file mode 100644
index 0000000..49dad0b
--- /dev/null
+++ b/include/vintf/Level.h
@@ -0,0 +1,44 @@
+/*
+ * 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_LEVEL_H
+#define ANDROID_VINTF_LEVEL_H
+
+#include <stdint.h>
+
+namespace android {
+namespace vintf {
+
+// Manifest and Compatibility Matrix Level, a.k.a FCM Version, is a number assigned to each
+// manifest / matrix.
+// - For manifest, the FCM Version that it implements
+// - For matrix, the single FCM Version that this matrix file details.
+// This is not a strong-typed enum because Level can be any integer value. Listed are some
+// special values.
+enum Level : size_t {
+    // Non-Treble devices.
+    LEGACY = 0,
+    // Actual values starts from 1. Following are some historic values for convenience.
+    O = 1,
+    O_MR1 = 2,
+    // For older manifests and compatibility matrices, "level" is not specified.
+    UNSPECIFIED = SIZE_MAX,
+};
+
+}  // namespace vintf
+}  // namespace android
+
+#endif  // ANDROID_VINTF_LEVEL_H
diff --git a/include/vintf/parse_string.h b/include/vintf/parse_string.h
index 77d1830..00844d7 100644
--- a/include/vintf/parse_string.h
+++ b/include/vintf/parse_string.h
@@ -35,6 +35,7 @@
 std::ostream &operator<<(std::ostream &os, Tristate tr);
 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, const ManifestHal &hal);
 std::ostream &operator<<(std::ostream &os, const Version &ver);
 std::ostream &operator<<(std::ostream &os, const VersionRange &vr);
@@ -60,6 +61,7 @@
 bool parse(const std::string &s, Tristate *tr);
 bool parse(const std::string &s, SchemaType *ver);
 bool parse(const std::string& s, XmlSchemaFormat* ver);
+bool parse(const std::string& s, Level* l);
 bool parse(const std::string &s, KernelSepolicyVersion *ksv);
 bool parse(const std::string &s, Version *ver);
 bool parse(const std::string &s, VersionRange *vr);
diff --git a/parse_string.cpp b/parse_string.cpp
index a6bb418..7e23f5f 100644
--- a/parse_string.cpp
+++ b/parse_string.cpp
@@ -113,6 +113,33 @@
     }
 }
 
+bool parse(const std::string& s, Level* l) {
+    if (s.empty()) {
+        *l = Level::UNSPECIFIED;
+        return true;
+    }
+    if (s == "legacy") {
+        *l = Level::LEGACY;
+        return true;
+    }
+    size_t value;
+    if (!ParseUint(s, &value)) {
+        return false;
+    }
+    *l = static_cast<Level>(value);
+    return true;
+}
+
+std::ostream& operator<<(std::ostream& os, Level l) {
+    if (l == Level::UNSPECIFIED) {
+        return os;
+    }
+    if (l == Level::LEGACY) {
+        return os << "legacy";
+    }
+    return os << static_cast<size_t>(l);
+}
+
 // Notice that strtoull is used even though KernelConfigIntValue is signed int64_t,
 // because strtoull can accept negative values as well.
 // Notice that according to man strtoul, strtoull can actually accept
diff --git a/parse_xml.cpp b/parse_xml.cpp
index 1a0626a..350d10b 100644
--- a/parse_xml.cpp
+++ b/parse_xml.cpp
@@ -748,7 +748,9 @@
     void mutateNode(const HalManifest &m, NodeType *root, DocType *d) const override {
         appendAttr(root, "version", m.getMetaVersion());
         appendAttr(root, "type", m.mType);
-
+        if (m.mLevel != Level::UNSPECIFIED) {
+            this->appendAttr(root, "target-level", m.mLevel);
+        }
         appendChildren(root, manifestHalConverter, m.getHals(), d);
         if (m.mType == SchemaType::DEVICE) {
             appendChild(root, halManifestSepolicyConverter(m.device.mSepolicyVersion, d));
@@ -762,6 +764,7 @@
         std::vector<ManifestHal> hals;
         if (!parseAttr(root, "version", &object->mMetaVersion) ||
             !parseAttr(root, "type", &object->mType) ||
+            !parseOptionalAttr(root, "target-level", Level::UNSPECIFIED, &object->mLevel) ||
             !parseChildren(root, manifestHalConverter, &hals)) {
             return false;
         }
@@ -858,6 +861,9 @@
     void mutateNode(const CompatibilityMatrix &m, NodeType *root, DocType *d) const override {
         appendAttr(root, "version", m.getMinimumMetaVersion());
         appendAttr(root, "type", m.mType);
+        if (m.mLevel != Level::UNSPECIFIED) {
+            this->appendAttr(root, "level", m.mLevel);
+        }
         appendChildren(root, matrixHalConverter, iterateValues(m.mHals), d);
         if (m.mType == SchemaType::FRAMEWORK) {
             appendChildren(root, matrixKernelConverter, m.framework.mKernels, d);
@@ -872,8 +878,8 @@
     bool buildObject(CompatibilityMatrix *object, NodeType *root) const override {
         Version version;
         std::vector<MatrixHal> hals;
-        if (!parseAttr(root, "version", &version) ||
-            !parseAttr(root, "type", &object->mType) ||
+        if (!parseAttr(root, "version", &version) || !parseAttr(root, "type", &object->mType) ||
+            !parseOptionalAttr(root, "level", Level::UNSPECIFIED, &object->mLevel) ||
             !parseChildren(root, matrixHalConverter, &hals)) {
             return false;
         }
diff --git a/test/main.cpp b/test/main.cpp
index 8ec02a8..b8a25a2 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -2047,6 +2047,43 @@
 
 INSTANTIATE_TEST_CASE_P(KernelConfigParser, KernelConfigParserInvalidTest, ::testing::Bool());
 
+TEST_F(LibVintfTest, MatrixLevel) {
+    CompatibilityMatrix cm;
+    std::string xml;
+
+    xml = "<compatibility-matrix version=\"1.0\" type=\"framework\"/>";
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&cm, xml))
+        << gCompatibilityMatrixConverter.lastError();
+    EXPECT_EQ(Level::UNSPECIFIED, cm.level());
+
+    xml = "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"legacy\"/>";
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&cm, xml))
+        << gCompatibilityMatrixConverter.lastError();
+    EXPECT_EQ(Level::LEGACY, cm.level());
+
+    xml = "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"1\"/>";
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&cm, xml))
+        << gCompatibilityMatrixConverter.lastError();
+    EXPECT_EQ(1u, cm.level());
+}
+
+TEST_F(LibVintfTest, ManifestLevel) {
+    HalManifest manifest;
+    std::string xml;
+
+    xml = "<manifest version=\"1.0\" type=\"device\"/>";
+    EXPECT_TRUE(gHalManifestConverter(&manifest, xml)) << gHalManifestConverter.lastError();
+    EXPECT_EQ(Level::UNSPECIFIED, manifest.level());
+
+    xml = "<manifest version=\"1.0\" type=\"device\" target-level=\"legacy\"/>";
+    EXPECT_TRUE(gHalManifestConverter(&manifest, xml)) << gHalManifestConverter.lastError();
+    EXPECT_EQ(Level::LEGACY, manifest.level());
+
+    xml = "<manifest version=\"1.0\" type=\"device\" target-level=\"1\"/>";
+    EXPECT_TRUE(gHalManifestConverter(&manifest, xml)) << gHalManifestConverter.lastError();
+    EXPECT_EQ(1u, manifest.level());
+}
+
 } // namespace vintf
 } // namespace android