aapt: add option for stricter symbol generation

Adds an option that prevents generating java symbols for string
resources that don't have a default localization.

Bug: 21537397
Change-Id: Ifafa942b24d5cdbed93651cde363e859be13d395
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 3dae917..cbe7c5d 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -61,6 +61,7 @@
           mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
           mVersionCode(NULL), mVersionName(NULL), mReplaceVersion(false), mCustomPackage(NULL),
           mExtraPackages(NULL), mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false),
+          mSkipSymbolsWithoutDefaultLocalization(false),
           mProduct(NULL), mUseCrunchCache(false), mErrorOnFailedInsert(false),
           mErrorOnMissingConfigEntry(false), mOutputTextSymbols(NULL),
           mSingleCrunchInputFile(NULL), mSingleCrunchOutputFile(NULL),
@@ -191,6 +192,8 @@
     void setDebugMode(bool val) { mDebugMode = val; }
     bool getNonConstantId() const { return mNonConstantId; }
     void setNonConstantId(bool val) { mNonConstantId = val; }
+    bool getSkipSymbolsWithoutDefaultLocalization() const { return mSkipSymbolsWithoutDefaultLocalization; }
+    void setSkipSymbolsWithoutDefaultLocalization(bool val) { mSkipSymbolsWithoutDefaultLocalization = val; }
     const char* getProduct() const { return mProduct; }
     void setProduct(const char * val) { mProduct = val; }
     void setUseCrunchCache(bool val) { mUseCrunchCache = val; }
@@ -315,6 +318,7 @@
     const char* mMaxResVersion;
     bool        mDebugMode;
     bool        mNonConstantId;
+    bool        mSkipSymbolsWithoutDefaultLocalization;
     const char* mProduct;
     bool        mUseCrunchCache;
     bool        mErrorOnFailedInsert;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 7dee585..f832c60 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -212,6 +212,9 @@
         "   --ignore-assets\n"
         "       Assets to be ignored. Default pattern is:\n"
         "       %s\n"
+        "   --skip-symbols-without-default-localization\n"
+        "       Prevents symbols from being generated for strings that do not have a default\n"
+        "       localization\n"
         "   --no-version-vectors\n"
         "       Do not automatically generate versioned copies of vector XML resources.\n",
         gDefaultIgnoreAssets);
@@ -659,6 +662,8 @@
                     bundle.setProduct(argv[0]);
                 } else if (strcmp(cp, "-non-constant-id") == 0) {
                     bundle.setNonConstantId(true);
+                } else if (strcmp(cp, "-skip-symbols-without-default-localization") == 0) {
+                    bundle.setSkipSymbolsWithoutDefaultLocalization(true);
                 } else if (strcmp(cp, "-shared-lib") == 0) {
                     bundle.setNonConstantId(true);
                     bundle.setBuildSharedLibrary(true);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index beb94fd..5d20815 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1604,7 +1604,7 @@
     
     if (table.hasResources()) {
         sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
-        err = table.addSymbols(symbols);
+        err = table.addSymbols(symbols, bundle->getSkipSymbolsWithoutDefaultLocalization());
         if (err < NO_ERROR) {
             return err;
         }
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 3b146da..e64fdf7 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -913,6 +913,7 @@
         if (code == ResXMLTree::START_TAG) {
             const String16* curTag = NULL;
             String16 curType;
+            String16 curName;
             int32_t curFormat = ResTable_map::TYPE_ANY;
             bool curIsBag = false;
             bool curIsBagReplaceOnOverwrite = false;
@@ -1321,6 +1322,10 @@
                 ssize_t attri = block.indexOfAttribute(NULL, "type");
                 if (attri >= 0) {
                     curType = String16(block.getAttributeStringValue(attri, &len));
+                    ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
+                    if (nameIdx >= 0) {
+                        curName = String16(block.getAttributeStringValue(nameIdx, &len));
+                    }
                     ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
                     if (formatIdx >= 0) {
                         String16 formatStr = String16(block.getAttributeStringValue(
@@ -1363,6 +1368,9 @@
                 }
                 
                 if (name.size() > 0) {
+                    if (locale.size() == 0) {
+                        outTable->addDefaultLocalization(name);
+                    }
                     if (translatable == false16) {
                         curIsFormatted = false;
                         // Untranslatable strings must only exist in the default [empty] locale
@@ -1658,6 +1666,9 @@
                     hasErrors = localHasErrors = true;
                 }
                 else if (err == NO_ERROR) {
+                    if (curType == string16 && !curParams.language[0] && !curParams.country[0]) {
+                        outTable->addDefaultLocalization(curName);
+                    }
                     if (curIsPseudolocalizable && localeIsDefined(curParams)
                             && bundle->getPseudolocalize() > 0) {
                         // pseudolocalize here
@@ -2622,8 +2633,11 @@
     return firstError;
 }
 
-status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
+status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols,
+        bool skipSymbolsWithoutDefaultLocalization) {
     const size_t N = mOrderedPackages.size();
+    const String8 defaultLocale;
+    const String16 stringType("string");
     size_t pi;
 
     for (pi=0; pi<N; pi++) {
@@ -2664,6 +2678,19 @@
                     return UNKNOWN_ERROR;
                 }
                 if (Res_GETPACKAGE(rid) + 1 == p->getAssignedId()) {
+
+                    if (skipSymbolsWithoutDefaultLocalization &&
+                            t->getName() == stringType) {
+
+                        // Don't generate symbols for strings without a default localization.
+                        if (mHasDefaultLocalization.find(c->getName())
+                                == mHasDefaultLocalization.end()) {
+                            // printf("Skip symbol [%08x] %s\n", rid,
+                            //          String8(c->getName()).string());
+                            continue;
+                        }
+                    }
+
                     typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
                     
                     String16 comment(c->getComment());
@@ -2686,6 +2713,12 @@
     mLocalizations[name][locale] = src;
 }
 
+void
+ResourceTable::addDefaultLocalization(const String16& name)
+{
+    mHasDefaultLocalization.insert(name);
+}
+
 
 /*!
  * Flag various sorts of localization problems.  '+' indicates checks already implemented;
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index 9644224..2c1bec1 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -235,8 +235,10 @@
                        const ConfigDescription* config = NULL);
 
     status_t assignResourceIds();
-    status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL);
+    status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL,
+                        bool skipSymbolsWithoutDefaultLocalization = false);
     void addLocalization(const String16& name, const String8& locale, const SourcePos& src);
+    void addDefaultLocalization(const String16& name);
     status_t validateLocalizations(void);
 
     status_t flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
@@ -588,6 +590,8 @@
 
     // key = string resource name, value = set of locales in which that name is defined
     std::map<String16, std::map<String8, SourcePos>> mLocalizations;
+    // set of string resources names that have a default localization
+    std::set<String16> mHasDefaultLocalization;
     std::queue<CompileResourceWorkItem> mWorkQueue;
 };