AAPT: mipmaps should not end up in Splits

Mipmaps are never filtered, and so they will always
end up in the base APK. Make sure they get omitted from
any split.

Change-Id: Id24b082bc9bd2d3f031a58bd0de4d30b4f0de7e0
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 869a6fc..1d93b89 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1399,7 +1399,8 @@
             sp<ApkSplit>& split = splits.editItemAt(i);
             sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"),
                     AaptGroupEntry(), String8());
-            err = table.flatten(bundle, split->getResourceFilter(), flattenedTable);
+            err = table.flatten(bundle, split->getResourceFilter(),
+                    flattenedTable, split->isBase());
             if (err != NO_ERROR) {
                 fprintf(stderr, "Failed to generate resource table for split '%s'\n",
                         split->getPrintableName().string());
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 49d8699..b3c364b 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2074,10 +2074,11 @@
     return mNumLocal > 0;
 }
 
-sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter)
+sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
+        const bool isBase)
 {
     sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
-    status_t err = flatten(bundle, filter, data);
+    status_t err = flatten(bundle, filter, data, isBase);
     return err == NO_ERROR ? data : NULL;
 }
 
@@ -2699,7 +2700,9 @@
     return err;
 }
 
-status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest)
+status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
+        const sp<AaptFile>& dest,
+        const bool isBase)
 {
     const ConfigDescription nullConfig;
 
@@ -2768,6 +2771,13 @@
                 configTypeName = "2value";
             }
 
+            // mipmaps don't get filtered, so they will
+            // allways end up in the base. Make sure they
+            // don't end up in a split.
+            if (typeName == mipmap16 && !isBase) {
+                continue;
+            }
+
             const bool filterable = (typeName != mipmap16);
 
             const size_t N = t->getOrderedConfigs().size();
@@ -2871,10 +2881,12 @@
             return amt;
         }
 
-        status_t err = flattenLibraryTable(data, libraryPackages);
-        if (err != NO_ERROR) {
-            fprintf(stderr, "ERROR: failed to write library table\n");
-            return err;
+        if (isBase) {
+            status_t err = flattenLibraryTable(data, libraryPackages);
+            if (err != NO_ERROR) {
+                fprintf(stderr, "ERROR: failed to write library table\n");
+                return err;
+            }
         }
 
         // Build the type chunks inside of this package.
@@ -2890,6 +2902,7 @@
                 continue;
             }
             const bool filterable = (typeName != mipmap16);
+            const bool skipEntireType = (typeName == mipmap16 && !isBase);
 
             const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
 
@@ -2927,6 +2940,11 @@
                     if (cl->getPublic()) {
                         typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
                     }
+
+                    if (skipEntireType) {
+                        continue;
+                    }
+
                     const size_t CN = cl->getEntries().size();
                     for (size_t ci=0; ci<CN; ci++) {
                         if (filterable && !filter->match(cl->getEntries().keyAt(ci))) {
@@ -2943,6 +2961,10 @@
                 }
             }
             
+            if (skipEntireType) {
+                continue;
+            }
+
             // We need to write one type chunk for each configuration for
             // which we have entries in this type.
             const size_t NC = t->getUniqueConfigs().size();
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index d4f47ef..3721de4 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -165,7 +165,8 @@
     size_t numLocalResources() const;
     bool hasResources() const;
 
-    sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter);
+    sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
+            const bool isBase);
 
     static inline uint32_t makeResId(uint32_t packageId,
                                      uint32_t typeId,
@@ -206,7 +207,8 @@
     void addLocalization(const String16& name, const String8& locale, const SourcePos& src);
     status_t validateLocalizations(void);
 
-    status_t flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest);
+    status_t flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
+            const sp<AaptFile>& dest, const bool isBase);
     status_t flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs);
 
     void writePublicDefinitions(const String16& package, FILE* fp);
diff --git a/tools/aapt/tests/ResourceFilter_test.cpp b/tools/aapt/tests/ResourceFilter_test.cpp
index 30697bb..b55379e 100644
--- a/tools/aapt/tests/ResourceFilter_test.cpp
+++ b/tools/aapt/tests/ResourceFilter_test.cpp
@@ -126,3 +126,38 @@
     EXPECT_TRUE(filter.match(config));
 }
 
+TEST(StrongResourceFilterTest, MatchesDensities) {
+    ConfigDescription config;
+    config.density = 160;
+    config.version = 4;
+    std::set<ConfigDescription> configs;
+    configs.insert(config);
+
+    StrongResourceFilter filter(configs);
+
+    ConfigDescription expectedConfig;
+    expectedConfig.density = 160;
+    expectedConfig.version = 4;
+    ASSERT_TRUE(filter.match(expectedConfig));
+}
+
+TEST(StrongResourceFilterTest, MatchOnlyMdpiAndExcludeAllOthers) {
+    std::set<ConfigDescription> configsToMatch;
+    ConfigDescription config;
+    config.density = 160;
+    config.version = 4;
+    configsToMatch.insert(config);
+
+    std::set<ConfigDescription> configsToNotMatch;
+    config.density = 480;
+    configsToNotMatch.insert(config);
+
+    AndResourceFilter filter;
+    filter.addFilter(new InverseResourceFilter(new StrongResourceFilter(configsToNotMatch)));
+    filter.addFilter(new StrongResourceFilter(configsToMatch));
+
+    ConfigDescription expectedConfig;
+    expectedConfig.density = 160;
+    expectedConfig.version = 4;
+    ASSERT_TRUE(filter.match(expectedConfig));
+}