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