Merge "AAPT2: Add version collapsing"
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 4b5ea65..f2c13ba 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -39,6 +39,7 @@
 	link/PrivateAttributeMover.cpp \
 	link/ReferenceLinker.cpp \
 	link/TableMerger.cpp \
+	link/VersionCollapser.cpp \
 	link/XmlReferenceLinker.cpp \
 	process/SymbolTable.cpp \
 	proto/ProtoHelpers.cpp \
@@ -87,6 +88,7 @@
 	link/ProductFilter_test.cpp \
 	link/ReferenceLinker_test.cpp \
 	link/TableMerger_test.cpp \
+	link/VersionCollapser_test.cpp \
 	link/XmlReferenceLinker_test.cpp \
 	process/SymbolTable_test.cpp \
 	proto/TableProtoSerializer_test.cpp \
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index 30047f7..51d8ca6 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -17,6 +17,8 @@
 #ifndef AAPT_APP_INFO_H
 #define AAPT_APP_INFO_H
 
+#include "util/Maybe.h"
+
 #include <string>
 
 namespace aapt {
@@ -30,6 +32,11 @@
      * App's package name.
      */
     std::u16string package;
+
+    /**
+     * The App's minimum SDK version.
+     */
+    Maybe<std::u16string> minSdkVersion;
 };
 
 } // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index a0a7efc..302c04f 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -16,6 +16,7 @@
 
 #include "NameMangler.h"
 #include "ResourceUtils.h"
+#include "SdkConstants.h"
 #include "flatten/ResourceTypeExtensions.h"
 #include "util/Files.h"
 #include "util/Util.h"
@@ -402,6 +403,21 @@
     return false;
 }
 
