Home | History | Annotate | Download | only in aapt
      1 //
      2 // Copyright 2006 The Android Open Source Project
      3 //
      4 // Build resource files from raw assets.
      5 //
      6 #include "Main.h"
      7 #include "AaptAssets.h"
      8 #include "StringPool.h"
      9 #include "XMLNode.h"
     10 #include "ResourceTable.h"
     11 #include "Images.h"
     12 
     13 #include "CrunchCache.h"
     14 #include "FileFinder.h"
     15 #include "CacheUpdater.h"
     16 
     17 #include "WorkQueue.h"
     18 
     19 #if HAVE_PRINTF_ZD
     20 #  define ZD "%zd"
     21 #  define ZD_TYPE ssize_t
     22 #else
     23 #  define ZD "%ld"
     24 #  define ZD_TYPE long
     25 #endif
     26 
     27 #define NOISY(x) // x
     28 
     29 // Number of threads to use for preprocessing images.
     30 static const size_t MAX_THREADS = 4;
     31 
     32 // ==========================================================================
     33 // ==========================================================================
     34 // ==========================================================================
     35 
     36 class PackageInfo
     37 {
     38 public:
     39     PackageInfo()
     40     {
     41     }
     42     ~PackageInfo()
     43     {
     44     }
     45 
     46     status_t parsePackage(const sp<AaptGroup>& grp);
     47 };
     48 
     49 // ==========================================================================
     50 // ==========================================================================
     51 // ==========================================================================
     52 
     53 static String8 parseResourceName(const String8& leaf)
     54 {
     55     const char* firstDot = strchr(leaf.string(), '.');
     56     const char* str = leaf.string();
     57 
     58     if (firstDot) {
     59         return String8(str, firstDot-str);
     60     } else {
     61         return String8(str);
     62     }
     63 }
     64 
     65 ResourceTypeSet::ResourceTypeSet()
     66     :RefBase(),
     67      KeyedVector<String8,sp<AaptGroup> >()
     68 {
     69 }
     70 
     71 FilePathStore::FilePathStore()
     72     :RefBase(),
     73      Vector<String8>()
     74 {
     75 }
     76 
     77 class ResourceDirIterator
     78 {
     79 public:
     80     ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType)
     81         : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0)
     82     {
     83     }
     84 
     85     inline const sp<AaptGroup>& getGroup() const { return mGroup; }
     86     inline const sp<AaptFile>& getFile() const { return mFile; }
     87 
     88     inline const String8& getBaseName() const { return mBaseName; }
     89     inline const String8& getLeafName() const { return mLeafName; }
     90     inline String8 getPath() const { return mPath; }
     91     inline const ResTable_config& getParams() const { return mParams; }
     92 
     93     enum {
     94         EOD = 1
     95     };
     96 
     97     ssize_t next()
     98     {
     99         while (true) {
    100             sp<AaptGroup> group;
    101             sp<AaptFile> file;
    102 
    103             // Try to get next file in this current group.
    104             if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) {
    105                 group = mGroup;
    106                 file = group->getFiles().valueAt(mGroupPos++);
    107 
    108             // Try to get the next group/file in this directory
    109             } else if (mSetPos < mSet->size()) {
    110                 mGroup = group = mSet->valueAt(mSetPos++);
    111                 if (group->getFiles().size() < 1) {
    112                     continue;
    113                 }
    114                 file = group->getFiles().valueAt(0);
    115                 mGroupPos = 1;
    116 
    117             // All done!
    118             } else {
    119                 return EOD;
    120             }
    121 
    122             mFile = file;
    123 
    124             String8 leaf(group->getLeaf());
    125             mLeafName = String8(leaf);
    126             mParams = file->getGroupEntry().toParams();
    127             NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n",
    128                    group->getPath().string(), mParams.mcc, mParams.mnc,
    129                    mParams.language[0] ? mParams.language[0] : '-',
    130                    mParams.language[1] ? mParams.language[1] : '-',
    131                    mParams.country[0] ? mParams.country[0] : '-',
    132                    mParams.country[1] ? mParams.country[1] : '-',
    133                    mParams.orientation, mParams.uiMode,
    134                    mParams.density, mParams.touchscreen, mParams.keyboard,
    135                    mParams.inputFlags, mParams.navigation));
    136             mPath = "res";
    137             mPath.appendPath(file->getGroupEntry().toDirName(mResType));
    138             mPath.appendPath(leaf);
    139             mBaseName = parseResourceName(leaf);
    140             if (mBaseName == "") {
    141                 fprintf(stderr, "Error: malformed resource filename %s\n",
    142                         file->getPrintableSource().string());
    143                 return UNKNOWN_ERROR;
    144             }
    145 
    146             NOISY(printf("file name=%s\n", mBaseName.string()));
    147 
    148             return NO_ERROR;
    149         }
    150     }
    151 
    152 private:
    153     String8 mResType;
    154 
    155     const sp<ResourceTypeSet> mSet;
    156     size_t mSetPos;
    157 
    158     sp<AaptGroup> mGroup;
    159     size_t mGroupPos;
    160 
    161     sp<AaptFile> mFile;
    162     String8 mBaseName;
    163     String8 mLeafName;
    164     String8 mPath;
    165     ResTable_config mParams;
    166 };
    167 
    168 // ==========================================================================
    169 // ==========================================================================
    170 // ==========================================================================
    171 
    172 bool isValidResourceType(const String8& type)
    173 {
    174     return type == "anim" || type == "animator" || type == "interpolator"
    175         || type == "transition"
    176         || type == "drawable" || type == "layout"
    177         || type == "values" || type == "xml" || type == "raw"
    178         || type == "color" || type == "menu" || type == "mipmap";
    179 }
    180 
    181 static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
    182 {
    183     sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
    184     sp<AaptFile> file;
    185     if (group != NULL) {
    186         file = group->getFiles().valueFor(AaptGroupEntry());
    187         if (file != NULL) {
    188             return file;
    189         }
    190     }
    191 
    192     if (!makeIfNecessary) {
    193         return NULL;
    194     }
    195     return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
    196                             NULL, String8());
    197 }
    198 
    199 static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
    200     const sp<AaptGroup>& grp)
    201 {
    202     if (grp->getFiles().size() != 1) {
    203         fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
    204                 grp->getFiles().valueAt(0)->getPrintableSource().string());
    205     }
    206 
    207     sp<AaptFile> file = grp->getFiles().valueAt(0);
    208 
    209     ResXMLTree block;
    210     status_t err = parseXMLResource(file, &block);
    211     if (err != NO_ERROR) {
    212         return err;
    213     }
    214     //printXMLBlock(&block);
    215 
    216     ResXMLTree::event_code_t code;
    217     while ((code=block.next()) != ResXMLTree::START_TAG
    218            && code != ResXMLTree::END_DOCUMENT
    219            && code != ResXMLTree::BAD_DOCUMENT) {
    220     }
    221 
    222     size_t len;
    223     if (code != ResXMLTree::START_TAG) {
    224         fprintf(stderr, "%s:%d: No start tag found\n",
    225                 file->getPrintableSource().string(), block.getLineNumber());
    226         return UNKNOWN_ERROR;
    227     }
    228     if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) {
    229         fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n",
    230                 file->getPrintableSource().string(), block.getLineNumber(),
    231                 String8(block.getElementName(&len)).string());
    232         return UNKNOWN_ERROR;
    233     }
    234 
    235     ssize_t nameIndex = block.indexOfAttribute(NULL, "package");
    236     if (nameIndex < 0) {
    237         fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n",
    238                 file->getPrintableSource().string(), block.getLineNumber());
    239         return UNKNOWN_ERROR;
    240     }
    241 
    242     assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len)));
    243 
    244     String16 uses_sdk16("uses-sdk");
    245     while ((code=block.next()) != ResXMLTree::END_DOCUMENT
    246            && code != ResXMLTree::BAD_DOCUMENT) {
    247         if (code == ResXMLTree::START_TAG) {
    248             if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) {
    249                 ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
    250                                                              "minSdkVersion");
    251                 if (minSdkIndex >= 0) {
    252                     const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
    253                     const char* minSdk8 = strdup(String8(minSdk16).string());
    254                     bundle->setManifestMinSdkVersion(minSdk8);
    255                 }
    256             }
    257         }
    258     }
    259 
    260     return NO_ERROR;
    261 }
    262 
    263 // ==========================================================================
    264 // ==========================================================================
    265 // ==========================================================================
    266 
    267 static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,
    268                                   ResourceTable* table,
    269                                   const sp<ResourceTypeSet>& set,
    270                                   const char* resType)
    271 {
    272     String8 type8(resType);
    273     String16 type16(resType);
    274 
    275     bool hasErrors = false;
    276 
    277     ResourceDirIterator it(set, String8(resType));
    278     ssize_t res;
    279     while ((res=it.next()) == NO_ERROR) {
    280         if (bundle->getVerbose()) {
    281             printf("    (new resource id %s from %s)\n",
    282                    it.getBaseName().string(), it.getFile()->getPrintableSource().string());
    283         }
    284         String16 baseName(it.getBaseName());
    285         const char16_t* str = baseName.string();
    286         const char16_t* const end = str + baseName.size();
    287         while (str < end) {
    288             if (!((*str >= 'a' && *str <= 'z')
    289                     || (*str >= '0' && *str <= '9')
    290                     || *str == '_' || *str == '.')) {
    291                 fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",
    292                         it.getPath().string());
    293                 hasErrors = true;
    294             }
    295             str++;
    296         }
    297         String8 resPath = it.getPath();
    298         resPath.convertToResPath();
    299         table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),
    300                         type16,
    301                         baseName,
    302                         String16(resPath),
    303                         NULL,
    304                         &it.getParams());
    305         assets->addResource(it.getLeafName(), resPath, it.getFile(), type8);
    306     }
    307 
    308     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
    309 }
    310 
    311 class PreProcessImageWorkUnit : public WorkQueue::WorkUnit {
    312 public:
    313     PreProcessImageWorkUnit(const Bundle* bundle, const sp<AaptAssets>& assets,
    314             const sp<AaptFile>& file, volatile bool* hasErrors) :
    315             mBundle(bundle), mAssets(assets), mFile(file), mHasErrors(hasErrors) {
    316     }
    317 
    318     virtual bool run() {
    319         status_t status = preProcessImage(mBundle, mAssets, mFile, NULL);
    320         if (status) {
    321             *mHasErrors = true;
    322         }
    323         return true; // continue even if there are errors
    324     }
    325 
    326 private:
    327     const Bundle* mBundle;
    328     sp<AaptAssets> mAssets;
    329     sp<AaptFile> mFile;
    330     volatile bool* mHasErrors;
    331 };
    332 
    333 static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& assets,
    334                           const sp<ResourceTypeSet>& set, const char* type)
    335 {
    336     volatile bool hasErrors = false;
    337     ssize_t res = NO_ERROR;
    338     if (bundle->getUseCrunchCache() == false) {
    339         WorkQueue wq(MAX_THREADS, false);
    340         ResourceDirIterator it(set, String8(type));
    341         while ((res=it.next()) == NO_ERROR) {
    342             PreProcessImageWorkUnit* w = new PreProcessImageWorkUnit(
    343                     bundle, assets, it.getFile(), &hasErrors);
    344             status_t status = wq.schedule(w);
    345             if (status) {
    346                 fprintf(stderr, "preProcessImages failed: schedule() returned %d\n", status);
    347                 hasErrors = true;
    348                 delete w;
    349                 break;
    350             }
    351         }
    352         status_t status = wq.finish();
    353         if (status) {
    354             fprintf(stderr, "preProcessImages failed: finish() returned %d\n", status);
    355             hasErrors = true;
    356         }
    357     }
    358     return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
    359 }
    360 
    361 status_t postProcessImages(const sp<AaptAssets>& assets,
    362                            ResourceTable* table,
    363                            const sp<ResourceTypeSet>& set)
    364 {
    365     ResourceDirIterator it(set, String8("drawable"));
    366     bool hasErrors = false;
    367     ssize_t res;
    368     while ((res=it.next()) == NO_ERROR) {
    369         res = postProcessImage(assets, table, it.getFile());
    370         if (res < NO_ERROR) {
    371             hasErrors = true;
    372         }
    373     }
    374 
    375     return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
    376 }
    377 
    378 static void collect_files(const sp<AaptDir>& dir,
    379         KeyedVector<String8, sp<ResourceTypeSet> >* resources)
    380 {
    381     const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles();
    382     int N = groups.size();
    383     for (int i=0; i<N; i++) {
    384         String8 leafName = groups.keyAt(i);
    385         const sp<AaptGroup>& group = groups.valueAt(i);
    386 
    387         const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files
    388                 = group->getFiles();
    389 
    390         if (files.size() == 0) {
    391             continue;
    392         }
    393 
    394         String8 resType = files.valueAt(0)->getResourceType();
    395 
    396         ssize_t index = resources->indexOfKey(resType);
    397 
    398         if (index < 0) {
    399             sp<ResourceTypeSet> set = new ResourceTypeSet();
    400             NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n",
    401                     leafName.string(), group->getPath().string(), group.get()));
    402             set->add(leafName, group);
    403             resources->add(resType, set);
    404         } else {
    405             sp<ResourceTypeSet> set = resources->valueAt(index);
    406             index = set->indexOfKey(leafName);
    407             if (index < 0) {
    408                 NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n",
    409                         leafName.string(), group->getPath().string(), group.get()));
    410                 set->add(leafName, group);
    411             } else {
    412                 sp<AaptGroup> existingGroup = set->valueAt(index);
    413                 NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n",
    414                         leafName.string(), group->getPath().string(), group.get()));
    415                 for (size_t j=0; j<files.size(); j++) {
    416                     NOISY(printf("Adding file %s in group %s resType %s\n",
    417                         files.valueAt(j)->getSourceFile().string(),
    418                         files.keyAt(j).toDirName(String8()).string(),
    419                         resType.string()));
    420                     status_t err = existingGroup->addFile(files.valueAt(j));
    421                 }
    422             }
    423         }
    424     }
    425 }
    426 
    427 static void collect_files(const sp<AaptAssets>& ass,
    428         KeyedVector<String8, sp<ResourceTypeSet> >* resources)
    429 {
    430     const Vector<sp<AaptDir> >& dirs = ass->resDirs();
    431     int N = dirs.size();
    432 
    433     for (int i=0; i<N; i++) {
    434         sp<AaptDir> d = dirs.itemAt(i);
    435         NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
    436                 d->getLeaf().string()));
    437         collect_files(d, resources);
    438 
    439         // don't try to include the res dir
    440         NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string()));
    441         ass->removeDir(d->getLeaf());
    442     }
    443 }
    444 
    445 enum {
    446     ATTR_OKAY = -1,
    447     ATTR_NOT_FOUND = -2,
    448     ATTR_LEADING_SPACES = -3,
    449     ATTR_TRAILING_SPACES = -4
    450 };
    451 static int validateAttr(const String8& path, const ResTable& table,
    452         const ResXMLParser& parser,
    453         const char* ns, const char* attr, const char* validChars, bool required)
    454 {
    455     size_t len;
    456 
    457     ssize_t index = parser.indexOfAttribute(ns, attr);
    458     const uint16_t* str;
    459     Res_value value;
    460     if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) {
    461         const ResStringPool* pool = &parser.getStrings();
    462         if (value.dataType == Res_value::TYPE_REFERENCE) {
    463             uint32_t specFlags = 0;
    464             int strIdx;
    465             if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) {
    466                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n",
    467                         path.string(), parser.getLineNumber(),
    468                         String8(parser.getElementName(&len)).string(), attr,
    469                         value.data);
    470                 return ATTR_NOT_FOUND;
    471             }
    472 
    473             pool = table.getTableStringBlock(strIdx);
    474             #if 0
    475             if (pool != NULL) {
    476                 str = pool->stringAt(value.data, &len);
    477             }
    478             printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr,
    479                     specFlags, strIdx, str != NULL ? String8(str).string() : "???");
    480             #endif
    481             if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) {
    482                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n",
    483                         path.string(), parser.getLineNumber(),
    484                         String8(parser.getElementName(&len)).string(), attr,
    485                         specFlags);
    486                 return ATTR_NOT_FOUND;
    487             }
    488         }
    489         if (value.dataType == Res_value::TYPE_STRING) {
    490             if (pool == NULL) {
    491                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n",
    492                         path.string(), parser.getLineNumber(),
    493                         String8(parser.getElementName(&len)).string(), attr);
    494                 return ATTR_NOT_FOUND;
    495             }
    496             if ((str=pool->stringAt(value.data, &len)) == NULL) {
    497                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n",
    498                         path.string(), parser.getLineNumber(),
    499                         String8(parser.getElementName(&len)).string(), attr);
    500                 return ATTR_NOT_FOUND;
    501             }
    502         } else {
    503             fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n",
    504                     path.string(), parser.getLineNumber(),
    505                     String8(parser.getElementName(&len)).string(), attr,
    506                     value.dataType);
    507             return ATTR_NOT_FOUND;
    508         }
    509         if (validChars) {
    510             for (size_t i=0; i<len; i++) {
    511                 uint16_t c = str[i];
    512                 const char* p = validChars;
    513                 bool okay = false;
    514                 while (*p) {
    515                     if (c == *p) {
    516                         okay = true;
    517                         break;
    518                     }
    519                     p++;
    520                 }
    521                 if (!okay) {
    522                     fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n",
    523                             path.string(), parser.getLineNumber(),
    524                             String8(parser.getElementName(&len)).string(), attr, (char)str[i]);
    525                     return (int)i;
    526                 }
    527             }
    528         }
    529         if (*str == ' ') {
    530             fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n",
    531                     path.string(), parser.getLineNumber(),
    532                     String8(parser.getElementName(&len)).string(), attr);
    533             return ATTR_LEADING_SPACES;
    534         }
    535         if (str[len-1] == ' ') {
    536             fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n",
    537                     path.string(), parser.getLineNumber(),
    538                     String8(parser.getElementName(&len)).string(), attr);
    539             return ATTR_TRAILING_SPACES;
    540         }
    541         return ATTR_OKAY;
    542     }
    543     if (required) {
    544         fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n",
    545                 path.string(), parser.getLineNumber(),
    546                 String8(parser.getElementName(&len)).string(), attr);
    547         return ATTR_NOT_FOUND;
    548     }
    549     return ATTR_OKAY;
    550 }
    551 
    552 static void checkForIds(const String8& path, ResXMLParser& parser)
    553 {
    554     ResXMLTree::event_code_t code;
    555     while ((code=parser.next()) != ResXMLTree::END_DOCUMENT
    556            && code > ResXMLTree::BAD_DOCUMENT) {
    557         if (code == ResXMLTree::START_TAG) {
    558             ssize_t index = parser.indexOfAttribute(NULL, "id");
    559             if (index >= 0) {
    560                 fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
    561                         path.string(), parser.getLineNumber());
    562             }
    563         }
    564     }
    565 }
    566 
    567 static bool applyFileOverlay(Bundle *bundle,
    568                              const sp<AaptAssets>& assets,
    569                              sp<ResourceTypeSet> *baseSet,
    570                              const char *resType)
    571 {
    572     if (bundle->getVerbose()) {
    573         printf("applyFileOverlay for %s\n", resType);
    574     }
    575 
    576     // Replace any base level files in this category with any found from the overlay
    577     // Also add any found only in the overlay.
    578     sp<AaptAssets> overlay = assets->getOverlay();
    579     String8 resTypeString(resType);
    580 
    581     // work through the linked list of overlays
    582     while (overlay.get()) {
    583         KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources();
    584 
    585         // get the overlay resources of the requested type
    586         ssize_t index = overlayRes->indexOfKey(resTypeString);
    587         if (index >= 0) {
    588             sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index);
    589 
    590             // for each of the resources, check for a match in the previously built
    591             // non-overlay "baseset".
    592             size_t overlayCount = overlaySet->size();
    593             for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
    594                 if (bundle->getVerbose()) {
    595                     printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
    596                 }
    597                 size_t baseIndex = UNKNOWN_ERROR;
    598                 if (baseSet->get() != NULL) {
    599                     baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
    600                 }
    601                 if (baseIndex < UNKNOWN_ERROR) {
    602                     // look for same flavor.  For a given file (strings.xml, for example)
    603                     // there may be a locale specific or other flavors - we want to match
    604                     // the same flavor.
    605                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
    606                     sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex);
    607 
    608                     DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
    609                             overlayGroup->getFiles();
    610                     if (bundle->getVerbose()) {
    611                         DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
    612                                 baseGroup->getFiles();
    613                         for (size_t i=0; i < baseFiles.size(); i++) {
    614                             printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i,
    615                                     baseFiles.keyAt(i).toString().string());
    616                         }
    617                         for (size_t i=0; i < overlayFiles.size(); i++) {
    618                             printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i,
    619                                     overlayFiles.keyAt(i).toString().string());
    620                         }
    621                     }
    622 
    623                     size_t overlayGroupSize = overlayFiles.size();
    624                     for (size_t overlayGroupIndex = 0;
    625                             overlayGroupIndex<overlayGroupSize;
    626                             overlayGroupIndex++) {
    627                         size_t baseFileIndex =
    628                                 baseGroup->getFiles().indexOfKey(overlayFiles.
    629                                 keyAt(overlayGroupIndex));
    630                         if (baseFileIndex < UNKNOWN_ERROR) {
    631                             if (bundle->getVerbose()) {
    632                                 printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
    633                                         (ZD_TYPE) baseFileIndex,
    634                                         overlayGroup->getLeaf().string(),
    635                                         overlayFiles.keyAt(overlayGroupIndex).toString().string());
    636                             }
    637                             baseGroup->removeFile(baseFileIndex);
    638                         } else {
    639                             // didn't find a match fall through and add it..
    640                             if (true || bundle->getVerbose()) {
    641                                 printf("nothing matches overlay file %s, for flavor %s\n",
    642                                         overlayGroup->getLeaf().string(),
    643                                         overlayFiles.keyAt(overlayGroupIndex).toString().string());
    644                             }
    645                         }
    646                         baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
    647                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
    648                     }
    649                 } else {
    650                     if (baseSet->get() == NULL) {
    651                         *baseSet = new ResourceTypeSet();
    652                         assets->getResources()->add(String8(resType), *baseSet);
    653                     }
    654                     // this group doesn't exist (a file that's only in the overlay)
    655                     (*baseSet)->add(overlaySet->keyAt(overlayIndex),
    656                             overlaySet->valueAt(overlayIndex));
    657                     // make sure all flavors are defined in the resources.
    658                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
    659                     DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
    660                             overlayGroup->getFiles();
    661                     size_t overlayGroupSize = overlayFiles.size();
    662                     for (size_t overlayGroupIndex = 0;
    663                             overlayGroupIndex<overlayGroupSize;
    664                             overlayGroupIndex++) {
    665                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
    666                     }
    667                 }
    668             }
    669             // this overlay didn't have resources for this type
    670         }
    671         // try next overlay
    672         overlay = overlay->getOverlay();
    673     }
    674     return true;
    675 }
    676 
    677 /*
    678  * Inserts an attribute in a given node, only if the attribute does not
    679  * exist.
    680  * If errorOnFailedInsert is true, and the attribute already exists, returns false.
    681  * Returns true otherwise, even if the attribute already exists.
    682  */
    683 bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
    684         const char* attr8, const char* value, bool errorOnFailedInsert)
    685 {
    686     if (value == NULL) {
    687         return true;
    688     }
    689 
    690     const String16 ns(ns8);
    691     const String16 attr(attr8);
    692 
    693     if (node->getAttribute(ns, attr) != NULL) {
    694         if (errorOnFailedInsert) {
    695             fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);"
    696                             " cannot insert new value %s.\n",
    697                     String8(attr).string(), String8(ns).string(), value);
    698             return false;
    699         }
    700 
    701         fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
    702                         " using existing value in manifest.\n",
    703                 String8(attr).string(), String8(ns).string());
    704 
    705         // don't stop the build.
    706         return true;
    707     }
    708 
    709     node->addAttribute(ns, attr, String16(value));
    710     return true;
    711 }
    712 
    713 static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
    714         const String16& attrName) {
    715     XMLNode::attribute_entry* attr = node->editAttribute(
    716             String16("http://schemas.android.com/apk/res/android"), attrName);
    717     if (attr != NULL) {
    718         String8 name(attr->string);
    719 
    720         // asdf     --> package.asdf
    721         // .asdf  .a.b  --> package.asdf package.a.b
    722         // asdf.adsf --> asdf.asdf
    723         String8 className;
    724         const char* p = name.string();
    725         const char* q = strchr(p, '.');
    726         if (p == q) {
    727             className += package;
    728             className += name;
    729         } else if (q == NULL) {
    730             className += package;
    731             className += ".";
    732             className += name;
    733         } else {
    734             className += name;
    735         }
    736         NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string()));
    737         attr->string.setTo(String16(className));
    738     }
    739 }
    740 
    741 status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
    742 {
    743     root = root->searchElement(String16(), String16("manifest"));
    744     if (root == NULL) {
    745         fprintf(stderr, "No <manifest> tag.\n");
    746         return UNKNOWN_ERROR;
    747     }
    748 
    749     bool errorOnFailedInsert = bundle->getErrorOnFailedInsert();
    750 
    751     if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
    752             bundle->getVersionCode(), errorOnFailedInsert)) {
    753         return UNKNOWN_ERROR;
    754     }
    755     if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
    756             bundle->getVersionName(), errorOnFailedInsert)) {
    757         return UNKNOWN_ERROR;
    758     }
    759 
    760     if (bundle->getMinSdkVersion() != NULL
    761             || bundle->getTargetSdkVersion() != NULL
    762             || bundle->getMaxSdkVersion() != NULL) {
    763         sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk"));
    764         if (vers == NULL) {
    765             vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk"));
    766             root->insertChildAt(vers, 0);
    767         }
    768 
    769         if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
    770                 bundle->getMinSdkVersion(), errorOnFailedInsert)) {
    771             return UNKNOWN_ERROR;
    772         }
    773         if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
    774                 bundle->getTargetSdkVersion(), errorOnFailedInsert)) {
    775             return UNKNOWN_ERROR;
    776         }
    777         if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
    778                 bundle->getMaxSdkVersion(), errorOnFailedInsert)) {
    779             return UNKNOWN_ERROR;
    780         }
    781     }
    782 
    783     if (bundle->getDebugMode()) {
    784         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
    785         if (application != NULL) {
    786             if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true",
    787                     errorOnFailedInsert)) {
    788                 return UNKNOWN_ERROR;
    789             }
    790         }
    791     }
    792 
    793     // Deal with manifest package name overrides
    794     const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
    795     if (manifestPackageNameOverride != NULL) {
    796         // Update the actual package name
    797         XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package"));
    798         if (attr == NULL) {
    799             fprintf(stderr, "package name is required with --rename-manifest-package.\n");
    800             return UNKNOWN_ERROR;
    801         }
    802         String8 origPackage(attr->string);
    803         attr->string.setTo(String16(manifestPackageNameOverride));
    804         NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride));
    805 
    806         // Make class names fully qualified
    807         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
    808         if (application != NULL) {
    809             fullyQualifyClassName(origPackage, application, String16("name"));
    810             fullyQualifyClassName(origPackage, application, String16("backupAgent"));
    811 
    812             Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren());
    813             for (size_t i = 0; i < children.size(); i++) {
    814                 sp<XMLNode> child = children.editItemAt(i);
    815                 String8 tag(child->getElementName());
    816                 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
    817                     fullyQualifyClassName(origPackage, child, String16("name"));
    818                 } else if (tag == "activity-alias") {
    819                     fullyQualifyClassName(origPackage, child, String16("name"));
    820                     fullyQualifyClassName(origPackage, child, String16("targetActivity"));
    821                 }
    822             }
    823         }
    824     }
    825 
    826     // Deal with manifest package name overrides
    827     const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride();
    828     if (instrumentationPackageNameOverride != NULL) {
    829         // Fix up instrumentation targets.
    830         Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren());
    831         for (size_t i = 0; i < children.size(); i++) {
    832             sp<XMLNode> child = children.editItemAt(i);
    833             String8 tag(child->getElementName());
    834             if (tag == "instrumentation") {
    835                 XMLNode::attribute_entry* attr = child->editAttribute(
    836                         String16("http://schemas.android.com/apk/res/android"), String16("targetPackage"));
    837                 if (attr != NULL) {
    838                     attr->string.setTo(String16(instrumentationPackageNameOverride));
    839                 }
    840             }
    841         }
    842     }
    843 
    844     return NO_ERROR;
    845 }
    846 
    847 #define ASSIGN_IT(n) \
    848         do { \
    849             ssize_t index = resources->indexOfKey(String8(#n)); \
    850             if (index >= 0) { \
    851                 n ## s = resources->valueAt(index); \
    852             } \
    853         } while (0)
    854 
    855 status_t updatePreProcessedCache(Bundle* bundle)
    856 {
    857     #if BENCHMARK
    858     fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n");
    859     long startPNGTime = clock();
    860     #endif /* BENCHMARK */
    861 
    862     String8 source(bundle->getResourceSourceDirs()[0]);
    863     String8 dest(bundle->getCrunchedOutputDir());
    864 
    865     FileFinder* ff = new SystemFileFinder();
    866     CrunchCache cc(source,dest,ff);
    867 
    868     CacheUpdater* cu = new SystemCacheUpdater(bundle);
    869     size_t numFiles = cc.crunch(cu);
    870 
    871     if (bundle->getVerbose())
    872         fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles);
    873 
    874     delete ff;
    875     delete cu;
    876 
    877     #if BENCHMARK
    878     fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n"
    879             ,(clock() - startPNGTime)/1000.0);
    880     #endif /* BENCHMARK */
    881     return 0;
    882 }
    883 
    884 status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
    885 {
    886     // First, look for a package file to parse.  This is required to
    887     // be able to generate the resource information.
    888     sp<AaptGroup> androidManifestFile =
    889             assets->getFiles().valueFor(String8("AndroidManifest.xml"));
    890     if (androidManifestFile == NULL) {
    891         fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
    892         return UNKNOWN_ERROR;
    893     }
    894 
    895     status_t err = parsePackage(bundle, assets, androidManifestFile);
    896     if (err != NO_ERROR) {
    897         return err;
    898     }
    899 
    900     NOISY(printf("Creating resources for package %s\n",
    901                  assets->getPackage().string()));
    902 
    903     ResourceTable table(bundle, String16(assets->getPackage()));
    904     err = table.addIncludedResources(bundle, assets);
    905     if (err != NO_ERROR) {
    906         return err;
    907     }
    908 
    909     NOISY(printf("Found %d included resource packages\n", (int)table.size()));
    910 
    911     // Standard flags for compiled XML and optional UTF-8 encoding
    912     int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
    913 
    914     /* Only enable UTF-8 if the caller of aapt didn't specifically
    915      * request UTF-16 encoding and the parameters of this package
    916      * allow UTF-8 to be used.
    917      */
    918     if (!bundle->getUTF16StringsOption()) {
    919         xmlFlags |= XML_COMPILE_UTF8;
    920     }
    921 
    922     // --------------------------------------------------------------
    923     // First, gather all resource information.
    924     // --------------------------------------------------------------
    925 
    926     // resType -> leafName -> group
    927     KeyedVector<String8, sp<ResourceTypeSet> > *resources =
    928             new KeyedVector<String8, sp<ResourceTypeSet> >;
    929     collect_files(assets, resources);
    930 
    931     sp<ResourceTypeSet> drawables;
    932     sp<ResourceTypeSet> layouts;
    933     sp<ResourceTypeSet> anims;
    934     sp<ResourceTypeSet> animators;
    935     sp<ResourceTypeSet> interpolators;
    936     sp<ResourceTypeSet> transitions;
    937     sp<ResourceTypeSet> xmls;
    938     sp<ResourceTypeSet> raws;
    939     sp<ResourceTypeSet> colors;
    940     sp<ResourceTypeSet> menus;
    941     sp<ResourceTypeSet> mipmaps;
    942 
    943     ASSIGN_IT(drawable);
    944     ASSIGN_IT(layout);
    945     ASSIGN_IT(anim);
    946     ASSIGN_IT(animator);
    947     ASSIGN_IT(interpolator);
    948     ASSIGN_IT(transition);
    949     ASSIGN_IT(xml);
    950     ASSIGN_IT(raw);
    951     ASSIGN_IT(color);
    952     ASSIGN_IT(menu);
    953     ASSIGN_IT(mipmap);
    954 
    955     assets->setResources(resources);
    956     // now go through any resource overlays and collect their files
    957     sp<AaptAssets> current = assets->getOverlay();
    958     while(current.get()) {
    959         KeyedVector<String8, sp<ResourceTypeSet> > *resources =
    960                 new KeyedVector<String8, sp<ResourceTypeSet> >;
    961         current->setResources(resources);
    962         collect_files(current, resources);
    963         current = current->getOverlay();
    964     }
    965     // apply the overlay files to the base set
    966     if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
    967             !applyFileOverlay(bundle, assets, &layouts, "layout") ||
    968             !applyFileOverlay(bundle, assets, &anims, "anim") ||
    969             !applyFileOverlay(bundle, assets, &animators, "animator") ||
    970             !applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
    971             !applyFileOverlay(bundle, assets, &transitions, "transition") ||
    972             !applyFileOverlay(bundle, assets, &xmls, "xml") ||
    973             !applyFileOverlay(bundle, assets, &raws, "raw") ||
    974             !applyFileOverlay(bundle, assets, &colors, "color") ||
    975             !applyFileOverlay(bundle, assets, &menus, "menu") ||
    976             !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
    977         return UNKNOWN_ERROR;
    978     }
    979 
    980     bool hasErrors = false;
    981 
    982     if (drawables != NULL) {
    983         if (bundle->getOutputAPKFile() != NULL) {
    984             err = preProcessImages(bundle, assets, drawables, "drawable");
    985         }
    986         if (err == NO_ERROR) {
    987             err = makeFileResources(bundle, assets, &table, drawables, "drawable");
    988             if (err != NO_ERROR) {
    989                 hasErrors = true;
    990             }
    991         } else {
    992             hasErrors = true;
    993         }
    994     }
    995 
    996     if (mipmaps != NULL) {
    997         if (bundle->getOutputAPKFile() != NULL) {
    998             err = preProcessImages(bundle, assets, mipmaps, "mipmap");
    999         }
   1000         if (err == NO_ERROR) {
   1001             err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
   1002             if (err != NO_ERROR) {
   1003                 hasErrors = true;
   1004             }
   1005         } else {
   1006             hasErrors = true;
   1007         }
   1008     }
   1009 
   1010     if (layouts != NULL) {
   1011         err = makeFileResources(bundle, assets, &table, layouts, "layout");
   1012         if (err != NO_ERROR) {
   1013             hasErrors = true;
   1014         }
   1015     }
   1016 
   1017     if (anims != NULL) {
   1018         err = makeFileResources(bundle, assets, &table, anims, "anim");
   1019         if (err != NO_ERROR) {
   1020             hasErrors = true;
   1021         }
   1022     }
   1023 
   1024     if (animators != NULL) {
   1025         err = makeFileResources(bundle, assets, &table, animators, "animator");
   1026         if (err != NO_ERROR) {
   1027             hasErrors = true;
   1028         }
   1029     }
   1030 
   1031     if (transitions != NULL) {
   1032         err = makeFileResources(bundle, assets, &table, transitions, "transition");
   1033         if (err != NO_ERROR) {
   1034             hasErrors = true;
   1035         }
   1036     }
   1037 
   1038     if (interpolators != NULL) {
   1039         err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
   1040         if (err != NO_ERROR) {
   1041             hasErrors = true;
   1042         }
   1043     }
   1044 
   1045     if (xmls != NULL) {
   1046         err = makeFileResources(bundle, assets, &table, xmls, "xml");
   1047         if (err != NO_ERROR) {
   1048             hasErrors = true;
   1049         }
   1050     }
   1051 
   1052     if (raws != NULL) {
   1053         err = makeFileResources(bundle, assets, &table, raws, "raw");
   1054         if (err != NO_ERROR) {
   1055             hasErrors = true;
   1056         }
   1057     }
   1058 
   1059     // compile resources
   1060     current = assets;
   1061     while(current.get()) {
   1062         KeyedVector<String8, sp<ResourceTypeSet> > *resources =
   1063                 current->getResources();
   1064 
   1065         ssize_t index = resources->indexOfKey(String8("values"));
   1066         if (index >= 0) {
   1067             ResourceDirIterator it(resources->valueAt(index), String8("values"));
   1068             ssize_t res;
   1069             while ((res=it.next()) == NO_ERROR) {
   1070                 sp<AaptFile> file = it.getFile();
   1071                 res = compileResourceFile(bundle, assets, file, it.getParams(),
   1072                                           (current!=assets), &table);
   1073                 if (res != NO_ERROR) {
   1074                     hasErrors = true;
   1075                 }
   1076             }
   1077         }
   1078         current = current->getOverlay();
   1079     }
   1080 
   1081     if (colors != NULL) {
   1082         err = makeFileResources(bundle, assets, &table, colors, "color");
   1083         if (err != NO_ERROR) {
   1084             hasErrors = true;
   1085         }
   1086     }
   1087 
   1088     if (menus != NULL) {
   1089         err = makeFileResources(bundle, assets, &table, menus, "menu");
   1090         if (err != NO_ERROR) {
   1091             hasErrors = true;
   1092         }
   1093     }
   1094 
   1095     // --------------------------------------------------------------------
   1096     // Assignment of resource IDs and initial generation of resource table.
   1097     // --------------------------------------------------------------------
   1098 
   1099     if (table.hasResources()) {
   1100         sp<AaptFile> resFile(getResourceFile(assets));
   1101         if (resFile == NULL) {
   1102             fprintf(stderr, "Error: unable to generate entry for resource data\n");
   1103             return UNKNOWN_ERROR;
   1104         }
   1105 
   1106         err = table.assignResourceIds();
   1107         if (err < NO_ERROR) {
   1108             return err;
   1109         }
   1110     }
   1111 
   1112     // --------------------------------------------------------------
   1113     // Finally, we can now we can compile XML files, which may reference
   1114     // resources.
   1115     // --------------------------------------------------------------
   1116 
   1117     if (layouts != NULL) {
   1118         ResourceDirIterator it(layouts, String8("layout"));
   1119         while ((err=it.next()) == NO_ERROR) {
   1120             String8 src = it.getFile()->getPrintableSource();
   1121             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1122             if (err == NO_ERROR) {
   1123                 ResXMLTree block;
   1124                 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
   1125                 checkForIds(src, block);
   1126             } else {
   1127                 hasErrors = true;
   1128             }
   1129         }
   1130 
   1131         if (err < NO_ERROR) {
   1132             hasErrors = true;
   1133         }
   1134         err = NO_ERROR;
   1135     }
   1136 
   1137     if (anims != NULL) {
   1138         ResourceDirIterator it(anims, String8("anim"));
   1139         while ((err=it.next()) == NO_ERROR) {
   1140             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1141             if (err != NO_ERROR) {
   1142                 hasErrors = true;
   1143             }
   1144         }
   1145 
   1146         if (err < NO_ERROR) {
   1147             hasErrors = true;
   1148         }
   1149         err = NO_ERROR;
   1150     }
   1151 
   1152     if (animators != NULL) {
   1153         ResourceDirIterator it(animators, String8("animator"));
   1154         while ((err=it.next()) == NO_ERROR) {
   1155             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1156             if (err != NO_ERROR) {
   1157                 hasErrors = true;
   1158             }
   1159         }
   1160 
   1161         if (err < NO_ERROR) {
   1162             hasErrors = true;
   1163         }
   1164         err = NO_ERROR;
   1165     }
   1166 
   1167     if (interpolators != NULL) {
   1168         ResourceDirIterator it(interpolators, String8("interpolator"));
   1169         while ((err=it.next()) == NO_ERROR) {
   1170             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1171             if (err != NO_ERROR) {
   1172                 hasErrors = true;
   1173             }
   1174         }
   1175 
   1176         if (err < NO_ERROR) {
   1177             hasErrors = true;
   1178         }
   1179         err = NO_ERROR;
   1180     }
   1181 
   1182     if (transitions != NULL) {
   1183         ResourceDirIterator it(transitions, String8("transition"));
   1184         while ((err=it.next()) == NO_ERROR) {
   1185             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1186             if (err != NO_ERROR) {
   1187                 hasErrors = true;
   1188             }
   1189         }
   1190 
   1191         if (err < NO_ERROR) {
   1192             hasErrors = true;
   1193         }
   1194         err = NO_ERROR;
   1195     }
   1196 
   1197     if (xmls != NULL) {
   1198         ResourceDirIterator it(xmls, String8("xml"));
   1199         while ((err=it.next()) == NO_ERROR) {
   1200             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1201             if (err != NO_ERROR) {
   1202                 hasErrors = true;
   1203             }
   1204         }
   1205 
   1206         if (err < NO_ERROR) {
   1207             hasErrors = true;
   1208         }
   1209         err = NO_ERROR;
   1210     }
   1211 
   1212     if (drawables != NULL) {
   1213         err = postProcessImages(assets, &table, drawables);
   1214         if (err != NO_ERROR) {
   1215             hasErrors = true;
   1216         }
   1217     }
   1218 
   1219     if (colors != NULL) {
   1220         ResourceDirIterator it(colors, String8("color"));
   1221         while ((err=it.next()) == NO_ERROR) {
   1222           err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1223             if (err != NO_ERROR) {
   1224                 hasErrors = true;
   1225             }
   1226         }
   1227 
   1228         if (err < NO_ERROR) {
   1229             hasErrors = true;
   1230         }
   1231         err = NO_ERROR;
   1232     }
   1233 
   1234     if (menus != NULL) {
   1235         ResourceDirIterator it(menus, String8("menu"));
   1236         while ((err=it.next()) == NO_ERROR) {
   1237             String8 src = it.getFile()->getPrintableSource();
   1238             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
   1239             if (err != NO_ERROR) {
   1240                 hasErrors = true;
   1241             }
   1242             ResXMLTree block;
   1243             block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
   1244             checkForIds(src, block);
   1245         }
   1246 
   1247         if (err < NO_ERROR) {
   1248             hasErrors = true;
   1249         }
   1250         err = NO_ERROR;
   1251     }
   1252 
   1253     if (table.validateLocalizations()) {
   1254         hasErrors = true;
   1255     }
   1256 
   1257     if (hasErrors) {
   1258         return UNKNOWN_ERROR;
   1259     }
   1260 
   1261     const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
   1262     String8 manifestPath(manifestFile->getPrintableSource());
   1263 
   1264     // Generate final compiled manifest file.
   1265     manifestFile->clearData();
   1266     sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
   1267     if (manifestTree == NULL) {
   1268         return UNKNOWN_ERROR;
   1269     }
   1270     err = massageManifest(bundle, manifestTree);
   1271     if (err < NO_ERROR) {
   1272         return err;
   1273     }
   1274     err = compileXmlFile(assets, manifestTree, manifestFile, &table);
   1275     if (err < NO_ERROR) {
   1276         return err;
   1277     }
   1278 
   1279     //block.restart();
   1280     //printXMLBlock(&block);
   1281 
   1282     // --------------------------------------------------------------
   1283     // Generate the final resource table.
   1284     // Re-flatten because we may have added new resource IDs
   1285     // --------------------------------------------------------------
   1286 
   1287     ResTable finalResTable;
   1288     sp<AaptFile> resFile;
   1289 
   1290     if (table.hasResources()) {
   1291         sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
   1292         err = table.addSymbols(symbols);
   1293         if (err < NO_ERROR) {
   1294             return err;
   1295         }
   1296 
   1297         resFile = getResourceFile(assets);
   1298         if (resFile == NULL) {
   1299             fprintf(stderr, "Error: unable to generate entry for resource data\n");
   1300             return UNKNOWN_ERROR;
   1301         }
   1302 
   1303         err = table.flatten(bundle, resFile);
   1304         if (err < NO_ERROR) {
   1305             return err;
   1306         }
   1307 
   1308         if (bundle->getPublicOutputFile()) {
   1309             FILE* fp = fopen(bundle->getPublicOutputFile(), "w+");
   1310             if (fp == NULL) {
   1311                 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n",
   1312                         (const char*)bundle->getPublicOutputFile(), strerror(errno));
   1313                 return UNKNOWN_ERROR;
   1314             }
   1315             if (bundle->getVerbose()) {
   1316                 printf("  Writing public definitions to %s.\n", bundle->getPublicOutputFile());
   1317             }
   1318             table.writePublicDefinitions(String16(assets->getPackage()), fp);
   1319             fclose(fp);
   1320         }
   1321 
   1322         // Read resources back in,
   1323         finalResTable.add(resFile->getData(), resFile->getSize(), NULL);
   1324 
   1325 #if 0
   1326         NOISY(
   1327               printf("Generated resources:\n");
   1328               finalResTable.print();
   1329         )
   1330 #endif
   1331     }
   1332 
   1333     // Perform a basic validation of the manifest file.  This time we
   1334     // parse it with the comments intact, so that we can use them to
   1335     // generate java docs...  so we are not going to write this one
   1336     // back out to the final manifest data.
   1337     sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(),
   1338             manifestFile->getGroupEntry(),
   1339             manifestFile->getResourceType());
   1340     err = compileXmlFile(assets, manifestFile,
   1341             outManifestFile, &table,
   1342             XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
   1343             | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
   1344     if (err < NO_ERROR) {
   1345         return err;
   1346     }
   1347     ResXMLTree block;
   1348     block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true);
   1349     String16 manifest16("manifest");
   1350     String16 permission16("permission");
   1351     String16 permission_group16("permission-group");
   1352     String16 uses_permission16("uses-permission");
   1353     String16 instrumentation16("instrumentation");
   1354     String16 application16("application");
   1355     String16 provider16("provider");
   1356     String16 service16("service");
   1357     String16 receiver16("receiver");
   1358     String16 activity16("activity");
   1359     String16 action16("action");
   1360     String16 category16("category");
   1361     String16 data16("scheme");
   1362     const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1363         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
   1364     const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
   1365         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
   1366     const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1367         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$";
   1368     const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1369         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:";
   1370     const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1371         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;";
   1372     const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1373         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+";
   1374     const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz"
   1375         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
   1376     ResXMLTree::event_code_t code;
   1377     sp<AaptSymbols> permissionSymbols;
   1378     sp<AaptSymbols> permissionGroupSymbols;
   1379     while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1380            && code > ResXMLTree::BAD_DOCUMENT) {
   1381         if (code == ResXMLTree::START_TAG) {
   1382             size_t len;
   1383             if (block.getElementNamespace(&len) != NULL) {
   1384                 continue;
   1385             }
   1386             if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) {
   1387                 if (validateAttr(manifestPath, finalResTable, block, NULL, "package",
   1388                                  packageIdentChars, true) != ATTR_OKAY) {
   1389                     hasErrors = true;
   1390                 }
   1391                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1392                                  "sharedUserId", packageIdentChars, false) != ATTR_OKAY) {
   1393                     hasErrors = true;
   1394                 }
   1395             } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0
   1396                     || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) {
   1397                 const bool isGroup = strcmp16(block.getElementName(&len),
   1398                         permission_group16.string()) == 0;
   1399                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1400                                  "name", isGroup ? packageIdentCharsWithTheStupid
   1401                                  : packageIdentChars, true) != ATTR_OKAY) {
   1402                     hasErrors = true;
   1403                 }
   1404                 SourcePos srcPos(manifestPath, block.getLineNumber());
   1405                 sp<AaptSymbols> syms;
   1406                 if (!isGroup) {
   1407                     syms = permissionSymbols;
   1408                     if (syms == NULL) {
   1409                         sp<AaptSymbols> symbols =
   1410                                 assets->getSymbolsFor(String8("Manifest"));
   1411                         syms = permissionSymbols = symbols->addNestedSymbol(
   1412                                 String8("permission"), srcPos);
   1413                     }
   1414                 } else {
   1415                     syms = permissionGroupSymbols;
   1416                     if (syms == NULL) {
   1417                         sp<AaptSymbols> symbols =
   1418                                 assets->getSymbolsFor(String8("Manifest"));
   1419                         syms = permissionGroupSymbols = symbols->addNestedSymbol(
   1420                                 String8("permission_group"), srcPos);
   1421                     }
   1422                 }
   1423                 size_t len;
   1424                 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name");
   1425                 const uint16_t* id = block.getAttributeStringValue(index, &len);
   1426                 if (id == NULL) {
   1427                     fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n",
   1428                             manifestPath.string(), block.getLineNumber(),
   1429                             String8(block.getElementName(&len)).string());
   1430                     hasErrors = true;
   1431                     break;
   1432                 }
   1433                 String8 idStr(id);
   1434                 char* p = idStr.lockBuffer(idStr.size());
   1435                 char* e = p + idStr.size();
   1436                 bool begins_with_digit = true;  // init to true so an empty string fails
   1437                 while (e > p) {
   1438                     e--;
   1439                     if (*e >= '0' && *e <= '9') {
   1440                       begins_with_digit = true;
   1441                       continue;
   1442                     }
   1443                     if ((*e >= 'a' && *e <= 'z') ||
   1444                         (*e >= 'A' && *e <= 'Z') ||
   1445                         (*e == '_')) {
   1446                       begins_with_digit = false;
   1447                       continue;
   1448                     }
   1449                     if (isGroup && (*e == '-')) {
   1450                         *e = '_';
   1451                         begins_with_digit = false;
   1452                         continue;
   1453                     }
   1454                     e++;
   1455                     break;
   1456                 }
   1457                 idStr.unlockBuffer();
   1458                 // verify that we stopped because we hit a period or
   1459                 // the beginning of the string, and that the
   1460                 // identifier didn't begin with a digit.
   1461                 if (begins_with_digit || (e != p && *(e-1) != '.')) {
   1462                   fprintf(stderr,
   1463                           "%s:%d: Permission name <%s> is not a valid Java symbol\n",
   1464                           manifestPath.string(), block.getLineNumber(), idStr.string());
   1465                   hasErrors = true;
   1466                 }
   1467                 syms->addStringSymbol(String8(e), idStr, srcPos);
   1468                 const uint16_t* cmt = block.getComment(&len);
   1469                 if (cmt != NULL && *cmt != 0) {
   1470                     //printf("Comment of %s: %s\n", String8(e).string(),
   1471                     //        String8(cmt).string());
   1472                     syms->appendComment(String8(e), String16(cmt), srcPos);
   1473                 } else {
   1474                     //printf("No comment for %s\n", String8(e).string());
   1475                 }
   1476                 syms->makeSymbolPublic(String8(e), srcPos);
   1477             } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
   1478                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1479                                  "name", packageIdentChars, true) != ATTR_OKAY) {
   1480                     hasErrors = true;
   1481                 }
   1482             } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) {
   1483                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1484                                  "name", classIdentChars, true) != ATTR_OKAY) {
   1485                     hasErrors = true;
   1486                 }
   1487                 if (validateAttr(manifestPath, finalResTable, block,
   1488                                  RESOURCES_ANDROID_NAMESPACE, "targetPackage",
   1489                                  packageIdentChars, true) != ATTR_OKAY) {
   1490                     hasErrors = true;
   1491                 }
   1492             } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) {
   1493                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1494                                  "name", classIdentChars, false) != ATTR_OKAY) {
   1495                     hasErrors = true;
   1496                 }
   1497                 if (validateAttr(manifestPath, finalResTable, block,
   1498                                  RESOURCES_ANDROID_NAMESPACE, "permission",
   1499                                  packageIdentChars, false) != ATTR_OKAY) {
   1500                     hasErrors = true;
   1501                 }
   1502                 if (validateAttr(manifestPath, finalResTable, block,
   1503                                  RESOURCES_ANDROID_NAMESPACE, "process",
   1504                                  processIdentChars, false) != ATTR_OKAY) {
   1505                     hasErrors = true;
   1506                 }
   1507                 if (validateAttr(manifestPath, finalResTable, block,
   1508                                  RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
   1509                                  processIdentChars, false) != ATTR_OKAY) {
   1510                     hasErrors = true;
   1511                 }
   1512             } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) {
   1513                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1514                                  "name", classIdentChars, true) != ATTR_OKAY) {
   1515                     hasErrors = true;
   1516                 }
   1517                 if (validateAttr(manifestPath, finalResTable, block,
   1518                                  RESOURCES_ANDROID_NAMESPACE, "authorities",
   1519                                  authoritiesIdentChars, true) != ATTR_OKAY) {
   1520                     hasErrors = true;
   1521                 }
   1522                 if (validateAttr(manifestPath, finalResTable, block,
   1523                                  RESOURCES_ANDROID_NAMESPACE, "permission",
   1524                                  packageIdentChars, false) != ATTR_OKAY) {
   1525                     hasErrors = true;
   1526                 }
   1527                 if (validateAttr(manifestPath, finalResTable, block,
   1528                                  RESOURCES_ANDROID_NAMESPACE, "process",
   1529                                  processIdentChars, false) != ATTR_OKAY) {
   1530                     hasErrors = true;
   1531                 }
   1532             } else if (strcmp16(block.getElementName(&len), service16.string()) == 0
   1533                        || strcmp16(block.getElementName(&len), receiver16.string()) == 0
   1534                        || strcmp16(block.getElementName(&len), activity16.string()) == 0) {
   1535                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
   1536                                  "name", classIdentChars, true) != ATTR_OKAY) {
   1537                     hasErrors = true;
   1538                 }
   1539                 if (validateAttr(manifestPath, finalResTable, block,
   1540                                  RESOURCES_ANDROID_NAMESPACE, "permission",
   1541                                  packageIdentChars, false) != ATTR_OKAY) {
   1542                     hasErrors = true;
   1543                 }
   1544                 if (validateAttr(manifestPath, finalResTable, block,
   1545                                  RESOURCES_ANDROID_NAMESPACE, "process",
   1546                                  processIdentChars, false) != ATTR_OKAY) {
   1547                     hasErrors = true;
   1548                 }
   1549                 if (validateAttr(manifestPath, finalResTable, block,
   1550                                  RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
   1551                                  processIdentChars, false) != ATTR_OKAY) {
   1552                     hasErrors = true;
   1553                 }
   1554             } else if (strcmp16(block.getElementName(&len), action16.string()) == 0
   1555                        || strcmp16(block.getElementName(&len), category16.string()) == 0) {
   1556                 if (validateAttr(manifestPath, finalResTable, block,
   1557                                  RESOURCES_ANDROID_NAMESPACE, "name",
   1558                                  packageIdentChars, true) != ATTR_OKAY) {
   1559                     hasErrors = true;
   1560                 }
   1561             } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) {
   1562                 if (validateAttr(manifestPath, finalResTable, block,
   1563                                  RESOURCES_ANDROID_NAMESPACE, "mimeType",
   1564                                  typeIdentChars, true) != ATTR_OKAY) {
   1565                     hasErrors = true;
   1566                 }
   1567                 if (validateAttr(manifestPath, finalResTable, block,
   1568                                  RESOURCES_ANDROID_NAMESPACE, "scheme",
   1569                                  schemeIdentChars, true) != ATTR_OKAY) {
   1570                     hasErrors = true;
   1571                 }
   1572             }
   1573         }
   1574     }
   1575 
   1576     if (resFile != NULL) {
   1577         // These resources are now considered to be a part of the included
   1578         // resources, for others to reference.
   1579         err = assets->addIncludedResources(resFile);
   1580         if (err < NO_ERROR) {
   1581             fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n");
   1582             return err;
   1583         }
   1584     }
   1585 
   1586     return err;
   1587 }
   1588 
   1589 static const char* getIndentSpace(int indent)
   1590 {
   1591 static const char whitespace[] =
   1592 "                                                                                       ";
   1593 
   1594     return whitespace + sizeof(whitespace) - 1 - indent*4;
   1595 }
   1596 
   1597 static String8 flattenSymbol(const String8& symbol) {
   1598     String8 result(symbol);
   1599     ssize_t first;
   1600     if ((first = symbol.find(":", 0)) >= 0
   1601             || (first = symbol.find(".", 0)) >= 0) {
   1602         size_t size = symbol.size();
   1603         char* buf = result.lockBuffer(size);
   1604         for (size_t i = first; i < size; i++) {
   1605             if (buf[i] == ':' || buf[i] == '.') {
   1606                 buf[i] = '_';
   1607             }
   1608         }
   1609         result.unlockBuffer(size);
   1610     }
   1611     return result;
   1612 }
   1613 
   1614 static String8 getSymbolPackage(const String8& symbol, const sp<AaptAssets>& assets, bool pub) {
   1615     ssize_t colon = symbol.find(":", 0);
   1616     if (colon >= 0) {
   1617         return String8(symbol.string(), colon);
   1618     }
   1619     return pub ? assets->getPackage() : assets->getSymbolsPrivatePackage();
   1620 }
   1621 
   1622 static String8 getSymbolName(const String8& symbol) {
   1623     ssize_t colon = symbol.find(":", 0);
   1624     if (colon >= 0) {
   1625         return String8(symbol.string() + colon + 1);
   1626     }
   1627     return symbol;
   1628 }
   1629 
   1630 static String16 getAttributeComment(const sp<AaptAssets>& assets,
   1631                                     const String8& name,
   1632                                     String16* outTypeComment = NULL)
   1633 {
   1634     sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R"));
   1635     if (asym != NULL) {
   1636         //printf("Got R symbols!\n");
   1637         asym = asym->getNestedSymbols().valueFor(String8("attr"));
   1638         if (asym != NULL) {
   1639             //printf("Got attrs symbols! comment %s=%s\n",
   1640             //     name.string(), String8(asym->getComment(name)).string());
   1641             if (outTypeComment != NULL) {
   1642                 *outTypeComment = asym->getTypeComment(name);
   1643             }
   1644             return asym->getComment(name);
   1645         }
   1646     }
   1647     return String16();
   1648 }
   1649 
   1650 static status_t writeLayoutClasses(
   1651     FILE* fp, const sp<AaptAssets>& assets,
   1652     const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
   1653 {
   1654     const char* indentStr = getIndentSpace(indent);
   1655     if (!includePrivate) {
   1656         fprintf(fp, "%s/** @doconly */\n", indentStr);
   1657     }
   1658     fprintf(fp, "%spublic static final class styleable {\n", indentStr);
   1659     indent++;
   1660 
   1661     String16 attr16("attr");
   1662     String16 package16(assets->getPackage());
   1663 
   1664     indentStr = getIndentSpace(indent);
   1665     bool hasErrors = false;
   1666 
   1667     size_t i;
   1668     size_t N = symbols->getNestedSymbols().size();
   1669     for (i=0; i<N; i++) {
   1670         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
   1671         String8 realClassName(symbols->getNestedSymbols().keyAt(i));
   1672         String8 nclassName(flattenSymbol(realClassName));
   1673 
   1674         SortedVector<uint32_t> idents;
   1675         Vector<uint32_t> origOrder;
   1676         Vector<bool> publicFlags;
   1677 
   1678         size_t a;
   1679         size_t NA = nsymbols->getSymbols().size();
   1680         for (a=0; a<NA; a++) {
   1681             const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
   1682             int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
   1683                     ? sym.int32Val : 0;
   1684             bool isPublic = true;
   1685             if (code == 0) {
   1686                 String16 name16(sym.name);
   1687                 uint32_t typeSpecFlags;
   1688                 code = assets->getIncludedResources().identifierForName(
   1689                     name16.string(), name16.size(),
   1690                     attr16.string(), attr16.size(),
   1691                     package16.string(), package16.size(), &typeSpecFlags);
   1692                 if (code == 0) {
   1693                     fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
   1694                             nclassName.string(), sym.name.string());
   1695                     hasErrors = true;
   1696                 }
   1697                 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
   1698             }
   1699             idents.add(code);
   1700             origOrder.add(code);
   1701             publicFlags.add(isPublic);
   1702         }
   1703 
   1704         NA = idents.size();
   1705 
   1706         bool deprecated = false;
   1707 
   1708         String16 comment = symbols->getComment(realClassName);
   1709         fprintf(fp, "%s/** ", indentStr);
   1710         if (comment.size() > 0) {
   1711             String8 cmt(comment);
   1712             fprintf(fp, "%s\n", cmt.string());
   1713             if (strstr(cmt.string(), "@deprecated") != NULL) {
   1714                 deprecated = true;
   1715             }
   1716         } else {
   1717             fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
   1718         }
   1719         bool hasTable = false;
   1720         for (a=0; a<NA; a++) {
   1721             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
   1722             if (pos >= 0) {
   1723                 if (!hasTable) {
   1724                     hasTable = true;
   1725                     fprintf(fp,
   1726                             "%s   <p>Includes the following attributes:</p>\n"
   1727                             "%s   <table>\n"
   1728                             "%s   <colgroup align=\"left\" />\n"
   1729                             "%s   <colgroup align=\"left\" />\n"
   1730                             "%s   <tr><th>Attribute</th><th>Description</th></tr>\n",
   1731                             indentStr,
   1732                             indentStr,
   1733                             indentStr,
   1734                             indentStr,
   1735                             indentStr);
   1736                 }
   1737                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
   1738                 if (!publicFlags.itemAt(a) && !includePrivate) {
   1739                     continue;
   1740                 }
   1741                 String8 name8(sym.name);
   1742                 String16 comment(sym.comment);
   1743                 if (comment.size() <= 0) {
   1744                     comment = getAttributeComment(assets, name8);
   1745                 }
   1746                 if (comment.size() > 0) {
   1747                     const char16_t* p = comment.string();
   1748                     while (*p != 0 && *p != '.') {
   1749                         if (*p == '{') {
   1750                             while (*p != 0 && *p != '}') {
   1751                                 p++;
   1752                             }
   1753                         } else {
   1754                             p++;
   1755                         }
   1756                     }
   1757                     if (*p == '.') {
   1758                         p++;
   1759                     }
   1760                     comment = String16(comment.string(), p-comment.string());
   1761                 }
   1762                 fprintf(fp, "%s   <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n",
   1763                         indentStr, nclassName.string(),
   1764                         flattenSymbol(name8).string(),
   1765                         getSymbolPackage(name8, assets, true).string(),
   1766                         getSymbolName(name8).string(),
   1767                         String8(comment).string());
   1768             }
   1769         }
   1770         if (hasTable) {
   1771             fprintf(fp, "%s   </table>\n", indentStr);
   1772         }
   1773         for (a=0; a<NA; a++) {
   1774             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
   1775             if (pos >= 0) {
   1776                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
   1777                 if (!publicFlags.itemAt(a) && !includePrivate) {
   1778                     continue;
   1779                 }
   1780                 fprintf(fp, "%s   @see #%s_%s\n",
   1781                         indentStr, nclassName.string(),
   1782                         flattenSymbol(sym.name).string());
   1783             }
   1784         }
   1785         fprintf(fp, "%s */\n", getIndentSpace(indent));
   1786 
   1787         if (deprecated) {
   1788             fprintf(fp, "%s@Deprecated\n", indentStr);
   1789         }
   1790 
   1791         fprintf(fp,
   1792                 "%spublic static final int[] %s = {\n"
   1793                 "%s",
   1794                 indentStr, nclassName.string(),
   1795                 getIndentSpace(indent+1));
   1796 
   1797         for (a=0; a<NA; a++) {
   1798             if (a != 0) {
   1799                 if ((a&3) == 0) {
   1800                     fprintf(fp, ",\n%s", getIndentSpace(indent+1));
   1801                 } else {
   1802                     fprintf(fp, ", ");
   1803                 }
   1804             }
   1805             fprintf(fp, "0x%08x", idents[a]);
   1806         }
   1807 
   1808         fprintf(fp, "\n%s};\n", indentStr);
   1809 
   1810         for (a=0; a<NA; a++) {
   1811             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
   1812             if (pos >= 0) {
   1813                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
   1814                 if (!publicFlags.itemAt(a) && !includePrivate) {
   1815                     continue;
   1816                 }
   1817                 String8 name8(sym.name);
   1818                 String16 comment(sym.comment);
   1819                 String16 typeComment;
   1820                 if (comment.size() <= 0) {
   1821                     comment = getAttributeComment(assets, name8, &typeComment);
   1822                 } else {
   1823                     getAttributeComment(assets, name8, &typeComment);
   1824                 }
   1825 
   1826                 uint32_t typeSpecFlags = 0;
   1827                 String16 name16(sym.name);
   1828                 assets->getIncludedResources().identifierForName(
   1829                     name16.string(), name16.size(),
   1830                     attr16.string(), attr16.size(),
   1831                     package16.string(), package16.size(), &typeSpecFlags);
   1832                 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
   1833                 //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
   1834                 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
   1835 
   1836                 bool deprecated = false;
   1837 
   1838                 fprintf(fp, "%s/**\n", indentStr);
   1839                 if (comment.size() > 0) {
   1840                     String8 cmt(comment);
   1841                     fprintf(fp, "%s  <p>\n%s  @attr description\n", indentStr, indentStr);
   1842                     fprintf(fp, "%s  %s\n", indentStr, cmt.string());
   1843                     if (strstr(cmt.string(), "@deprecated") != NULL) {
   1844                         deprecated = true;
   1845                     }
   1846                 } else {
   1847                     fprintf(fp,
   1848                             "%s  <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
   1849                             "%s  attribute's value can be found in the {@link #%s} array.\n",
   1850                             indentStr,
   1851                             getSymbolPackage(name8, assets, pub).string(),
   1852                             getSymbolName(name8).string(),
   1853                             indentStr, nclassName.string());
   1854                 }
   1855                 if (typeComment.size() > 0) {
   1856                     String8 cmt(typeComment);
   1857                     fprintf(fp, "\n\n%s  %s\n", indentStr, cmt.string());
   1858                     if (strstr(cmt.string(), "@deprecated") != NULL) {
   1859                         deprecated = true;
   1860                     }
   1861                 }
   1862                 if (comment.size() > 0) {
   1863                     if (pub) {
   1864                         fprintf(fp,
   1865                                 "%s  <p>This corresponds to the global attribute\n"
   1866                                 "%s  resource symbol {@link %s.R.attr#%s}.\n",
   1867                                 indentStr, indentStr,
   1868                                 getSymbolPackage(name8, assets, true).string(),
   1869                                 getSymbolName(name8).string());
   1870                     } else {
   1871                         fprintf(fp,
   1872                                 "%s  <p>This is a private symbol.\n", indentStr);
   1873                     }
   1874                 }
   1875                 fprintf(fp, "%s  @attr name %s:%s\n", indentStr,
   1876                         getSymbolPackage(name8, assets, pub).string(),
   1877                         getSymbolName(name8).string());
   1878                 fprintf(fp, "%s*/\n", indentStr);
   1879                 if (deprecated) {
   1880                     fprintf(fp, "%s@Deprecated\n", indentStr);
   1881                 }
   1882                 fprintf(fp,
   1883                         "%spublic static final int %s_%s = %d;\n",
   1884                         indentStr, nclassName.string(),
   1885                         flattenSymbol(name8).string(), (int)pos);
   1886             }
   1887         }
   1888     }
   1889 
   1890     indent--;
   1891     fprintf(fp, "%s};\n", getIndentSpace(indent));
   1892     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
   1893 }
   1894 
   1895 static status_t writeTextLayoutClasses(
   1896     FILE* fp, const sp<AaptAssets>& assets,
   1897     const sp<AaptSymbols>& symbols, bool includePrivate)
   1898 {
   1899     String16 attr16("attr");
   1900     String16 package16(assets->getPackage());
   1901 
   1902     bool hasErrors = false;
   1903 
   1904     size_t i;
   1905     size_t N = symbols->getNestedSymbols().size();
   1906     for (i=0; i<N; i++) {
   1907         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
   1908         String8 realClassName(symbols->getNestedSymbols().keyAt(i));
   1909         String8 nclassName(flattenSymbol(realClassName));
   1910 
   1911         SortedVector<uint32_t> idents;
   1912         Vector<uint32_t> origOrder;
   1913         Vector<bool> publicFlags;
   1914 
   1915         size_t a;
   1916         size_t NA = nsymbols->getSymbols().size();
   1917         for (a=0; a<NA; a++) {
   1918             const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
   1919             int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
   1920                     ? sym.int32Val : 0;
   1921             bool isPublic = true;
   1922             if (code == 0) {
   1923                 String16 name16(sym.name);
   1924                 uint32_t typeSpecFlags;
   1925                 code = assets->getIncludedResources().identifierForName(
   1926                     name16.string(), name16.size(),
   1927                     attr16.string(), attr16.size(),
   1928                     package16.string(), package16.size(), &typeSpecFlags);
   1929                 if (code == 0) {
   1930                     fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
   1931                             nclassName.string(), sym.name.string());
   1932                     hasErrors = true;
   1933                 }
   1934                 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
   1935             }
   1936             idents.add(code);
   1937             origOrder.add(code);
   1938             publicFlags.add(isPublic);
   1939         }
   1940 
   1941         NA = idents.size();
   1942 
   1943         fprintf(fp, "int[] styleable %s {", nclassName.string());
   1944 
   1945         for (a=0; a<NA; a++) {
   1946             if (a != 0) {
   1947                 fprintf(fp, ",");
   1948             }
   1949             fprintf(fp, " 0x%08x", idents[a]);
   1950         }
   1951 
   1952         fprintf(fp, " }\n");
   1953 
   1954         for (a=0; a<NA; a++) {
   1955             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
   1956             if (pos >= 0) {
   1957                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
   1958                 if (!publicFlags.itemAt(a) && !includePrivate) {
   1959                     continue;
   1960                 }
   1961                 String8 name8(sym.name);
   1962                 String16 comment(sym.comment);
   1963                 String16 typeComment;
   1964                 if (comment.size() <= 0) {
   1965                     comment = getAttributeComment(assets, name8, &typeComment);
   1966                 } else {
   1967                     getAttributeComment(assets, name8, &typeComment);
   1968                 }
   1969 
   1970                 uint32_t typeSpecFlags = 0;
   1971                 String16 name16(sym.name);
   1972                 assets->getIncludedResources().identifierForName(
   1973                     name16.string(), name16.size(),
   1974                     attr16.string(), attr16.size(),
   1975                     package16.string(), package16.size(), &typeSpecFlags);
   1976                 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
   1977                 //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
   1978                 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
   1979 
   1980                 fprintf(fp,
   1981                         "int styleable %s_%s %d\n",
   1982                         nclassName.string(),
   1983                         flattenSymbol(name8).string(), (int)pos);
   1984             }
   1985         }
   1986     }
   1987 
   1988     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
   1989 }
   1990 
   1991 static status_t writeSymbolClass(
   1992     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
   1993     const sp<AaptSymbols>& symbols, const String8& className, int indent,
   1994     bool nonConstantId)
   1995 {
   1996     fprintf(fp, "%spublic %sfinal class %s {\n",
   1997             getIndentSpace(indent),
   1998             indent != 0 ? "static " : "", className.string());
   1999     indent++;
   2000 
   2001     size_t i;
   2002     status_t err = NO_ERROR;
   2003 
   2004     const char * id_format = nonConstantId ?
   2005             "%spublic static int %s=0x%08x;\n" :
   2006             "%spublic static final int %s=0x%08x;\n";
   2007 
   2008     size_t N = symbols->getSymbols().size();
   2009     for (i=0; i<N; i++) {
   2010         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
   2011         if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
   2012             continue;
   2013         }
   2014         if (!assets->isJavaSymbol(sym, includePrivate)) {
   2015             continue;
   2016         }
   2017         String8 name8(sym.name);
   2018         String16 comment(sym.comment);
   2019         bool haveComment = false;
   2020         bool deprecated = false;
   2021         if (comment.size() > 0) {
   2022             haveComment = true;
   2023             String8 cmt(comment);
   2024             fprintf(fp,
   2025                     "%s/** %s\n",
   2026                     getIndentSpace(indent), cmt.string());
   2027             if (strstr(cmt.string(), "@deprecated") != NULL) {
   2028                 deprecated = true;
   2029             }
   2030         } else if (sym.isPublic && !includePrivate) {
   2031             sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
   2032                 assets->getPackage().string(), className.string(),
   2033                 String8(sym.name).string());
   2034         }
   2035         String16 typeComment(sym.typeComment);
   2036         if (typeComment.size() > 0) {
   2037             String8 cmt(typeComment);
   2038             if (!haveComment) {
   2039                 haveComment = true;
   2040                 fprintf(fp,
   2041                         "%s/** %s\n", getIndentSpace(indent), cmt.string());
   2042             } else {
   2043                 fprintf(fp,
   2044                         "%s %s\n", getIndentSpace(indent), cmt.string());
   2045             }
   2046             if (strstr(cmt.string(), "@deprecated") != NULL) {
   2047                 deprecated = true;
   2048             }
   2049         }
   2050         if (haveComment) {
   2051             fprintf(fp,"%s */\n", getIndentSpace(indent));
   2052         }
   2053         if (deprecated) {
   2054             fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
   2055         }
   2056         fprintf(fp, id_format,
   2057                 getIndentSpace(indent),
   2058                 flattenSymbol(name8).string(), (int)sym.int32Val);
   2059     }
   2060 
   2061     for (i=0; i<N; i++) {
   2062         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
   2063         if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) {
   2064             continue;
   2065         }
   2066         if (!assets->isJavaSymbol(sym, includePrivate)) {
   2067             continue;
   2068         }
   2069         String8 name8(sym.name);
   2070         String16 comment(sym.comment);
   2071         bool deprecated = false;
   2072         if (comment.size() > 0) {
   2073             String8 cmt(comment);
   2074             fprintf(fp,
   2075                     "%s/** %s\n"
   2076                      "%s */\n",
   2077                     getIndentSpace(indent), cmt.string(),
   2078                     getIndentSpace(indent));
   2079             if (strstr(cmt.string(), "@deprecated") != NULL) {
   2080                 deprecated = true;
   2081             }
   2082         } else if (sym.isPublic && !includePrivate) {
   2083             sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
   2084                 assets->getPackage().string(), className.string(),
   2085                 String8(sym.name).string());
   2086         }
   2087         if (deprecated) {
   2088             fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
   2089         }
   2090         fprintf(fp, "%spublic static final String %s=\"%s\";\n",
   2091                 getIndentSpace(indent),
   2092                 flattenSymbol(name8).string(), sym.stringVal.string());
   2093     }
   2094 
   2095     sp<AaptSymbols> styleableSymbols;
   2096 
   2097     N = symbols->getNestedSymbols().size();
   2098     for (i=0; i<N; i++) {
   2099         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
   2100         String8 nclassName(symbols->getNestedSymbols().keyAt(i));
   2101         if (nclassName == "styleable") {
   2102             styleableSymbols = nsymbols;
   2103         } else {
   2104             err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
   2105         }
   2106         if (err != NO_ERROR) {
   2107             return err;
   2108         }
   2109     }
   2110 
   2111     if (styleableSymbols != NULL) {
   2112         err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate);
   2113         if (err != NO_ERROR) {
   2114             return err;
   2115         }
   2116     }
   2117 
   2118     indent--;
   2119     fprintf(fp, "%s}\n", getIndentSpace(indent));
   2120     return NO_ERROR;
   2121 }
   2122 
   2123 static status_t writeTextSymbolClass(
   2124     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
   2125     const sp<AaptSymbols>& symbols, const String8& className)
   2126 {
   2127     size_t i;
   2128     status_t err = NO_ERROR;
   2129 
   2130     size_t N = symbols->getSymbols().size();
   2131     for (i=0; i<N; i++) {
   2132         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
   2133         if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
   2134             continue;
   2135         }
   2136 
   2137         if (!assets->isJavaSymbol(sym, includePrivate)) {
   2138             continue;
   2139         }
   2140 
   2141         String8 name8(sym.name);
   2142         fprintf(fp, "int %s %s 0x%08x\n",
   2143                 className.string(),
   2144                 flattenSymbol(name8).string(), (int)sym.int32Val);
   2145     }
   2146 
   2147     N = symbols->getNestedSymbols().size();
   2148     for (i=0; i<N; i++) {
   2149         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
   2150         String8 nclassName(symbols->getNestedSymbols().keyAt(i));
   2151         if (nclassName == "styleable") {
   2152             err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate);
   2153         } else {
   2154             err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName);
   2155         }
   2156         if (err != NO_ERROR) {
   2157             return err;
   2158         }
   2159     }
   2160 
   2161     return NO_ERROR;
   2162 }
   2163 
   2164 status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
   2165     const String8& package, bool includePrivate)
   2166 {
   2167     if (!bundle->getRClassDir()) {
   2168         return NO_ERROR;
   2169     }
   2170 
   2171     const char* textSymbolsDest = bundle->getOutputTextSymbols();
   2172 
   2173     String8 R("R");
   2174     const size_t N = assets->getSymbols().size();
   2175     for (size_t i=0; i<N; i++) {
   2176         sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
   2177         String8 className(assets->getSymbols().keyAt(i));
   2178         String8 dest(bundle->getRClassDir());
   2179 
   2180         if (bundle->getMakePackageDirs()) {
   2181             String8 pkg(package);
   2182             const char* last = pkg.string();
   2183             const char* s = last-1;
   2184             do {
   2185                 s++;
   2186                 if (s > last && (*s == '.' || *s == 0)) {
   2187                     String8 part(last, s-last);
   2188                     dest.appendPath(part);
   2189 #ifdef HAVE_MS_C_RUNTIME
   2190                     _mkdir(dest.string());
   2191 #else
   2192                     mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
   2193 #endif
   2194                     last = s+1;
   2195                 }
   2196             } while (*s);
   2197         }
   2198         dest.appendPath(className);
   2199         dest.append(".java");
   2200         FILE* fp = fopen(dest.string(), "w+");
   2201         if (fp == NULL) {
   2202             fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
   2203                     dest.string(), strerror(errno));
   2204             return UNKNOWN_ERROR;
   2205         }
   2206         if (bundle->getVerbose()) {
   2207             printf("  Writing symbols for class %s.\n", className.string());
   2208         }
   2209 
   2210         fprintf(fp,
   2211             "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
   2212             " *\n"
   2213             " * This class was automatically generated by the\n"
   2214             " * aapt tool from the resource data it found.  It\n"
   2215             " * should not be modified by hand.\n"
   2216             " */\n"
   2217             "\n"
   2218             "package %s;\n\n", package.string());
   2219 
   2220         status_t err = writeSymbolClass(fp, assets, includePrivate, symbols,
   2221                 className, 0, bundle->getNonConstantId());
   2222         if (err != NO_ERROR) {
   2223             return err;
   2224         }
   2225         fclose(fp);
   2226 
   2227         if (textSymbolsDest != NULL && R == className) {
   2228             String8 textDest(textSymbolsDest);
   2229             textDest.appendPath(className);
   2230             textDest.append(".txt");
   2231 
   2232             FILE* fp = fopen(textDest.string(), "w+");
   2233             if (fp == NULL) {
   2234                 fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n",
   2235                         textDest.string(), strerror(errno));
   2236                 return UNKNOWN_ERROR;
   2237             }
   2238             if (bundle->getVerbose()) {
   2239                 printf("  Writing text symbols for class %s.\n", className.string());
   2240             }
   2241 
   2242             status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols,
   2243                     className);
   2244             if (err != NO_ERROR) {
   2245                 return err;
   2246             }
   2247             fclose(fp);
   2248         }
   2249 
   2250         // If we were asked to generate a dependency file, we'll go ahead and add this R.java
   2251         // as a target in the dependency file right next to it.
   2252         if (bundle->getGenDependencies() && R == className) {
   2253             // Add this R.java to the dependency file
   2254             String8 dependencyFile(bundle->getRClassDir());
   2255             dependencyFile.appendPath("R.java.d");
   2256 
   2257             FILE *fp = fopen(dependencyFile.string(), "a");
   2258             fprintf(fp,"%s \\\n", dest.string());
   2259             fclose(fp);
   2260         }
   2261     }
   2262 
   2263     return NO_ERROR;
   2264 }
   2265 
   2266 
   2267 class ProguardKeepSet
   2268 {
   2269 public:
   2270     // { rule --> { file locations } }
   2271     KeyedVector<String8, SortedVector<String8> > rules;
   2272 
   2273     void add(const String8& rule, const String8& where);
   2274 };
   2275 
   2276 void ProguardKeepSet::add(const String8& rule, const String8& where)
   2277 {
   2278     ssize_t index = rules.indexOfKey(rule);
   2279     if (index < 0) {
   2280         index = rules.add(rule, SortedVector<String8>());
   2281     }
   2282     rules.editValueAt(index).add(where);
   2283 }
   2284 
   2285 void
   2286 addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
   2287         const char* pkg, const String8& srcName, int line)
   2288 {
   2289     String8 className(inClassName);
   2290     if (pkg != NULL) {
   2291         // asdf     --> package.asdf
   2292         // .asdf  .a.b  --> package.asdf package.a.b
   2293         // asdf.adsf --> asdf.asdf
   2294         const char* p = className.string();
   2295         const char* q = strchr(p, '.');
   2296         if (p == q) {
   2297             className = pkg;
   2298             className.append(inClassName);
   2299         } else if (q == NULL) {
   2300             className = pkg;
   2301             className.append(".");
   2302             className.append(inClassName);
   2303         }
   2304     }
   2305 
   2306     String8 rule("-keep class ");
   2307     rule += className;
   2308     rule += " { <init>(...); }";
   2309 
   2310     String8 location("view ");
   2311     location += srcName;
   2312     char lineno[20];
   2313     sprintf(lineno, ":%d", line);
   2314     location += lineno;
   2315 
   2316     keep->add(rule, location);
   2317 }
   2318 
   2319 void
   2320 addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName,
   2321         const char* pkg, const String8& srcName, int line)
   2322 {
   2323     String8 rule("-keepclassmembers class * { *** ");
   2324     rule += memberName;
   2325     rule += "(...); }";
   2326 
   2327     String8 location("onClick ");
   2328     location += srcName;
   2329     char lineno[20];
   2330     sprintf(lineno, ":%d", line);
   2331     location += lineno;
   2332 
   2333     keep->add(rule, location);
   2334 }
   2335 
   2336 status_t
   2337 writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
   2338 {
   2339     status_t err;
   2340     ResXMLTree tree;
   2341     size_t len;
   2342     ResXMLTree::event_code_t code;
   2343     int depth = 0;
   2344     bool inApplication = false;
   2345     String8 error;
   2346     sp<AaptGroup> assGroup;
   2347     sp<AaptFile> assFile;
   2348     String8 pkg;
   2349 
   2350     // First, look for a package file to parse.  This is required to
   2351     // be able to generate the resource information.
   2352     assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml"));
   2353     if (assGroup == NULL) {
   2354         fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
   2355         return -1;
   2356     }
   2357 
   2358     if (assGroup->getFiles().size() != 1) {
   2359         fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
   2360                 assGroup->getFiles().valueAt(0)->getPrintableSource().string());
   2361     }
   2362 
   2363     assFile = assGroup->getFiles().valueAt(0);
   2364 
   2365     err = parseXMLResource(assFile, &tree);
   2366     if (err != NO_ERROR) {
   2367         return err;
   2368     }
   2369 
   2370     tree.restart();
   2371 
   2372     while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   2373         if (code == ResXMLTree::END_TAG) {
   2374             if (/* name == "Application" && */ depth == 2) {
   2375                 inApplication = false;
   2376             }
   2377             depth--;
   2378             continue;
   2379         }
   2380         if (code != ResXMLTree::START_TAG) {
   2381             continue;
   2382         }
   2383         depth++;
   2384         String8 tag(tree.getElementName(&len));
   2385         // printf("Depth %d tag %s\n", depth, tag.string());
   2386         bool keepTag = false;
   2387         if (depth == 1) {
   2388             if (tag != "manifest") {
   2389                 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
   2390                 return -1;
   2391             }
   2392             pkg = getAttribute(tree, NULL, "package", NULL);
   2393         } else if (depth == 2) {
   2394             if (tag == "application") {
   2395                 inApplication = true;
   2396                 keepTag = true;
   2397 
   2398                 String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
   2399                         "backupAgent", &error);
   2400                 if (agent.length() > 0) {
   2401                     addProguardKeepRule(keep, agent, pkg.string(),
   2402                             assFile->getPrintableSource(), tree.getLineNumber());
   2403                 }
   2404             } else if (tag == "instrumentation") {
   2405                 keepTag = true;
   2406             }
   2407         }
   2408         if (!keepTag && inApplication && depth == 3) {
   2409             if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
   2410                 keepTag = true;
   2411             }
   2412         }
   2413         if (keepTag) {
   2414             String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
   2415                     "name", &error);
   2416             if (error != "") {
   2417                 fprintf(stderr, "ERROR: %s\n", error.string());
   2418                 return -1;
   2419             }
   2420             if (name.length() > 0) {
   2421                 addProguardKeepRule(keep, name, pkg.string(),
   2422                         assFile->getPrintableSource(), tree.getLineNumber());
   2423             }
   2424         }
   2425     }
   2426 
   2427     return NO_ERROR;
   2428 }
   2429 
   2430 struct NamespaceAttributePair {
   2431     const char* ns;
   2432     const char* attr;
   2433 
   2434     NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {}
   2435     NamespaceAttributePair() : ns(NULL), attr(NULL) {}
   2436 };
   2437 
   2438 status_t
   2439 writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
   2440         const char* startTag, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs)
   2441 {
   2442     status_t err;
   2443     ResXMLTree tree;
   2444     size_t len;
   2445     ResXMLTree::event_code_t code;
   2446 
   2447     err = parseXMLResource(layoutFile, &tree);
   2448     if (err != NO_ERROR) {
   2449         return err;
   2450     }
   2451 
   2452     tree.restart();
   2453 
   2454     if (startTag != NULL) {
   2455         bool haveStart = false;
   2456         while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   2457             if (code != ResXMLTree::START_TAG) {
   2458                 continue;
   2459             }
   2460             String8 tag(tree.getElementName(&len));
   2461             if (tag == startTag) {
   2462                 haveStart = true;
   2463             }
   2464             break;
   2465         }
   2466         if (!haveStart) {
   2467             return NO_ERROR;
   2468         }
   2469     }
   2470 
   2471     while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   2472         if (code != ResXMLTree::START_TAG) {
   2473             continue;
   2474         }
   2475         String8 tag(tree.getElementName(&len));
   2476 
   2477         // If there is no '.', we'll assume that it's one of the built in names.
   2478         if (strchr(tag.string(), '.')) {
   2479             addProguardKeepRule(keep, tag, NULL,
   2480                     layoutFile->getPrintableSource(), tree.getLineNumber());
   2481         } else if (tagAttrPairs != NULL) {
   2482             ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
   2483             if (tagIndex >= 0) {
   2484                 const Vector<NamespaceAttributePair>& nsAttrVector = tagAttrPairs->valueAt(tagIndex);
   2485                 for (size_t i = 0; i < nsAttrVector.size(); i++) {
   2486                     const NamespaceAttributePair& nsAttr = nsAttrVector[i];
   2487 
   2488                     ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
   2489                     if (attrIndex < 0) {
   2490                         // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
   2491                         //        layoutFile->getPrintableSource().string(), tree.getLineNumber(),
   2492                         //        tag.string(), nsAttr.ns, nsAttr.attr);
   2493                     } else {
   2494                         size_t len;
   2495                         addProguardKeepRule(keep,
   2496                                             String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
   2497                                             layoutFile->getPrintableSource(), tree.getLineNumber());
   2498                     }
   2499                 }
   2500             }
   2501         }
   2502         ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick");
   2503         if (attrIndex >= 0) {
   2504             size_t len;
   2505             addProguardKeepMethodRule(keep,
   2506                                 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
   2507                                 layoutFile->getPrintableSource(), tree.getLineNumber());
   2508         }
   2509     }
   2510 
   2511     return NO_ERROR;
   2512 }
   2513 
   2514 static void addTagAttrPair(KeyedVector<String8, Vector<NamespaceAttributePair> >* dest,
   2515         const char* tag, const char* ns, const char* attr) {
   2516     String8 tagStr(tag);
   2517     ssize_t index = dest->indexOfKey(tagStr);
   2518 
   2519     if (index < 0) {
   2520         Vector<NamespaceAttributePair> vector;
   2521         vector.add(NamespaceAttributePair(ns, attr));
   2522         dest->add(tagStr, vector);
   2523     } else {
   2524         dest->editValueAt(index).add(NamespaceAttributePair(ns, attr));
   2525     }
   2526 }
   2527 
   2528 status_t
   2529 writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
   2530 {
   2531     status_t err;
   2532 
   2533     // tag:attribute pairs that should be checked in layout files.
   2534     KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs;
   2535     addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
   2536     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
   2537     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
   2538 
   2539     // tag:attribute pairs that should be checked in xml files.
   2540     KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs;
   2541     addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
   2542     addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
   2543 
   2544     const Vector<sp<AaptDir> >& dirs = assets->resDirs();
   2545     const size_t K = dirs.size();
   2546     for (size_t k=0; k<K; k++) {
   2547         const sp<AaptDir>& d = dirs.itemAt(k);
   2548         const String8& dirName = d->getLeaf();
   2549         const char* startTag = NULL;
   2550         const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL;
   2551         if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
   2552             tagAttrPairs = &kLayoutTagAttrPairs;
   2553         } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
   2554             startTag = "PreferenceScreen";
   2555             tagAttrPairs = &kXmlTagAttrPairs;
   2556         } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
   2557             startTag = "menu";
   2558             tagAttrPairs = NULL;
   2559         } else {
   2560             continue;
   2561         }
   2562 
   2563         const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
   2564         const size_t N = groups.size();
   2565         for (size_t i=0; i<N; i++) {
   2566             const sp<AaptGroup>& group = groups.valueAt(i);
   2567             const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
   2568             const size_t M = files.size();
   2569             for (size_t j=0; j<M; j++) {
   2570                 err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
   2571                 if (err < 0) {
   2572                     return err;
   2573                 }
   2574             }
   2575         }
   2576     }
   2577     // Handle the overlays
   2578     sp<AaptAssets> overlay = assets->getOverlay();
   2579     if (overlay.get()) {
   2580         return writeProguardForLayouts(keep, overlay);
   2581     }
   2582 
   2583     return NO_ERROR;
   2584 }
   2585 
   2586 status_t
   2587 writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
   2588 {
   2589     status_t err = -1;
   2590 
   2591     if (!bundle->getProguardFile()) {
   2592         return NO_ERROR;
   2593     }
   2594 
   2595     ProguardKeepSet keep;
   2596 
   2597     err = writeProguardForAndroidManifest(&keep, assets);
   2598     if (err < 0) {
   2599         return err;
   2600     }
   2601 
   2602     err = writeProguardForLayouts(&keep, assets);
   2603     if (err < 0) {
   2604         return err;
   2605     }
   2606 
   2607     FILE* fp = fopen(bundle->getProguardFile(), "w+");
   2608     if (fp == NULL) {
   2609         fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
   2610                 bundle->getProguardFile(), strerror(errno));
   2611         return UNKNOWN_ERROR;
   2612     }
   2613 
   2614     const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules;
   2615     const size_t N = rules.size();
   2616     for (size_t i=0; i<N; i++) {
   2617         const SortedVector<String8>& locations = rules.valueAt(i);
   2618         const size_t M = locations.size();
   2619         for (size_t j=0; j<M; j++) {
   2620             fprintf(fp, "# %s\n", locations.itemAt(j).string());
   2621         }
   2622         fprintf(fp, "%s\n\n", rules.keyAt(i).string());
   2623     }
   2624     fclose(fp);
   2625 
   2626     return err;
   2627 }
   2628 
   2629 // Loops through the string paths and writes them to the file pointer
   2630 // Each file path is written on its own line with a terminating backslash.
   2631 status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
   2632 {
   2633     status_t deps = -1;
   2634     for (size_t file_i = 0; file_i < files->size(); ++file_i) {
   2635         // Add the full file path to the dependency file
   2636         fprintf(fp, "%s \\\n", files->itemAt(file_i).string());
   2637         deps++;
   2638     }
   2639     return deps;
   2640 }
   2641 
   2642 status_t
   2643 writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw)
   2644 {
   2645     status_t deps = -1;
   2646     deps += writePathsToFile(assets->getFullResPaths(), fp);
   2647     if (includeRaw) {
   2648         deps += writePathsToFile(assets->getFullAssetPaths(), fp);
   2649     }
   2650     return deps;
   2651 }
   2652