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