New aapt feature to do smarter filtering of configurations.

This adds a --preferred-configurations flag that specifies the
specific configurations you would like to have.

It is smarter than "-c" because it will avoid stripping a
configuration if that would result in there being no value
for the resource.

It is dumber than "-c" because it can't process as many kinds
of resources.  It is really only intended for bitmaps and use
with density configs.

This required re-arranging AaptAssets to group files together
by config again, like they used to be.  I think this hasn't
broken anything.  Hopefully.

Change-Id: I4e9d12ff6e6dbd1abb8fd4cb1814c6674b19d0e5
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index d66cdf0..637c8b3 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -3,6 +3,7 @@
 //
 
 #include "AaptAssets.h"
+#include "ResourceFilter.h"
 #include "Main.h"
 
 #include <utils/misc.h>
@@ -16,6 +17,8 @@
 static const char* kWildcardName = "any";
 static const char* kAssetDir = "assets";
 static const char* kResourceDir = "res";
+static const char* kValuesDir = "values";
+static const char* kMipmapDir = "mipmap";
 static const char* kInvalidChars = "/\\:";
 static const size_t kMaxAssetFileName = 100;
 
@@ -257,9 +260,69 @@
     return 1;
 }
 
+uint32_t
+AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
+{
+    switch (axis) {
+        case AXIS_MCC:
+            return config.mcc;
+        case AXIS_MNC:
+            return config.mnc;
+        case AXIS_LANGUAGE:
+            return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
+                | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
+        case AXIS_SCREENLAYOUTSIZE:
+            return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
+        case AXIS_ORIENTATION:
+            return config.orientation;
+        case AXIS_UIMODETYPE:
+            return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+        case AXIS_UIMODENIGHT:
+            return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+        case AXIS_DENSITY:
+            return config.density;
+        case AXIS_TOUCHSCREEN:
+            return config.touchscreen;
+        case AXIS_KEYSHIDDEN:
+            return config.inputFlags;
+        case AXIS_KEYBOARD:
+            return config.keyboard;
+        case AXIS_NAVIGATION:
+            return config.navigation;
+        case AXIS_SCREENSIZE:
+            return config.screenSize;
+        case AXIS_SMALLESTSCREENWIDTHDP:
+            return config.smallestScreenWidthDp;
+        case AXIS_SCREENWIDTHDP:
+            return config.screenWidthDp;
+        case AXIS_SCREENHEIGHTDP:
+            return config.screenHeightDp;
+        case AXIS_VERSION:
+            return config.version;
+    }
+    return 0;
+}
+
+bool
+AaptGroupEntry::configSameExcept(const ResTable_config& config,
+        const ResTable_config& otherConfig, int axis)
+{
+    for (int i=AXIS_START; i<=AXIS_END; i++) {
+        if (i == axis) {
+            continue;
+        }
+        if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 bool
 AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
 {
+    mParamsChanged = true;
+
     Vector<String8> parts;
 
     String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
@@ -629,79 +692,117 @@
 {
     String8 s = resType;
     if (this->mcc != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += mcc;
     }
     if (this->mnc != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += mnc;
     }
     if (this->locale != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += locale;
     }
     if (this->smallestScreenWidthDp != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += smallestScreenWidthDp;
     }
     if (this->screenWidthDp != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += screenWidthDp;
     }
     if (this->screenHeightDp != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += screenHeightDp;
     }
     if (this->screenLayoutSize != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += screenLayoutSize;
     }
     if (this->screenLayoutLong != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += screenLayoutLong;
     }
     if (this->orientation != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += orientation;
     }
     if (this->uiModeType != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += uiModeType;
     }
     if (this->uiModeNight != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += uiModeNight;
     }
     if (this->density != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += density;
     }
     if (this->touchscreen != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += touchscreen;
     }
     if (this->keysHidden != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += keysHidden;
     }
     if (this->keyboard != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += keyboard;
     }
     if (this->navHidden != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += navHidden;
     }
     if (this->navigation != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += navigation;
     }
     if (this->screenSize != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += screenSize;
     }
     if (this->version != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += version;
     }
 
@@ -1286,9 +1387,14 @@
     return v;
 }
 
-ResTable_config AaptGroupEntry::toParams() const
+const ResTable_config& AaptGroupEntry::toParams() const
 {
-    ResTable_config params;
+    if (!mParamsChanged) {
+        return mParams;
+    }
+
+    mParamsChanged = false;
+    ResTable_config& params(mParams);
     memset(&params, 0, sizeof(params));
     getMccName(mcc.string(), &params);
     getMncName(mnc.string(), &params);
@@ -1403,8 +1509,7 @@
 String8 AaptFile::getPrintableSource() const
 {
     if (hasData()) {
-        String8 name(mGroupEntry.locale.string());
-        name.appendPath(mGroupEntry.vendor.string());
+        String8 name(mGroupEntry.toDirName(String8()));
         name.appendPath(mPath);
         name.append(" #generated");
         return name;
@@ -1424,6 +1529,13 @@
         return NO_ERROR;
     }
 
+#if 0
+    printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
+            file->getSourceFile().string(),
+            file->getGroupEntry().toDirName(String8()).string(),
+            mLeaf.string(), mPath.string());
+#endif
+
     SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
                                                getPrintableSource().string());
     return UNKNOWN_ERROR;
@@ -1434,20 +1546,23 @@
 	mFiles.removeItemsAt(index);
 }
 
-void AaptGroup::print() const
+void AaptGroup::print(const String8& prefix) const
 {
-    printf("  %s\n", getPath().string());
+    printf("%s%s\n", prefix.string(), getPath().string());
     const size_t N=mFiles.size();
     size_t i;
     for (i=0; i<N; i++) {
         sp<AaptFile> file = mFiles.valueAt(i);
         const AaptGroupEntry& e = file->getGroupEntry();
         if (file->hasData()) {
-            printf("      Gen: (%s) %d bytes\n", e.toString().string(),
+            printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
                     (int)file->getSize());
         } else {
-            printf("      Src: %s\n", file->getPrintableSource().string());
+            printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
+                    file->getPrintableSource().string());
         }
+        //printf("%s  File Group Entry: %s\n", prefix.string(),
+        //        file->getGroupEntry().toDirName(String8()).string());
     }
 }
 
@@ -1514,38 +1629,6 @@
     mDirs.removeItem(name);
 }
 
