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