+Maybe<int> tryParseSdkVersion(const StringPiece16& str) {
+    StringPiece16 trimmedStr(util::trimWhitespace(str));
+    android::Res_value value;
+    if (android::ResTable::stringToInt(trimmedStr.data(), trimmedStr.size(), &value)) {
+        return static_cast<int>(value.data);
+    }
+
+    // Try parsing the code name.
+    std::pair<StringPiece16, int> entry = getDevelopmentSdkCodeNameAndVersion();
+    if (entry.first == trimmedStr) {
+        return entry.second;
+    }
+    return {};
+}
+
 std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece16& str) {
     bool result = false;
     if (tryParseBool(str, &result)) {
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index a0fbcc6..3a03caf 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -79,6 +79,11 @@
  */
 bool tryParseBool(const StringPiece16& str, bool* outValue);
 
+/**
+ * Parses an SDK version, which can be an integer, or a letter from A-Z.
+ */
+Maybe<int> tryParseSdkVersion(const StringPiece16& str);
+
 /*
  * Returns a Reference, or None Maybe instance if the string `str` was parsed as a
  * valid reference to a style.
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index c2a22bf..7312ee3 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -23,6 +23,9 @@
 
 namespace aapt {
 
+static const char16_t* sDevelopmentSdkCodeName = u"O";
+static int sDevelopmentSdkLevel = 26;
+
 static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = {
     { 0x021c, 1 },
     { 0x021d, 2 },
@@ -735,4 +738,8 @@
     return SDK_LOLLIPOP_MR1;
 }
 
+std::pair<StringPiece16, int> getDevelopmentSdkCodeNameAndVersion() {
+    return std::make_pair(StringPiece16(sDevelopmentSdkCodeName), sDevelopmentSdkLevel);
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 282ed9a..a6dba8b 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -19,6 +19,8 @@
 
 #include "Resource.h"
 
+#include <utility>
+
 namespace aapt {
 
 enum {
@@ -47,6 +49,7 @@
 
 size_t findAttributeSdkLevel(ResourceId id);
 size_t findAttributeSdkLevel(const ResourceName& name);
+std::pair<StringPiece16, int> getDevelopmentSdkCodeNameAndVersion();
 
 } // namespace aapt
 
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index 2452a1d..5d11c40 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -454,6 +454,10 @@
        return nullptr;
     }
 
+    int getMinSdkVersion() override {
+        return 0;
+    }
+
 private:
     StdErrDiagnostics mDiagnostics;
     bool mVerbose = false;
diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/diff/Diff.cpp
index 20b7b59..67333a2 100644
--- a/tools/aapt2/diff/Diff.cpp
+++ b/tools/aapt2/diff/Diff.cpp
@@ -51,6 +51,10 @@
         return false;
     }
 
+    int getMinSdkVersion() override {
+        return 0;
+    }
+
 private:
     std::u16string mEmpty;
     StdErrDiagnostics mDiagnostics;
diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp
index 56b9f9a..dba2d28 100644
--- a/tools/aapt2/dump/Dump.cpp
+++ b/tools/aapt2/dump/Dump.cpp
@@ -142,6 +142,10 @@
         mVerbose = val;
     }
 
+    int getMinSdkVersion() override {
+        return 0;
+    }
+
 private:
     StdErrDiagnostics mDiagnostics;
     bool mVerbose = false;
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp
index 39c4fd3..d0f831e 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -15,14 +15,10 @@
  */
 
 #include "flatten/TableFlattener.h"
-#include "test/Builders.h"
-#include "test/Context.h"
+#include "test/Test.h"
 #include "unflatten/BinaryResourceParser.h"
 #include "util/Util.h"
 
-
-#include <gtest/gtest.h>
-
 using namespace android;
 
 namespace aapt {
@@ -150,8 +146,8 @@
                       test::buildReference(u"@com.app.test:id/one", ResourceId(0x7f020000)))
             .addValue(u"@com.app.test:integer/one", ResourceId(0x7f030000),
                       util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
-            .addValue(u"@com.app.test:integer/one", ResourceId(0x7f030000),
-                      test::parseConfigOrDie("v1"),
+            .addValue(u"@com.app.test:integer/one", test::parseConfigOrDie("v1"),
+                      ResourceId(0x7f030000),
                       util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
             .addString(u"@com.app.test:string/test", ResourceId(0x7f040000), u"foo")
             .addString(u"@com.app.test:layout/bar", ResourceId(0x7f050000), u"res/layout/bar.xml")
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 459c330..8ed27c3 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -27,7 +27,9 @@
 
 bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
                                      const int sdkVersionToGenerate) {
+    // We assume the caller is trying to generate a version greater than the current configuration.
     assert(sdkVersionToGenerate > config.sdkVersion);
+
     const auto endIter = entry->values.end();
     auto iter = entry->values.begin();
     for (; iter != endIter; ++iter) {
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 9b3a87c..99a7c02 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -16,10 +16,7 @@
 
 #include "ConfigDescription.h"
 #include "link/Linkers.h"
-#include "test/Builders.h"
-#include "test/Context.h"
-
-#include <gtest/gtest.h>
+#include "test/Test.h"
 
 namespace aapt {
 
@@ -54,7 +51,7 @@
 TEST(AutoVersionerTest, VersionStylesForTable) {
     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
             .setPackageId(u"app", 0x7f)
-            .addValue(u"@app:style/Foo", ResourceId(0x7f020000), test::parseConfigOrDie("v4"),
+            .addValue(u"@app:style/Foo", test::parseConfigOrDie("v4"), ResourceId(0x7f020000),
                       test::StyleBuilder()
                             .addItem(u"@android:attr/onClick", ResourceId(0x0101026f),
                                      util::make_unique<Id>())
@@ -65,7 +62,7 @@
                             .addItem(u"@android:attr/colorAccent", ResourceId(0x01010435),
                                      util::make_unique<Id>())
                             .build())
-            .addValue(u"@app:style/Foo", ResourceId(0x7f020000), test::parseConfigOrDie("v21"),
+            .addValue(u"@app:style/Foo", test::parseConfigOrDie("v21"), ResourceId(0x7f020000),
                       test::StyleBuilder()
                             .addItem(u"@android:attr/paddingEnd", ResourceId(0x010103b4),
                                      util::make_unique<Id>())
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 4767bc9..9411053 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -123,6 +123,14 @@
         mVerbose = val;
     }
 
+    int getMinSdkVersion() override {
+        return mMinSdkVersion;
+    }
+
+    void setMinSdkVersion(int minSdk) {
+        mMinSdkVersion = minSdk;
+    }
+
 private:
     StdErrDiagnostics mDiagnostics;
     NameMangler mNameMangler;
@@ -130,6 +138,7 @@
     uint8_t mPackageId = 0x0;
     SymbolTable mSymbols;
     bool mVerbose = false;
+    int mMinSdkVersion = 0;
 };
 
 static bool copyFileToArchive(io::IFile* file, const std::string& outPath,
@@ -578,14 +587,33 @@
         return true;
     }
 
-    Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
+    Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes, IDiagnostics* diag) {
         // Make sure the first element is <manifest> with package attribute.
         if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
-            if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
-                if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
-                    return AppInfo{ packageAttr->value };
+            AppInfo appInfo;
+
+            if (!manifestEl->namespaceUri.empty() || manifestEl->name != u"manifest") {
+                diag->error(DiagMessage(xmlRes->file.source) << "root tag must be <manifest>");
+                return {};
+            }
+
+            xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package");
+            if (!packageAttr) {
+                diag->error(DiagMessage(xmlRes->file.source)
+                            << "<manifest> must have a 'package' attribute");
+                return {};
+            }
+
+            appInfo.package = packageAttr->value;
+
+            if (xml::Element* usesSdkEl = manifestEl->findChild({}, u"uses-sdk")) {
+                if (xml::Attribute* minSdk =
+                        usesSdkEl->findAttribute(xml::kSchemaAndroid, u"minSdkVersion")) {
+                    appInfo.minSdkVersion = minSdk->value;
                 }
             }
+
+            return appInfo;
         }
         return {};
     }
@@ -1089,11 +1117,11 @@
             return 1;
         }
 
-        if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
-            mContext->setCompilationPackage(maybeAppInfo.value().package);
+        if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(),
+                                                                     mContext->getDiagnostics())) {
+            AppInfo& appInfo = maybeAppInfo.value();
+            mContext->setCompilationPackage(appInfo.package);
         } else {
-            mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
-                                             << "no package specified in <manifest> tag");
             return 1;
         }
 
@@ -1297,6 +1325,28 @@
             }
         }
 
+        Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(),
+                                                                 mContext->getDiagnostics());
+        if (maybeAppInfo && maybeAppInfo.value().minSdkVersion) {
+            if (Maybe<int> maybeMinSdkVersion =
+                    ResourceUtils::tryParseSdkVersion(maybeAppInfo.value().minSdkVersion.value())) {
+                mContext->setMinSdkVersion(maybeMinSdkVersion.value());
+            }
+        }
+
+        if (!mOptions.staticLib && mContext->getMinSdkVersion() > 0) {
+            if (mContext->verbose()) {
+                mContext->getDiagnostics()->note(
+                        DiagMessage() << "collapsing resource versions for minimum SDK "
+                        << mContext->getMinSdkVersion());
+            }
+
+            VersionCollapser collapser;
+            if (!collapser.consume(mContext, &mFinalTable)) {
+                return 1;
+            }
+        }
+
         if (mOptions.staticLib) {
             if (!flattenTableToPb(&mFinalTable, archiveWriter.get())) {
                 mContext->getDiagnostics()->error(DiagMessage()
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index ec532ab..43b8fb4 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -44,14 +44,21 @@
 bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
                                      const int sdkVersionToGenerate);
 
-struct AutoVersioner : public IResourceTableConsumer {
+class AutoVersioner : public IResourceTableConsumer {
+public:
     bool consume(IAaptContext* context, ResourceTable* table) override;
 };
 
-struct XmlAutoVersioner : public IXmlResourceConsumer {
+class XmlAutoVersioner : public IXmlResourceConsumer {
+public:
     bool consume(IAaptContext* context, xml::XmlResource* resource) override;
 };
 
+class VersionCollapser : public IResourceTableConsumer {
+public:
+    bool consume(IAaptContext* context, ResourceTable* table) override;
+};
+
 /**
  * If any attribute resource values are defined as public, this consumer will move all private
  * attribute resource values to a private ^private-attr type, avoiding backwards compatibility
diff --git a/tools/aapt2/link/VersionCollapser.cpp b/tools/aapt2/link/VersionCollapser.cpp
new file mode 100644
index 0000000..c0e96be
--- /dev/null
+++ b/tools/aapt2/link/VersionCollapser.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 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 "ResourceTable.h"
+#include "link/Linkers.h"
+
+#include <algorithm>
+#include <vector>
+
+namespace aapt {
+
+template <typename Iterator, typename Pred>
+class FilterIterator {
+public:
+    FilterIterator(Iterator begin, Iterator end, Pred pred=Pred()) :
+            mCurrent(begin), mEnd(end), mPred(pred) {
+        advance();
+    }
+
+    bool hasNext() {
+        return mCurrent != mEnd;
+    }
+
+    Iterator nextIter() {
+        Iterator iter = mCurrent;
+        ++mCurrent;
+        advance();
+        return iter;
+    }
+
+    typename Iterator::reference next() {
+        return *nextIter();
+    }
+
+private:
+    void advance() {
+        for (; mCurrent != mEnd; ++mCurrent) {
+            if (mPred(*mCurrent)) {
+                return;
+            }
+        }
+    }
+
+    Iterator mCurrent, mEnd;
+    Pred mPred;
+};
+
+template <typename Iterator, typename Pred>
+FilterIterator<Iterator, Pred> makeFilterIterator(Iterator begin, Iterator end=Iterator(),
+                                                  Pred pred=Pred()) {
+    return FilterIterator<Iterator, Pred>(begin, end, pred);
+}
+
+/**
+ * Every Configuration with an SDK version specified that is less than minSdk will be removed.
+ * The exception is when there is no exact matching resource for the minSdk. The next smallest
+ * one will be kept.
+ */
+static void collapseVersions(int minSdk, ResourceEntry* entry) {
+    // First look for all sdks less than minSdk.
+    for (auto iter = entry->values.rbegin(); iter != entry->values.rend(); ++iter) {
+        // Check if the item was already marked for removal.
+        if (!(*iter)) {
+            continue;
+        }
+
+        const ConfigDescription& config = (*iter)->config;
+        if (config.sdkVersion <= minSdk) {
+            // This is the first configuration we've found with a smaller or equal SDK level
+            // to the minimum. We MUST keep this one, but remove all others we find, which get
+            // overridden by this one.
+
+            ConfigDescription configWithoutSdk = config;
+            configWithoutSdk.sdkVersion = 0;
+            auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
+                // Check that the value hasn't already been marked for removal.
+                if (!val) {
+                    return false;
+                }
+
+                // Only return Configs that differ in SDK version.
+                configWithoutSdk.sdkVersion = val->config.sdkVersion;
+                return configWithoutSdk == val->config && val->config.sdkVersion <= minSdk;
+            };
+
+            // Remove the rest that match.
+            auto filterIter = makeFilterIterator(iter + 1, entry->values.rend(), pred);
+            while (filterIter.hasNext()) {
+                filterIter.next() = {};
+            }
+        }
+    }
+
+    // Now erase the nullptr values.
+    entry->values.erase(std::remove_if(entry->values.begin(), entry->values.end(),
+                   [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
+        return val == nullptr;
+    }), entry->values.end());
+}
+
+bool VersionCollapser::consume(IAaptContext* context, ResourceTable* table) {
+    const int minSdk = context->getMinSdkVersion();
+    for (auto& package : table->packages) {
+        for (auto& type : package->types) {
+            for (auto& entry : type->entries) {
+                collapseVersions(minSdk, entry.get());
+            }
+        }
+    }
+    return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/VersionCollapser_test.cpp b/tools/aapt2/link/VersionCollapser_test.cpp
new file mode 100644
index 0000000..b5387fe
--- /dev/null
+++ b/tools/aapt2/link/VersionCollapser_test.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 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 "link/Linkers.h"
+#include "test/Test.h"
+
+namespace aapt {
+
+template <typename T>
+using uptr = std::unique_ptr<T>;
+
+static uptr<ResourceTable> buildTableWithConfigs(const StringPiece16& name,
+                                                 std::initializer_list<std::string> list) {
+    test::ResourceTableBuilder builder;
+    for (const std::string& item : list) {
+        builder.addSimple(name, test::parseConfigOrDie(item));
+    }
+    return builder.build();
+}
+
+TEST(VersionCollapserTest, CollapseVersions) {
+    uptr<IAaptContext> context = test::ContextBuilder().setMinSdkVersion(7).build();
+
+    const StringPiece16 resName = u"@android:string/foo";
+
+    uptr<ResourceTable> table =
+            buildTableWithConfigs(resName,
+                                  { "land-v4", "land-v5", "sw600dp", "land-v6",
+                                          "land-v14", "land-v21" });
+
+    VersionCollapser collapser;
+    ASSERT_TRUE(collapser.consume(context.get(), table.get()));
+
+    // These should be removed.
+    EXPECT_EQ(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v4")));
+    EXPECT_EQ(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v5")));
+
+    // These should remain.
+    EXPECT_NE(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("sw600dp")));
+    EXPECT_NE(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v6")));
+    EXPECT_NE(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v14")));
+    EXPECT_NE(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v21")));
+}
+
+TEST(VersionCollapserTest, CollapseVersionsWhenMinSdkIsHighest) {
+    uptr<IAaptContext> context = test::ContextBuilder().setMinSdkVersion(26).build();
+
+    const StringPiece16 resName = u"@android:string/foo";
+
+    uptr<ResourceTable> table =
+                buildTableWithConfigs(resName,
+                                      { "land-v4", "land-v5", "sw600dp", "land-v6",
+                                              "land-v14", "land-v21" });
+    VersionCollapser collapser;
+    ASSERT_TRUE(collapser.consume(context.get(), table.get()));
+
+    // These should all be removed.
+    EXPECT_EQ(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v4")));
+    EXPECT_EQ(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v5")));
+    EXPECT_EQ(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v6")));
+    EXPECT_EQ(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v14")));
+
+    // These should remain.
+    EXPECT_NE(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("sw600dp")));
+    EXPECT_NE(nullptr,
+              test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v21")));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index 9affb83..762725d 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -41,6 +41,7 @@
     virtual uint8_t getPackageId() = 0;
     virtual NameMangler* getNameMangler() = 0;
     virtual bool verbose() = 0;
+    virtual int getMinSdkVersion() = 0;
 };
 
 struct IResourceTableConsumer {
diff --git a/tools/aapt2/split/TableSplitter_test.cpp b/tools/aapt2/split/TableSplitter_test.cpp
index 74ca32e..2d013e4 100644
--- a/tools/aapt2/split/TableSplitter_test.cpp
+++ b/tools/aapt2/split/TableSplitter_test.cpp
@@ -15,10 +15,7 @@
  */
 
 #include "split/TableSplitter.h"
-#include "test/Builders.h"
-#include "test/Common.h"
-
-#include <gtest/gtest.h>
+#include "test/Test.h"
 
 namespace aapt {
 
@@ -32,7 +29,7 @@
                               test::parseConfigOrDie("xhdpi"))
             .addFileReference(u"@android:drawable/icon", u"res/drawable-xxhdpi/icon.png",
                               test::parseConfigOrDie("xxhdpi"))
-            .addSimple(u"@android:string/one", {})
+            .addSimple(u"@android:string/one")
             .build();
 
     TableSplitterOptions options;
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 8eb4bc8..fb1d8f8 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -50,6 +50,11 @@
         return addValue(name, id, util::make_unique<Id>());
     }
 
+    ResourceTableBuilder& addSimple(const StringPiece16& name, const ConfigDescription& config,
+                                    const ResourceId id = {}) {
+        return addValue(name, config, id, util::make_unique<Id>());
+    }
+
     ResourceTableBuilder& addReference(const StringPiece16& name, const StringPiece16& ref) {
         return addReference(name, {}, ref);
     }
@@ -70,7 +75,7 @@
 
     ResourceTableBuilder& addString(const StringPiece16& name, const ResourceId id,
                                     const ConfigDescription& config, const StringPiece16& str) {
-        return addValue(name, id, config,
+        return addValue(name, config, id,
                         util::make_unique<String>(mTable->stringPool.makeRef(str)));
     }
 
@@ -86,7 +91,7 @@
 
     ResourceTableBuilder& addFileReference(const StringPiece16& name, const StringPiece16& path,
                                            const ConfigDescription& config) {
-        return addValue(name, {}, config,
+        return addValue(name, config, {},
                         util::make_unique<FileReference>(mTable->stringPool.makeRef(path)));
     }
 
@@ -96,13 +101,12 @@
     }
 
     ResourceTableBuilder& addValue(const StringPiece16& name, const ResourceId id,
-                                       std::unique_ptr<Value> value) {
-        return addValue(name, id, {}, std::move(value));
+                                   std::unique_ptr<Value> value) {
+        return addValue(name, {}, id, std::move(value));
     }
 
-    ResourceTableBuilder& addValue(const StringPiece16& name, const ResourceId id,
-                                   const ConfigDescription& config,
-                                   std::unique_ptr<Value> value) {
+    ResourceTableBuilder& addValue(const StringPiece16& name, const ConfigDescription& config,
+                                   const ResourceId id, std::unique_ptr<Value> value) {
         ResourceName resName = parseNameOrDie(name);
         bool result = mTable->addResourceAllowMangled(resName, id, config, std::string(),
                                                       std::move(value), &mDiagnostics);
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 96752d3..36f568b 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -58,17 +58,19 @@
         return false;
     }
 
+    int getMinSdkVersion() override {
+        return mMinSdkVersion;
+    }
+
 private:
     friend class ContextBuilder;
 
-    Context() : mNameMangler({}) {
-    }
-
     Maybe<std::u16string> mCompilationPackage;
     Maybe<uint8_t> mPackageId;
     StdErrDiagnostics mDiagnostics;
     SymbolTable mSymbols;
-    NameMangler mNameMangler;
+    NameMangler mNameMangler = NameMangler({});
+    int mMinSdkVersion = 0;
 };
 
 class ContextBuilder {
@@ -96,6 +98,11 @@
         return *this;
     }
 
+    ContextBuilder& setMinSdkVersion(int minSdk) {
+        mContext->mMinSdkVersion = minSdk;
+        return *this;
+    }
+
     std::unique_ptr<Context> build() {
         return std::move(mContext);
     }