-status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
-{
-	sp<AaptGroup> origGroup;
-
-	// Find and remove the given file with shear, brute force!
-	const size_t NG = mFiles.size();
-	size_t i;
-	for (i=0; origGroup == NULL && i<NG; i++) {
-		sp<AaptGroup> g = mFiles.valueAt(i);
-		const size_t NF = g->getFiles().size();
-		for (size_t j=0; j<NF; j++) {
-			if (g->getFiles().valueAt(j) == file) {
-				origGroup = g;
-				g->removeFile(j);
-				if (NF == 1) {
-					mFiles.removeItemsAt(i);
-				}
-				break;
-			}
-		}
-	}
-
-	//printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
-
-	// Place the file under its new name.
-	if (origGroup != NULL) {
-		return addLeafFile(newName, file);
-	}
-
-	return NO_ERROR;
-}
-
 status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
 {
     sp<AaptGroup> group;
@@ -1710,17 +1793,17 @@
     return NO_ERROR;
 }
 
-void AaptDir::print() const
+void AaptDir::print(const String8& prefix) const
 {
     const size_t ND=getDirs().size();
     size_t i;
     for (i=0; i<ND; i++) {
-        getDirs().valueAt(i)->print();
+        getDirs().valueAt(i)->print(prefix);
     }
 
     const size_t NF=getFiles().size();
     for (i=0; i<NF; i++) {
-        getFiles().valueAt(i)->print();
+        getFiles().valueAt(i)->print(prefix);
     }
 }
 
@@ -1744,6 +1827,24 @@
 // =========================================================================
 // =========================================================================
 
+AaptAssets::AaptAssets()
+    : AaptDir(String8(), String8()),
+      mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
+{
+}
+
+const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
+    if (mChanged) {
+    }
+    return mGroupEntries;
+}
+
+status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
+{
+    mChanged = true;
+    return AaptDir::addFile(name, file);
+}
+
 sp<AaptFile> AaptAssets::addFile(
         const String8& filePath, const AaptGroupEntry& entry,
         const String8& srcDir, sp<AaptGroup>* outGroup,
@@ -1945,6 +2046,11 @@
         goto bail;
     }
 
+    count = filter(bundle);
+    if (count != NO_ERROR) {
+        totalCount = count;
+        goto bail;
+    }
 
 bail:
     return totalCount;
@@ -2002,9 +2108,9 @@
             continue;
         }
 
-        if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
+        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
             int maxResInt = atoi(bundle->getMaxResVersion());
