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