-            const char *verString = group.version.string();
+            const char *verString = group.getVersionString().string();
             int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
             if (dirVersionInt > maxResInt) {
               fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
@@ -2015,7 +2121,7 @@
         FileType type = getFileType(subdirName.string());
 
         if (type == kFileTypeDirectory) {
-            sp<AaptDir> dir = makeDir(String8(entry->d_name));
+            sp<AaptDir> dir = makeDir(resType);
             ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
                                                 resType, mFullResPaths);
             if (res < 0) {
@@ -2027,7 +2133,13 @@
                 count += res;
             }
 
-            mDirs.add(dir);
+            // Only add this directory if we don't already have a resource dir
+            // for the current type.  This ensures that we only add the dir once
+            // for all configs.
+            sp<AaptDir> rdir = resDir(resType);
+            if (rdir == NULL) {
+                mResDirs.add(dir);
+            }
         } else {
             if (bundle->getVerbose()) {
                 fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
@@ -2136,6 +2248,142 @@
     return count;
 }
 
+status_t AaptAssets::filter(Bundle* bundle)
+{
+    ResourceFilter reqFilter;
+    status_t err = reqFilter.parse(bundle->getConfigurations());
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    ResourceFilter prefFilter;
+    err = prefFilter.parse(bundle->getPreferredConfigurations());
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
+        return NO_ERROR;
+    }
+
+    if (true || bundle->getVerbose()) {
+        if (!reqFilter.isEmpty()) {
+            printf("Applying required filter: %s\n",
+                    bundle->getConfigurations());
+        }
+        if (!prefFilter.isEmpty()) {
+            printf("Applying preferred filter: %s\n",
+                    bundle->getPreferredConfigurations());
+        }
+    }
+
+    const Vector<sp<AaptDir> >& resdirs = mResDirs;
+    const size_t ND = resdirs.size();
+    for (size_t i=0; i<ND; i++) {
+        const sp<AaptDir>& dir = resdirs.itemAt(i);
+        if (dir->getLeaf() == kValuesDir) {
+            // The "value" dir is special since a single file defines
+            // multiple resources, so we can not do filtering on the
+            // files themselves.
+            continue;
+        }
+        if (dir->getLeaf() == kMipmapDir) {
+            // We also skip the "mipmap" directory, since the point of this
+            // is to include all densities without stripping.  If you put
+            // other configurations in here as well they won't be stripped
+            // either...  So don't do that.  Seriously.  What is wrong with you?
+            continue;
+        }
+
+        const size_t NG = dir->getFiles().size();
+        for (size_t j=0; j<NG; j++) {
+            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
+
+            // First remove any configurations we know we don't need.
+            for (size_t k=0; k<grp->getFiles().size(); k++) {
+                sp<AaptFile> file = grp->getFiles().valueAt(k);
+                if (k == 0 && grp->getFiles().size() == 1) {
+                    // If this is the only file left, we need to keep it.
+                    // Otherwise the resource IDs we are using will be inconsistent
+                    // with what we get when not stripping.  Sucky, but at least
+                    // for now we can rely on the back-end doing another filtering
+                    // pass to take this out and leave us with this resource name
+                    // containing no entries.
+                    continue;
+                }
+                if (file->getPath().getPathExtension() == ".xml") {
+                    // We can't remove .xml files at this point, because when
+                    // we parse them they may add identifier resources, so
+                    // removing them can cause our resource identifiers to
+                    // become inconsistent.
+                    continue;
+                }
+                const ResTable_config& config(file->getGroupEntry().toParams());
+                if (!reqFilter.match(config)) {
+                    if (bundle->getVerbose()) {
+                        printf("Pruning unneeded resource: %s\n",
+                                file->getPrintableSource().string());
+                    }
+                    grp->removeFile(k);
+                    k--;
+                }
+            }
+
+            // Quick check: no preferred filters, nothing more to do.
+            if (prefFilter.isEmpty()) {
+                continue;
+            }
+
+            // Now deal with preferred configurations.
+            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
+                for (size_t k=0; k<grp->getFiles().size(); k++) {
+                    sp<AaptFile> file = grp->getFiles().valueAt(k);
+                    if (k == 0 && grp->getFiles().size() == 1) {
+                        // If this is the only file left, we need to keep it.
+                        // Otherwise the resource IDs we are using will be inconsistent
+                        // with what we get when not stripping.  Sucky, but at least
+                        // for now we can rely on the back-end doing another filtering
+                        // pass to take this out and leave us with this resource name
+                        // containing no entries.
+                        continue;
+                    }
+                    if (file->getPath().getPathExtension() == ".xml") {
+                        // We can't remove .xml files at this point, because when
+                        // we parse them they may add identifier resources, so
+                        // removing them can cause our resource identifiers to
+                        // become inconsistent.
+                        continue;
+                    }
+                    const ResTable_config& config(file->getGroupEntry().toParams());
+                    if (!prefFilter.match(axis, config)) {
+                        // This is a resource we would prefer not to have.  Check
+                        // to see if have a similar variation that we would like
+                        // to have and, if so, we can drop it.
+                        for (size_t m=0; m<grp->getFiles().size(); m++) {
+                            if (m == k) continue;
+                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
+                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
+                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
+                                if (prefFilter.match(axis, mconfig)) {
+                                    if (bundle->getVerbose()) {
+                                        printf("Pruning unneeded resource: %s\n",
+                                                file->getPrintableSource().string());
+                                    }
+                                    grp->removeFile(k);
+                                    k--;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
 sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
 {
     sp<AaptSymbols> sym = mSymbols.valueFor(name);
@@ -2179,26 +2427,39 @@
     return mIncludedAssets.getResources(false);
 }
 
-void AaptAssets::print() const
+void AaptAssets::print(const String8& prefix) const
 {
-    printf("Locale/Vendor pairs:\n");
+    String8 innerPrefix(prefix);
+    innerPrefix.append("  ");
+    String8 innerInnerPrefix(innerPrefix);
+    innerInnerPrefix.append("  ");
+    printf("%sConfigurations:\n", prefix.string());
     const size_t N=mGroupEntries.size();
     for (size_t i=0; i<N; i++) {
-        printf("   %s/%s\n",
-               mGroupEntries.itemAt(i).locale.string(),
-               mGroupEntries.itemAt(i).vendor.string());
+        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
+        printf("%s %s\n", prefix.string(),
+                cname != "" ? cname.string() : "(default)");
     }
 
-    printf("\nFiles:\n");
-    AaptDir::print();
+    printf("\n%sFiles:\n", prefix.string());
+    AaptDir::print(innerPrefix);
+
+    printf("\n%sResource Dirs:\n", prefix.string());
+    const Vector<sp<AaptDir> >& resdirs = mResDirs;
+    const size_t NR = resdirs.size();
+    for (size_t i=0; i<NR; i++) {
+        const sp<AaptDir>& d = resdirs.itemAt(i);
+        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
+        d->print(innerInnerPrefix);
+    }
 }
 
-sp<AaptDir> AaptAssets::resDir(const String8& name)
+sp<AaptDir> AaptAssets::resDir(const String8& name) const
 {
-    const Vector<sp<AaptDir> >& dirs = mDirs;
-    const size_t N = dirs.size();
+    const Vector<sp<AaptDir> >& resdirs = mResDirs;
+    const size_t N = resdirs.size();
     for (size_t i=0; i<N; i++) {
-        const sp<AaptDir>& d = dirs.itemAt(i);
+        const sp<AaptDir>& d = resdirs.itemAt(i);
         if (d->getLeaf() == name) {
             return d;
         }
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 82dfd71..d5345b2 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -24,6 +24,8 @@
 
 bool valid_symbol_name(const String8& str);
 
+class AaptAssets;
+
 enum {
     AXIS_NONE = 0,
     AXIS_MCC = 1,
@@ -45,7 +47,10 @@
     AXIS_SMALLESTSCREENWIDTHDP,
     AXIS_SCREENWIDTHDP,
     AXIS_SCREENHEIGHTDP,
-    AXIS_VERSION
+    AXIS_VERSION,
+
+    AXIS_START = AXIS_MCC,
+    AXIS_END = AXIS_VERSION,
 };
 
 enum {
@@ -56,6 +61,7 @@
     SDK_MR1 = 7,
     SDK_FROYO = 8,
     SDK_HONEYCOMB_MR2 = 13,
+    SDK_ICE_CREAM_SANDWICH = 14,
 };
 
 /**
@@ -65,35 +71,19 @@
 struct AaptGroupEntry
 {
 public:
-    AaptGroupEntry() { }
+    AaptGroupEntry() : mParamsChanged(true) { }
     AaptGroupEntry(const String8& _locale, const String8& _vendor)
-        : locale(_locale), vendor(_vendor) { }
-
-    String8 mcc;
-    String8 mnc;
-    String8 locale;
-    String8 vendor;
-    String8 smallestScreenWidthDp;
-    String8 screenWidthDp;
-    String8 screenHeightDp;
-    String8 screenLayoutSize;
-    String8 screenLayoutLong;
-    String8 orientation;
-    String8 uiModeType;
-    String8 uiModeNight;
-    String8 density;
-    String8 touchscreen;
-    String8 keysHidden;
-    String8 keyboard;
-    String8 navHidden;
-    String8 navigation;
-    String8 screenSize;
-    String8 version;
+        : locale(_locale), vendor(_vendor), mParamsChanged(true) { }
 
     bool initFromDirName(const char* dir, String8* resType);
 
     static status_t parseNamePart(const String8& part, int* axis, uint32_t* value);
-    
+
+    static uint32_t getConfigValueForAxis(const ResTable_config& config, int axis);
+
+    static bool configSameExcept(const ResTable_config& config,
+            const ResTable_config& otherConfig, int axis);
+
     static bool getMccName(const char* name, ResTable_config* out = NULL);
     static bool getMncName(const char* name, ResTable_config* out = NULL);
     static bool getLocaleName(const char* name, ResTable_config* out = NULL);
@@ -116,7 +106,7 @@
 
     int compare(const AaptGroupEntry& o) const;
 
-    ResTable_config toParams() const;
+    const ResTable_config& toParams() const;
 
     inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
     inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
@@ -127,6 +117,33 @@
 
     String8 toString() const;
     String8 toDirName(const String8& resType) const;
+
+    const String8& getVersionString() const { return version; }
+
+private:
+    String8 mcc;
+    String8 mnc;
+    String8 locale;
+    String8 vendor;
+    String8 smallestScreenWidthDp;
+    String8 screenWidthDp;
+    String8 screenHeightDp;
+    String8 screenLayoutSize;
+    String8 screenLayoutLong;
+    String8 orientation;
+    String8 uiModeType;
+    String8 uiModeNight;
+    String8 density;
+    String8 touchscreen;
+    String8 keysHidden;
+    String8 keyboard;
+    String8 navHidden;
+    String8 navigation;
+    String8 screenSize;
+    String8 version;
+
+    mutable bool mParamsChanged;
+    mutable ResTable_config mParams;
 };
 
 inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
@@ -225,7 +242,7 @@
     status_t addFile(const sp<AaptFile>& file);
     void removeFile(size_t index);
 
-    void print() const;
+    void print(const String8& prefix) const;
 
     String8 getPrintableSource() const;
 
@@ -237,7 +254,7 @@
 };
 
 /**
- * A single directory of assets, which can contain for files and other
+ * A single directory of assets, which can contain files and other
  * sub-directories.
  */
 class AaptDir : public RefBase
@@ -254,25 +271,11 @@
     const DefaultKeyedVector<String8, sp<AaptGroup> >& getFiles() const { return mFiles; }
     const DefaultKeyedVector<String8, sp<AaptDir> >& getDirs() const { return mDirs; }
 
-    status_t addFile(const String8& name, const sp<AaptGroup>& file);
-    status_t addDir(const String8& name, const sp<AaptDir>& dir);
-
-    sp<AaptDir> makeDir(const String8& name);
+    virtual status_t addFile(const String8& name, const sp<AaptGroup>& file);
 
     void removeFile(const String8& name);
     void removeDir(const String8& name);
 
-    status_t renameFile(const sp<AaptFile>& file, const String8& newName);
-
-    status_t addLeafFile(const String8& leafName,
-                         const sp<AaptFile>& file);
-
-    virtual ssize_t slurpFullTree(Bundle* bundle,
-                                  const String8& srcDir,
-                                  const AaptGroupEntry& kind,
-                                  const String8& resType,
-                                  sp<FilePathStore>& fullResPaths);
-
     /*
      * Perform some sanity checks on the names of files and directories here.
      * In particular:
@@ -292,11 +295,23 @@
      */
     status_t validate() const;
 
-    void print() const;
+    void print(const String8& prefix) const;
 
     String8 getPrintableSource() const;
 
 private:
+    friend class AaptAssets;
+
+    status_t addDir(const String8& name, const sp<AaptDir>& dir);
+    sp<AaptDir> makeDir(const String8& name);
+    status_t addLeafFile(const String8& leafName,
+                         const sp<AaptFile>& file);
+    virtual ssize_t slurpFullTree(Bundle* bundle,
+                                  const String8& srcDir,
+                                  const AaptGroupEntry& kind,
+                                  const String8& resType,
+                                  sp<FilePathStore>& fullResPaths);
+
     String8 mLeaf;
     String8 mPath;
 
@@ -501,13 +516,15 @@
 class AaptAssets : public AaptDir
 {
 public:
-    AaptAssets() : AaptDir(String8(), String8()), mHaveIncludedAssets(false), mRes(NULL) { }
+    AaptAssets();
     virtual ~AaptAssets() { delete mRes; }
 
     const String8& getPackage() const { return mPackage; }
     void setPackage(const String8& package) { mPackage = package; mSymbolsPrivatePackage = package; }
 
-    const SortedVector<AaptGroupEntry>& getGroupEntries() const { return mGroupEntries; }
+    const SortedVector<AaptGroupEntry>& getGroupEntries() const;
+
+    virtual status_t addFile(const String8& name, const sp<AaptGroup>& file);
 
     sp<AaptFile> addFile(const String8& filePath,
                          const AaptGroupEntry& entry,
@@ -524,15 +541,6 @@
     
     ssize_t slurpFromArgs(Bundle* bundle);
 
-    virtual ssize_t slurpFullTree(Bundle* bundle,
-                                  const String8& srcDir,
-                                  const AaptGroupEntry& kind,
-                                  const String8& resType,
-                                  sp<FilePathStore>& fullResPaths);
-
-    ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
-    ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
-
     sp<AaptSymbols> getSymbolsFor(const String8& name);
 
     const DefaultKeyedVector<String8, sp<AaptSymbols> >& getSymbols() const { return mSymbols; }
@@ -544,10 +552,10 @@
     status_t addIncludedResources(const sp<AaptFile>& file);
     const ResTable& getIncludedResources() const;
 
-    void print() const;
+    void print(const String8& prefix) const;
 
-    inline const Vector<sp<AaptDir> >& resDirs() { return mDirs; }
-    sp<AaptDir> resDir(const String8& name);
+    inline const Vector<sp<AaptDir> >& resDirs() const { return mResDirs; }
+    sp<AaptDir> resDir(const String8& name) const;
 
     inline sp<AaptAssets> getOverlay() { return mOverlay; }
     inline void setOverlay(sp<AaptAssets>& overlay) { mOverlay = overlay; }
@@ -565,12 +573,25 @@
         setFullAssetPaths(sp<FilePathStore>& res) { mFullAssetPaths = res; }
 
 private:
+    virtual ssize_t slurpFullTree(Bundle* bundle,
+                                  const String8& srcDir,
+                                  const AaptGroupEntry& kind,
+                                  const String8& resType,
+                                  sp<FilePathStore>& fullResPaths);
+
+    ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
+    ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
+
+    status_t filter(Bundle* bundle);
+
     String8 mPackage;
     SortedVector<AaptGroupEntry> mGroupEntries;
     DefaultKeyedVector<String8, sp<AaptSymbols> > mSymbols;
     String8 mSymbolsPrivatePackage;
 
-    Vector<sp<AaptDir> > mDirs;
+    Vector<sp<AaptDir> > mResDirs;
+
+    bool mChanged;
 
     bool mHaveIncludedAssets;
     AssetManager mIncludedAssets;
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index e507fb9..a3e5d9a 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -19,6 +19,7 @@
 	Package.cpp \
 	StringPool.cpp \
 	XMLNode.cpp \
+	ResourceFilter.cpp \
 	ResourceTable.cpp \
 	Images.cpp \
 	Resource.cpp \
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 539c312..2d1060b 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -122,6 +122,8 @@
     void setRClassDir(const char* dir) { mRClassDir = dir; }
     const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; }
     void addConfigurations(const char* val) { if (mConfigurations.size() > 0) { mConfigurations.append(","); mConfigurations.append(val); } else { mConfigurations = val; } }
+    const char* getPreferredConfigurations() const { return mPreferredConfigurations.size() > 0 ? mPreferredConfigurations.string() : NULL; }
+    void addPreferredConfigurations(const char* val) { if (mPreferredConfigurations.size() > 0) { mPreferredConfigurations.append(","); mPreferredConfigurations.append(val); } else { mPreferredConfigurations = val; } }
     const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; }
     void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; }
     const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; }
@@ -244,6 +246,7 @@
     const char* mRClassDir;
     const char* mResourceIntermediatesDir;
     android::String8 mConfigurations;
+    android::String8 mPreferredConfigurations;
     android::Vector<const char*> mPackageIncludes;
     android::Vector<const char*> mJarFiles;
     android::Vector<const char*> mNoCompressExtensions;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 413a2dc..637c27d 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -5,6 +5,7 @@
 //
 #include "Main.h"
 #include "Bundle.h"
+#include "ResourceFilter.h"
 #include "ResourceTable.h"
 #include "XMLNode.h"
 
@@ -1572,7 +1573,7 @@
     }
 
     if (bundle->getVerbose()) {
-        assets->print();
+        assets->print(String8());
     }
 
     // If they asked for any fileAs that need to be compiled, do so.
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 311ceea..ffbe875 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -980,6 +980,10 @@
 
     String8 printableName(file->getPrintableSource());
 
+    if (bundle->getVerbose()) {
+        printf("Processing image: %s\n", printableName.string());
+    }
+
     png_structp read_ptr = NULL;
     png_infop read_info = NULL;
     FILE* fp;
@@ -1094,6 +1098,10 @@
 
     status_t error = UNKNOWN_ERROR;
 
+    if (bundle->getVerbose()) {
+        printf("Processing image to cache: %s => %s\n", source.string(), dest.string());
+    }
+
     // Get a file handler to read from
     fp = fopen(source.string(),"rb");
     if (fp == NULL) {
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 5135787..50c828d 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -65,9 +65,10 @@
         "        [--max-res-version VAL] \\\n"
         "        [-I base-package [-I base-package ...]] \\\n"
         "        [-A asset-source-dir]  [-G class-list-file] [-P public-definitions-file] \\\n"
-        "        [-S resource-sources [-S resource-sources ...]] "
+        "        [-S resource-sources [-S resource-sources ...]] \\\n"
         "        [-F apk-file] [-J R-file-dir] \\\n"
         "        [--product product1,product2,...] \\\n"
+        "        [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
         "        [-o] \\\n"
         "        [raw-files-dir [raw-files-dir] ...]\n"
         "\n"
@@ -154,6 +155,10 @@
         "       generate dependency files in the same directories for R.java and resource package\n"
         "   --auto-add-overlay\n"
         "       Automatically add resources that are only in overlays.\n"
+        "   --preferred-configurations\n"
+        "       Like the -c option for filtering out unneeded configurations, but\n"
+        "       only expresses a preference.  If there is no resource available with\n"
+        "       the preferred configuration then it will not be stripped.\n"
         "   --rename-manifest-package\n"
         "       Rewrite the manifest so that its package name is the package name\n"
         "       given here.  Relative class names (for example .Foo) will be\n"
@@ -509,6 +514,15 @@
                     bundle.setGenDependencies(true);
                 } else if (strcmp(cp, "-utf16") == 0) {
                     bundle.setWantUTF16(true);
+                } else if (strcmp(cp, "-preferred-configurations") == 0) {
+                    argc--;
+                    argv++;
+                    if (!argc) {
+                        fprintf(stderr, "ERROR: No argument supplied for '--preferred-configurations' option\n");
+                        wantUsage = true;
+                        goto bail;
+                    }
+                    bundle.addPreferredConfigurations(argv[0]);
                 } else if (strcmp(cp, "-rename-manifest-package") == 0) {
                     argc--;
                     argv++;
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 1e3efde..3930117 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -6,6 +6,7 @@
 #include "Main.h"
 #include "AaptAssets.h"
 #include "ResourceTable.h"
+#include "ResourceFilter.h"
 
 #include <utils/Log.h>
 #include <utils/threads.h>
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 2e796a2..887fa74 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -352,18 +352,27 @@
 
         if (index < 0) {
             sp<ResourceTypeSet> set = new ResourceTypeSet();
+            NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n",
+                    leafName.string(), group->getPath().string(), group.get()));
             set->add(leafName, group);
             resources->add(resType, set);
         } else {
             sp<ResourceTypeSet> set = resources->valueAt(index);
             index = set->indexOfKey(leafName);
             if (index < 0) {
+                NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n",
+                        leafName.string(), group->getPath().string(), group.get()));
                 set->add(leafName, group);
             } else {
                 sp<AaptGroup> existingGroup = set->valueAt(index);
-                int M = files.size();
-                for (int j=0; j<M; j++) {
-                    existingGroup->addFile(files.valueAt(j));
+                NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n",
+                        leafName.string(), group->getPath().string(), group.get()));
+                for (size_t j=0; j<files.size(); j++) {
+                    NOISY(printf("Adding file %s in group %s resType %s\n",
+                        files.valueAt(j)->getSourceFile().string(),
+                        files.keyAt(j).toDirName(String8()).string(),
+                        resType.string()));
+                    status_t err = existingGroup->addFile(files.valueAt(j));
                 }
             }
         }
@@ -378,9 +387,12 @@
 
     for (int i=0; i<N; i++) {
         sp<AaptDir> d = dirs.itemAt(i);
+        NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
+                d->getLeaf().string()));
         collect_files(d, resources);
 
         // don't try to include the res dir
+        NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string()));
         ass->removeDir(d->getLeaf());
     }
 }
@@ -570,7 +582,7 @@
                         size_t baseFileIndex =
                                 baseGroup->getFiles().indexOfKey(overlayFiles.
                                 keyAt(overlayGroupIndex));
-                        if(baseFileIndex < UNKNOWN_ERROR) {
+                        if (baseFileIndex < UNKNOWN_ERROR) {
                             if (bundle->getVerbose()) {
                                 printf("found a match (%zd) for overlay file %s, for flavor %s\n",
                                         baseFileIndex,
@@ -580,6 +592,11 @@
                             baseGroup->removeFile(baseFileIndex);
                         } else {
                             // didn't find a match fall through and add it..
+                            if (true || bundle->getVerbose()) {
+                                printf("nothing matches overlay file %s, for flavor %s\n",
+                                        overlayGroup->getLeaf().string(),
+                                        overlayFiles.keyAt(overlayGroupIndex).toString().string());
+                            }
                         }
                         baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
new file mode 100644
index 0000000..8cfd2a5
--- /dev/null
+++ b/tools/aapt/ResourceFilter.cpp
@@ -0,0 +1,112 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#include "ResourceFilter.h"
+
+status_t
+ResourceFilter::parse(const char* arg)
+{
+    if (arg == NULL) {
+        return 0;
+    }
+
+    const char* p = arg;
+    const char* q;
+
+    while (true) {
+        q = strchr(p, ',');
+        if (q == NULL) {
+            q = p + strlen(p);
+        }
+
+        String8 part(p, q-p);
+
+        if (part == "zz_ZZ") {
+            mContainsPseudo = true;
+        }
+        int axis;
+        uint32_t value;
+        if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
+            fprintf(stderr, "Invalid configuration: %s\n", arg);
+            fprintf(stderr, "                       ");
+            for (int i=0; i<p-arg; i++) {
+                fprintf(stderr, " ");
+            }
+            for (int i=0; i<q-p; i++) {
+                fprintf(stderr, "^");
+            }
+            fprintf(stderr, "\n");
+            return 1;
+        }
+
+        ssize_t index = mData.indexOfKey(axis);
+        if (index < 0) {
+            mData.add(axis, SortedVector<uint32_t>());
+        }
+        SortedVector<uint32_t>& sv = mData.editValueFor(axis);
+        sv.add(value);
+        // if it's a locale with a region, also match an unmodified locale of the
+        // same language
+        if (axis == AXIS_LANGUAGE) {
+            if (value & 0xffff0000) {
+                sv.add(value & 0x0000ffff);
+            }
+        }
+        p = q;
+        if (!*p) break;
+        p++;
+    }
+
+    return NO_ERROR;
+}
+
+bool
+ResourceFilter::isEmpty() const
+{
+    return mData.size() == 0;
+}
+
+bool
+ResourceFilter::match(int axis, uint32_t value) const
+{
+    if (value == 0) {
+        // they didn't specify anything so take everything
+        return true;
+    }
+    ssize_t index = mData.indexOfKey(axis);
+    if (index < 0) {
+        // we didn't request anything on this axis so take everything
+        return true;
+    }
+    const SortedVector<uint32_t>& sv = mData.valueAt(index);
+    return sv.indexOf(value) >= 0;
+}
+
+bool
+ResourceFilter::match(int axis, const ResTable_config& config) const
+{
+    return match(axis, AaptGroupEntry::getConfigValueForAxis(config, axis));
+}
+
+bool
+ResourceFilter::match(const ResTable_config& config) const
+{
+    for (int i=AXIS_START; i<=AXIS_END; i++) {
+        if (!match(i, AaptGroupEntry::getConfigValueForAxis(config, i))) {
+            return false;
+        }
+    }
+    return true;
+}
+
+const SortedVector<uint32_t>* ResourceFilter::configsForAxis(int axis) const
+{
+    ssize_t index = mData.indexOfKey(axis);
+    if (index < 0) {
+        return NULL;
+    }
+    return &mData.valueAt(index);
+}
diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h
new file mode 100644
index 0000000..647b7bb
--- /dev/null
+++ b/tools/aapt/ResourceFilter.h
@@ -0,0 +1,33 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#ifndef RESOURCE_FILTER_H
+#define RESOURCE_FILTER_H
+
+#include "AaptAssets.h"
+
+/**
+ * Implements logic for parsing and handling "-c" and "--preferred-configurations"
+ * options.
+ */
+class ResourceFilter
+{
+public:
+    ResourceFilter() : mData(), mContainsPseudo(false) {}
+    status_t parse(const char* arg);
+    bool isEmpty() const;
+    bool match(int axis, uint32_t value) const;
+    bool match(int axis, const ResTable_config& config) const;
+    bool match(const ResTable_config& config) const;
+    const SortedVector<uint32_t>* configsForAxis(int axis) const;
+    inline bool containsPseudo() const { return mContainsPseudo; }
+
+private:
+    KeyedVector<int,SortedVector<uint32_t> > mData;
+    bool mContainsPseudo;
+};
+
+#endif
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 99f74c6..fdb39ca 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -7,6 +7,7 @@
 #include "ResourceTable.h"
 
 #include "XMLNode.h"
+#include "ResourceFilter.h"
 
 #include <utils/ByteOrder.h>
 #include <utils/ResourceTypes.h>
@@ -2528,135 +2529,6 @@
     return err;
 }
 
-
-status_t
-ResourceFilter::parse(const char* arg)
-{
-    if (arg == NULL) {
-        return 0;
-    }
-
-    const char* p = arg;
-    const char* q;
-
-    while (true) {
-        q = strchr(p, ',');
-        if (q == NULL) {
-            q = p + strlen(p);
-        }
-
-        String8 part(p, q-p);
-
-        if (part == "zz_ZZ") {
-            mContainsPseudo = true;
-        }
-        int axis;
-        uint32_t value;
-        if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
-            fprintf(stderr, "Invalid configuration: %s\n", arg);
-            fprintf(stderr, "                       ");
-            for (int i=0; i<p-arg; i++) {
-                fprintf(stderr, " ");
-            }
-            for (int i=0; i<q-p; i++) {
-                fprintf(stderr, "^");
-            }
-            fprintf(stderr, "\n");
-            return 1;
-        }
-
-        ssize_t index = mData.indexOfKey(axis);
-        if (index < 0) {
-            mData.add(axis, SortedVector<uint32_t>());
-        }
-        SortedVector<uint32_t>& sv = mData.editValueFor(axis);
-        sv.add(value);
-        // if it's a locale with a region, also match an unmodified locale of the
-        // same language
-        if (axis == AXIS_LANGUAGE) {
-            if (value & 0xffff0000) {
-                sv.add(value & 0x0000ffff);
-            }
-        }
-        p = q;
-        if (!*p) break;
-        p++;
-    }
-
-    return NO_ERROR;
-}
-
-bool
-ResourceFilter::match(int axis, uint32_t value) const
-{
-    if (value == 0) {
-        // they didn't specify anything so take everything
-        return true;
-    }
-    ssize_t index = mData.indexOfKey(axis);
-    if (index < 0) {
-        // we didn't request anything on this axis so take everything
-        return true;
-    }
-    const SortedVector<uint32_t>& sv = mData.valueAt(index);
-    return sv.indexOf(value) >= 0;
-}
-
-bool
-ResourceFilter::match(const ResTable_config& config) const
-{
-    if (config.locale) {
-        uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
-                | (config.language[1] << 8) | (config.language[0]);
-        if (!match(AXIS_LANGUAGE, locale)) {
-            return false;
-        }
-    }
-    if (!match(AXIS_ORIENTATION, config.orientation)) {
-        return false;
-    }
-    if (!match(AXIS_UIMODETYPE, (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE))) {
-        return false;
-    }
-    if (!match(AXIS_UIMODENIGHT, (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT))) {
-        return false;
-    }
-    if (!match(AXIS_DENSITY, config.density)) {
-        return false;
-    }
-    if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) {
-        return false;
-    }
-    if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) {
-        return false;
-    }
-    if (!match(AXIS_KEYBOARD, config.keyboard)) {
-        return false;
-    }
-    if (!match(AXIS_NAVIGATION, config.navigation)) {
-        return false;
-    }
-    if (!match(AXIS_SCREENSIZE, config.screenSize)) {
-        return false;
-    }
-    if (!match(AXIS_SMALLESTSCREENWIDTHDP, config.smallestScreenWidthDp)) {
-        return false;
-    }
-    if (!match(AXIS_SCREENWIDTHDP, config.screenWidthDp)) {
-        return false;
-    }
-    if (!match(AXIS_SCREENHEIGHTDP, config.screenHeightDp)) {
-        return false;
-    }
-    if (!match(AXIS_SCREENLAYOUTSIZE, config.screenLayout&ResTable_config::MASK_SCREENSIZE)) {
-        return false;
-    }
-    if (!match(AXIS_VERSION, config.version)) {
-        return false;
-    }
-    return true;
-}
-
 status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
 {
     ResourceFilter filter;
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index 80f2192..8123bb3 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -549,19 +549,4 @@
     map<String16, set<String8> > mLocalizations;
 };
 
-class ResourceFilter
-{
-public:
-    ResourceFilter() : mData(), mContainsPseudo(false) {}
-    status_t parse(const char* arg);
-    bool match(int axis, uint32_t value) const;
-    bool match(const ResTable_config& config) const;
-    inline bool containsPseudo() const { return mContainsPseudo; }
-
-private:
-    KeyedVector<int,SortedVector<uint32_t> > mData;
-    bool mContainsPseudo;
-};
-
-
 #endif