Resource.cpp revision 7c3bef85dab709326e052fec1fdc8b32e47c811e
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->getWantUTF16()
851            && bundle->isMinSdkAtLeast(SDK_FROYO)) {
852        xmlFlags |= XML_COMPILE_UTF8;
853    }
854
855    // --------------------------------------------------------------
856    // First, gather all resource information.
857    // --------------------------------------------------------------
858
859    // resType -> leafName -> group
860    KeyedVector<String8, sp<ResourceTypeSet> > *resources =
861            new KeyedVector<String8, sp<ResourceTypeSet> >;
862    collect_files(assets, resources);
863
864    sp<ResourceTypeSet> drawables;
865    sp<ResourceTypeSet> layouts;
866    sp<ResourceTypeSet> anims;
867    sp<ResourceTypeSet> animators;
868    sp<ResourceTypeSet> interpolators;
869    sp<ResourceTypeSet> xmls;
870    sp<ResourceTypeSet> raws;
871    sp<ResourceTypeSet> colors;
872    sp<ResourceTypeSet> menus;
873    sp<ResourceTypeSet> mipmaps;
874
875    ASSIGN_IT(drawable);
876    ASSIGN_IT(layout);
877    ASSIGN_IT(anim);
878    ASSIGN_IT(animator);
879    ASSIGN_IT(interpolator);
880    ASSIGN_IT(xml);
881    ASSIGN_IT(raw);
882    ASSIGN_IT(color);
883    ASSIGN_IT(menu);
884    ASSIGN_IT(mipmap);
885
886    assets->setResources(resources);
887    // now go through any resource overlays and collect their files
888    sp<AaptAssets> current = assets->getOverlay();
889    while(current.get()) {
890        KeyedVector<String8, sp<ResourceTypeSet> > *resources =
891                new KeyedVector<String8, sp<ResourceTypeSet> >;
892        current->setResources(resources);
893        collect_files(current, resources);
894        current = current->getOverlay();
895    }
896    // apply the overlay files to the base set
897    if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
898            !applyFileOverlay(bundle, assets, &layouts, "layout") ||
899            !applyFileOverlay(bundle, assets, &anims, "anim") ||
900            !applyFileOverlay(bundle, assets, &animators, "animator") ||
901            !applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
902            !applyFileOverlay(bundle, assets, &xmls, "xml") ||
903            !applyFileOverlay(bundle, assets, &raws, "raw") ||
904            !applyFileOverlay(bundle, assets, &colors, "color") ||
905            !applyFileOverlay(bundle, assets, &menus, "menu") ||
906            !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
907        return UNKNOWN_ERROR;
908    }
909
910    bool hasErrors = false;
911
912    if (drawables != NULL) {
913        if (bundle->getOutputAPKFile() != NULL) {
914            err = preProcessImages(bundle, assets, drawables, "drawable");
915        }
916        if (err == NO_ERROR) {
917            err = makeFileResources(bundle, assets, &table, drawables, "drawable");
918            if (err != NO_ERROR) {
919                hasErrors = true;
920            }
921        } else {
922            hasErrors = true;
923        }
924    }
925
926    if (mipmaps != NULL) {
927        if (bundle->getOutputAPKFile() != NULL) {
928            err = preProcessImages(bundle, assets, mipmaps, "mipmap");
929        }
930        if (err == NO_ERROR) {
931            err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
932            if (err != NO_ERROR) {
933                hasErrors = true;
934            }
935        } else {
936            hasErrors = true;
937        }
938    }
939
940    if (layouts != NULL) {
941        err = makeFileResources(bundle, assets, &table, layouts, "layout");
942        if (err != NO_ERROR) {
943            hasErrors = true;
944        }
945    }
946
947    if (anims != NULL) {
948        err = makeFileResources(bundle, assets, &table, anims, "anim");
949        if (err != NO_ERROR) {
950            hasErrors = true;
951        }
952    }
953
954    if (animators != NULL) {
955        err = makeFileResources(bundle, assets, &table, animators, "animator");
956        if (err != NO_ERROR) {
957            hasErrors = true;
958        }
959    }
960
961    if (interpolators != NULL) {
962        err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
963        if (err != NO_ERROR) {
964            hasErrors = true;
965        }
966    }
967
968    if (xmls != NULL) {
969        err = makeFileResources(bundle, assets, &table, xmls, "xml");
970        if (err != NO_ERROR) {
971            hasErrors = true;
972        }
973    }
974
975    if (raws != NULL) {
976        err = makeFileResources(bundle, assets, &table, raws, "raw");
977        if (err != NO_ERROR) {
978            hasErrors = true;
979        }
980    }
981
982    // compile resources
983    current = assets;
984    while(current.get()) {
985        KeyedVector<String8, sp<ResourceTypeSet> > *resources =
986                current->getResources();
987
988        ssize_t index = resources->indexOfKey(String8("values"));
989        if (index >= 0) {
990            ResourceDirIterator it(resources->valueAt(index), String8("values"));
991            ssize_t res;
992            while ((res=it.next()) == NO_ERROR) {
993                sp<AaptFile> file = it.getFile();
994                res = compileResourceFile(bundle, assets, file, it.getParams(),
995                                          (current!=assets), &table);
996                if (res != NO_ERROR) {
997                    hasErrors = true;
998                }
999            }
1000        }
1001        current = current->getOverlay();
1002    }
1003
1004    if (colors != NULL) {
1005        err = makeFileResources(bundle, assets, &table, colors, "color");
1006        if (err != NO_ERROR) {
1007            hasErrors = true;
1008        }
1009    }
1010
1011    if (menus != NULL) {
1012        err = makeFileResources(bundle, assets, &table, menus, "menu");
1013        if (err != NO_ERROR) {
1014            hasErrors = true;
1015        }
1016    }
1017
1018    // --------------------------------------------------------------------
1019    // Assignment of resource IDs and initial generation of resource table.
1020    // --------------------------------------------------------------------
1021
1022    if (table.hasResources()) {
1023        sp<AaptFile> resFile(getResourceFile(assets));
1024        if (resFile == NULL) {
1025            fprintf(stderr, "Error: unable to generate entry for resource data\n");
1026            return UNKNOWN_ERROR;
1027        }
1028
1029        err = table.assignResourceIds();
1030        if (err < NO_ERROR) {
1031            return err;
1032        }
1033    }
1034
1035    // --------------------------------------------------------------
1036    // Finally, we can now we can compile XML files, which may reference
1037    // resources.
1038    // --------------------------------------------------------------
1039
1040    if (layouts != NULL) {
1041        ResourceDirIterator it(layouts, String8("layout"));
1042        while ((err=it.next()) == NO_ERROR) {
1043            String8 src = it.getFile()->getPrintableSource();
1044            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1045            if (err == NO_ERROR) {
1046                ResXMLTree block;
1047                block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
1048                checkForIds(src, block);
1049            } else {
1050                hasErrors = true;
1051            }
1052        }
1053
1054        if (err < NO_ERROR) {
1055            hasErrors = true;
1056        }
1057        err = NO_ERROR;
1058    }
1059
1060    if (anims != NULL) {
1061        ResourceDirIterator it(anims, String8("anim"));
1062        while ((err=it.next()) == NO_ERROR) {
1063            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1064            if (err != NO_ERROR) {
1065                hasErrors = true;
1066            }
1067        }
1068
1069        if (err < NO_ERROR) {
1070            hasErrors = true;
1071        }
1072        err = NO_ERROR;
1073    }
1074
1075    if (animators != NULL) {
1076        ResourceDirIterator it(animators, String8("animator"));
1077        while ((err=it.next()) == NO_ERROR) {
1078            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1079            if (err != NO_ERROR) {
1080                hasErrors = true;
1081            }
1082        }
1083
1084        if (err < NO_ERROR) {
1085            hasErrors = true;
1086        }
1087        err = NO_ERROR;
1088    }
1089
1090    if (interpolators != NULL) {
1091        ResourceDirIterator it(interpolators, String8("interpolator"));
1092        while ((err=it.next()) == NO_ERROR) {
1093            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1094            if (err != NO_ERROR) {
1095                hasErrors = true;
1096            }
1097        }
1098
1099        if (err < NO_ERROR) {
1100            hasErrors = true;
1101        }
1102        err = NO_ERROR;
1103    }
1104
1105    if (xmls != NULL) {
1106        ResourceDirIterator it(xmls, String8("xml"));
1107        while ((err=it.next()) == NO_ERROR) {
1108            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1109            if (err != NO_ERROR) {
1110                hasErrors = true;
1111            }
1112        }
1113
1114        if (err < NO_ERROR) {
1115            hasErrors = true;
1116        }
1117        err = NO_ERROR;
1118    }
1119
1120    if (drawables != NULL) {
1121        err = postProcessImages(assets, &table, drawables);
1122        if (err != NO_ERROR) {
1123            hasErrors = true;
1124        }
1125    }
1126
1127    if (colors != NULL) {
1128        ResourceDirIterator it(colors, String8("color"));
1129        while ((err=it.next()) == NO_ERROR) {
1130          err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1131            if (err != NO_ERROR) {
1132                hasErrors = true;
1133            }
1134        }
1135
1136        if (err < NO_ERROR) {
1137            hasErrors = true;
1138        }
1139        err = NO_ERROR;
1140    }
1141
1142    if (menus != NULL) {
1143        ResourceDirIterator it(menus, String8("menu"));
1144        while ((err=it.next()) == NO_ERROR) {
1145            String8 src = it.getFile()->getPrintableSource();
1146            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1147            if (err != NO_ERROR) {
1148                hasErrors = true;
1149            }
1150            ResXMLTree block;
1151            block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
1152            checkForIds(src, block);
1153        }
1154
1155        if (err < NO_ERROR) {
1156            hasErrors = true;
1157        }
1158        err = NO_ERROR;
1159    }
1160
1161    if (table.validateLocalizations()) {
1162        hasErrors = true;
1163    }
1164
1165    if (hasErrors) {
1166        return UNKNOWN_ERROR;
1167    }
1168
1169    const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
1170    String8 manifestPath(manifestFile->getPrintableSource());
1171
1172    // Generate final compiled manifest file.
1173    manifestFile->clearData();
1174    sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
1175    if (manifestTree == NULL) {
1176        return UNKNOWN_ERROR;
1177    }
1178    err = massageManifest(bundle, manifestTree);
1179    if (err < NO_ERROR) {
1180        return err;
1181    }
1182    err = compileXmlFile(assets, manifestTree, manifestFile, &table);
1183    if (err < NO_ERROR) {
1184        return err;
1185    }
1186
1187    //block.restart();
1188    //printXMLBlock(&block);
1189
1190    // --------------------------------------------------------------
1191    // Generate the final resource table.
1192    // Re-flatten because we may have added new resource IDs
1193    // --------------------------------------------------------------
1194
1195    ResTable finalResTable;
1196    sp<AaptFile> resFile;
1197
1198    if (table.hasResources()) {
1199        sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
1200        err = table.addSymbols(symbols);
1201        if (err < NO_ERROR) {
1202            return err;
1203        }
1204
1205        resFile = getResourceFile(assets);
1206        if (resFile == NULL) {
1207            fprintf(stderr, "Error: unable to generate entry for resource data\n");
1208            return UNKNOWN_ERROR;
1209        }
1210
1211        err = table.flatten(bundle, resFile);
1212        if (err < NO_ERROR) {
1213            return err;
1214        }
1215
1216        if (bundle->getPublicOutputFile()) {
1217            FILE* fp = fopen(bundle->getPublicOutputFile(), "w+");
1218            if (fp == NULL) {
1219                fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n",
1220                        (const char*)bundle->getPublicOutputFile(), strerror(errno));
1221                return UNKNOWN_ERROR;
1222            }
1223            if (bundle->getVerbose()) {
1224                printf("  Writing public definitions to %s.\n", bundle->getPublicOutputFile());
1225            }
1226            table.writePublicDefinitions(String16(assets->getPackage()), fp);
1227            fclose(fp);
1228        }
1229
1230        // Read resources back in,
1231        finalResTable.add(resFile->getData(), resFile->getSize(), NULL);
1232
1233#if 0
1234        NOISY(
1235              printf("Generated resources:\n");
1236              finalResTable.print();
1237        )
1238#endif
1239    }
1240
1241    // Perform a basic validation of the manifest file.  This time we
1242    // parse it with the comments intact, so that we can use them to
1243    // generate java docs...  so we are not going to write this one
1244    // back out to the final manifest data.
1245    sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(),
1246            manifestFile->getGroupEntry(),
1247            manifestFile->getResourceType());
1248    err = compileXmlFile(assets, manifestFile,
1249            outManifestFile, &table,
1250            XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
1251            | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
1252    if (err < NO_ERROR) {
1253        return err;
1254    }
1255    ResXMLTree block;
1256    block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true);
1257    String16 manifest16("manifest");
1258    String16 permission16("permission");
1259    String16 permission_group16("permission-group");
1260    String16 uses_permission16("uses-permission");
1261    String16 instrumentation16("instrumentation");
1262    String16 application16("application");
1263    String16 provider16("provider");
1264    String16 service16("service");
1265    String16 receiver16("receiver");
1266    String16 activity16("activity");
1267    String16 action16("action");
1268    String16 category16("category");
1269    String16 data16("scheme");
1270    const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
1271        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
1272    const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
1273        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
1274    const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz"
1275        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$";
1276    const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz"
1277        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:";
1278    const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz"
1279        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;";
1280    const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz"
1281        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+";
1282    const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz"
1283        "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
1284    ResXMLTree::event_code_t code;
1285    sp<AaptSymbols> permissionSymbols;
1286    sp<AaptSymbols> permissionGroupSymbols;
1287    while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1288           && code > ResXMLTree::BAD_DOCUMENT) {
1289        if (code == ResXMLTree::START_TAG) {
1290            size_t len;
1291            if (block.getElementNamespace(&len) != NULL) {
1292                continue;
1293            }
1294            if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) {
1295                if (validateAttr(manifestPath, finalResTable, block, NULL, "package",
1296                                 packageIdentChars, true) != ATTR_OKAY) {
1297                    hasErrors = true;
1298                }
1299                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1300                                 "sharedUserId", packageIdentChars, false) != ATTR_OKAY) {
1301                    hasErrors = true;
1302                }
1303            } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0
1304                    || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) {
1305                const bool isGroup = strcmp16(block.getElementName(&len),
1306                        permission_group16.string()) == 0;
1307                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1308                                 "name", isGroup ? packageIdentCharsWithTheStupid
1309                                 : packageIdentChars, true) != ATTR_OKAY) {
1310                    hasErrors = true;
1311                }
1312                SourcePos srcPos(manifestPath, block.getLineNumber());
1313                sp<AaptSymbols> syms;
1314                if (!isGroup) {
1315                    syms = permissionSymbols;
1316                    if (syms == NULL) {
1317                        sp<AaptSymbols> symbols =
1318                                assets->getSymbolsFor(String8("Manifest"));
1319                        syms = permissionSymbols = symbols->addNestedSymbol(
1320                                String8("permission"), srcPos);
1321                    }
1322                } else {
1323                    syms = permissionGroupSymbols;
1324                    if (syms == NULL) {
1325                        sp<AaptSymbols> symbols =
1326                                assets->getSymbolsFor(String8("Manifest"));
1327                        syms = permissionGroupSymbols = symbols->addNestedSymbol(
1328                                String8("permission_group"), srcPos);
1329                    }
1330                }
1331                size_t len;
1332                ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name");
1333                const uint16_t* id = block.getAttributeStringValue(index, &len);
1334                if (id == NULL) {
1335                    fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n",
1336                            manifestPath.string(), block.getLineNumber(),
1337                            String8(block.getElementName(&len)).string());
1338                    hasErrors = true;
1339                    break;
1340                }
1341                String8 idStr(id);
1342                char* p = idStr.lockBuffer(idStr.size());
1343                char* e = p + idStr.size();
1344                bool begins_with_digit = true;  // init to true so an empty string fails
1345                while (e > p) {
1346                    e--;
1347                    if (*e >= '0' && *e <= '9') {
1348                      begins_with_digit = true;
1349                      continue;
1350                    }
1351                    if ((*e >= 'a' && *e <= 'z') ||
1352                        (*e >= 'A' && *e <= 'Z') ||
1353                        (*e == '_')) {
1354                      begins_with_digit = false;
1355                      continue;
1356                    }
1357                    if (isGroup && (*e == '-')) {
1358                        *e = '_';
1359                        begins_with_digit = false;
1360                        continue;
1361                    }
1362                    e++;
1363                    break;
1364                }
1365                idStr.unlockBuffer();
1366                // verify that we stopped because we hit a period or
1367                // the beginning of the string, and that the
1368                // identifier didn't begin with a digit.
1369                if (begins_with_digit || (e != p && *(e-1) != '.')) {
1370                  fprintf(stderr,
1371                          "%s:%d: Permission name <%s> is not a valid Java symbol\n",
1372                          manifestPath.string(), block.getLineNumber(), idStr.string());
1373                  hasErrors = true;
1374                }
1375                syms->addStringSymbol(String8(e), idStr, srcPos);
1376                const uint16_t* cmt = block.getComment(&len);
1377                if (cmt != NULL && *cmt != 0) {
1378                    //printf("Comment of %s: %s\n", String8(e).string(),
1379                    //        String8(cmt).string());
1380                    syms->appendComment(String8(e), String16(cmt), srcPos);
1381                } else {
1382                    //printf("No comment for %s\n", String8(e).string());
1383                }
1384                syms->makeSymbolPublic(String8(e), srcPos);
1385            } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
1386                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1387                                 "name", packageIdentChars, true) != ATTR_OKAY) {
1388                    hasErrors = true;
1389                }
1390            } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) {
1391                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1392                                 "name", classIdentChars, true) != ATTR_OKAY) {
1393                    hasErrors = true;
1394                }
1395                if (validateAttr(manifestPath, finalResTable, block,
1396                                 RESOURCES_ANDROID_NAMESPACE, "targetPackage",
1397                                 packageIdentChars, true) != ATTR_OKAY) {
1398                    hasErrors = true;
1399                }
1400            } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) {
1401                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1402                                 "name", classIdentChars, false) != ATTR_OKAY) {
1403                    hasErrors = true;
1404                }
1405                if (validateAttr(manifestPath, finalResTable, block,
1406                                 RESOURCES_ANDROID_NAMESPACE, "permission",
1407                                 packageIdentChars, false) != ATTR_OKAY) {
1408                    hasErrors = true;
1409                }
1410                if (validateAttr(manifestPath, finalResTable, block,
1411                                 RESOURCES_ANDROID_NAMESPACE, "process",
1412                                 processIdentChars, false) != ATTR_OKAY) {
1413                    hasErrors = true;
1414                }
1415                if (validateAttr(manifestPath, finalResTable, block,
1416                                 RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
1417                                 processIdentChars, false) != ATTR_OKAY) {
1418                    hasErrors = true;
1419                }
1420            } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) {
1421                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1422                                 "name", classIdentChars, true) != ATTR_OKAY) {
1423                    hasErrors = true;
1424                }
1425                if (validateAttr(manifestPath, finalResTable, block,
1426                                 RESOURCES_ANDROID_NAMESPACE, "authorities",
1427                                 authoritiesIdentChars, true) != ATTR_OKAY) {
1428                    hasErrors = true;
1429                }
1430                if (validateAttr(manifestPath, finalResTable, block,
1431                                 RESOURCES_ANDROID_NAMESPACE, "permission",
1432                                 packageIdentChars, false) != ATTR_OKAY) {
1433                    hasErrors = true;
1434                }
1435                if (validateAttr(manifestPath, finalResTable, block,
1436                                 RESOURCES_ANDROID_NAMESPACE, "process",
1437                                 processIdentChars, false) != ATTR_OKAY) {
1438                    hasErrors = true;
1439                }
1440            } else if (strcmp16(block.getElementName(&len), service16.string()) == 0
1441                       || strcmp16(block.getElementName(&len), receiver16.string()) == 0
1442                       || strcmp16(block.getElementName(&len), activity16.string()) == 0) {
1443                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1444                                 "name", classIdentChars, true) != ATTR_OKAY) {
1445                    hasErrors = true;
1446                }
1447                if (validateAttr(manifestPath, finalResTable, block,
1448                                 RESOURCES_ANDROID_NAMESPACE, "permission",
1449                                 packageIdentChars, false) != ATTR_OKAY) {
1450                    hasErrors = true;
1451                }
1452                if (validateAttr(manifestPath, finalResTable, block,
1453                                 RESOURCES_ANDROID_NAMESPACE, "process",
1454                                 processIdentChars, false) != ATTR_OKAY) {
1455                    hasErrors = true;
1456                }
1457                if (validateAttr(manifestPath, finalResTable, block,
1458                                 RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
1459                                 processIdentChars, false) != ATTR_OKAY) {
1460                    hasErrors = true;
1461                }
1462            } else if (strcmp16(block.getElementName(&len), action16.string()) == 0
1463                       || strcmp16(block.getElementName(&len), category16.string()) == 0) {
1464                if (validateAttr(manifestPath, finalResTable, block,
1465                                 RESOURCES_ANDROID_NAMESPACE, "name",
1466                                 packageIdentChars, true) != ATTR_OKAY) {
1467                    hasErrors = true;
1468                }
1469            } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) {
1470                if (validateAttr(manifestPath, finalResTable, block,
1471                                 RESOURCES_ANDROID_NAMESPACE, "mimeType",
1472                                 typeIdentChars, true) != ATTR_OKAY) {
1473                    hasErrors = true;
1474                }
1475                if (validateAttr(manifestPath, finalResTable, block,
1476                                 RESOURCES_ANDROID_NAMESPACE, "scheme",
1477                                 schemeIdentChars, true) != ATTR_OKAY) {
1478                    hasErrors = true;
1479                }
1480            }
1481        }
1482    }
1483
1484    if (resFile != NULL) {
1485        // These resources are now considered to be a part of the included
1486        // resources, for others to reference.
1487        err = assets->addIncludedResources(resFile);
1488        if (err < NO_ERROR) {
1489            fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n");
1490            return err;
1491        }
1492    }
1493
1494    return err;
1495}
1496
1497static const char* getIndentSpace(int indent)
1498{
1499static const char whitespace[] =
1500"                                                                                       ";
1501
1502    return whitespace + sizeof(whitespace) - 1 - indent*4;
1503}
1504
1505static status_t fixupSymbol(String16* inoutSymbol)
1506{
1507    inoutSymbol->replaceAll('.', '_');
1508    inoutSymbol->replaceAll(':', '_');
1509    return NO_ERROR;
1510}
1511
1512static String16 getAttributeComment(const sp<AaptAssets>& assets,
1513                                    const String8& name,
1514                                    String16* outTypeComment = NULL)
1515{
1516    sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R"));
1517    if (asym != NULL) {
1518        //printf("Got R symbols!\n");
1519        asym = asym->getNestedSymbols().valueFor(String8("attr"));
1520        if (asym != NULL) {
1521            //printf("Got attrs symbols! comment %s=%s\n",
1522            //     name.string(), String8(asym->getComment(name)).string());
1523            if (outTypeComment != NULL) {
1524                *outTypeComment = asym->getTypeComment(name);
1525            }
1526            return asym->getComment(name);
1527        }
1528    }
1529    return String16();
1530}
1531
1532static status_t writeLayoutClasses(
1533    FILE* fp, const sp<AaptAssets>& assets,
1534    const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
1535{
1536    const char* indentStr = getIndentSpace(indent);
1537    if (!includePrivate) {
1538        fprintf(fp, "%s/** @doconly */\n", indentStr);
1539    }
1540    fprintf(fp, "%spublic static final class styleable {\n", indentStr);
1541    indent++;
1542
1543    String16 attr16("attr");
1544    String16 package16(assets->getPackage());
1545
1546    indentStr = getIndentSpace(indent);
1547    bool hasErrors = false;
1548
1549    size_t i;
1550    size_t N = symbols->getNestedSymbols().size();
1551    for (i=0; i<N; i++) {
1552        sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
1553        String16 nclassName16(symbols->getNestedSymbols().keyAt(i));
1554        String8 realClassName(nclassName16);
1555        if (fixupSymbol(&nclassName16) != NO_ERROR) {
1556            hasErrors = true;
1557        }
1558        String8 nclassName(nclassName16);
1559
1560        SortedVector<uint32_t> idents;
1561        Vector<uint32_t> origOrder;
1562        Vector<bool> publicFlags;
1563
1564        size_t a;
1565        size_t NA = nsymbols->getSymbols().size();
1566        for (a=0; a<NA; a++) {
1567            const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
1568            int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
1569                    ? sym.int32Val : 0;
1570            bool isPublic = true;
1571            if (code == 0) {
1572                String16 name16(sym.name);
1573                uint32_t typeSpecFlags;
1574                code = assets->getIncludedResources().identifierForName(
1575                    name16.string(), name16.size(),
1576                    attr16.string(), attr16.size(),
1577                    package16.string(), package16.size(), &typeSpecFlags);
1578                if (code == 0) {
1579                    fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
1580                            nclassName.string(), sym.name.string());
1581                    hasErrors = true;
1582                }
1583                isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
1584            }
1585            idents.add(code);
1586            origOrder.add(code);
1587            publicFlags.add(isPublic);
1588        }
1589
1590        NA = idents.size();
1591
1592        bool deprecated = false;
1593
1594        String16 comment = symbols->getComment(realClassName);
1595        fprintf(fp, "%s/** ", indentStr);
1596        if (comment.size() > 0) {
1597            String8 cmt(comment);
1598            fprintf(fp, "%s\n", cmt.string());
1599            if (strstr(cmt.string(), "@deprecated") != NULL) {
1600                deprecated = true;
1601            }
1602        } else {
1603            fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
1604        }
1605        bool hasTable = false;
1606        for (a=0; a<NA; a++) {
1607            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1608            if (pos >= 0) {
1609                if (!hasTable) {
1610                    hasTable = true;
1611                    fprintf(fp,
1612                            "%s   <p>Includes the following attributes:</p>\n"
1613                            "%s   <table>\n"
1614                            "%s   <colgroup align=\"left\" />\n"
1615                            "%s   <colgroup align=\"left\" />\n"
1616                            "%s   <tr><th>Attribute</th><th>Description</th></tr>\n",
1617                            indentStr,
1618                            indentStr,
1619                            indentStr,
1620                            indentStr,
1621                            indentStr);
1622                }
1623                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1624                if (!publicFlags.itemAt(a) && !includePrivate) {
1625                    continue;
1626                }
1627                String8 name8(sym.name);
1628                String16 comment(sym.comment);
1629                if (comment.size() <= 0) {
1630                    comment = getAttributeComment(assets, name8);
1631                }
1632                if (comment.size() > 0) {
1633                    const char16_t* p = comment.string();
1634                    while (*p != 0 && *p != '.') {
1635                        if (*p == '{') {
1636                            while (*p != 0 && *p != '}') {
1637                                p++;
1638                            }
1639                        } else {
1640                            p++;
1641                        }
1642                    }
1643                    if (*p == '.') {
1644                        p++;
1645                    }
1646                    comment = String16(comment.string(), p-comment.string());
1647                }
1648                String16 name(name8);
1649                fixupSymbol(&name);
1650                fprintf(fp, "%s   <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n",
1651                        indentStr, nclassName.string(),
1652                        String8(name).string(),
1653                        assets->getPackage().string(),
1654                        String8(name).string(),
1655                        String8(comment).string());
1656            }
1657        }
1658        if (hasTable) {
1659            fprintf(fp, "%s   </table>\n", indentStr);
1660        }
1661        for (a=0; a<NA; a++) {
1662            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1663            if (pos >= 0) {
1664                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1665                if (!publicFlags.itemAt(a) && !includePrivate) {
1666                    continue;
1667                }
1668                String16 name(sym.name);
1669                fixupSymbol(&name);
1670                fprintf(fp, "%s   @see #%s_%s\n",
1671                        indentStr, nclassName.string(),
1672                        String8(name).string());
1673            }
1674        }
1675        fprintf(fp, "%s */\n", getIndentSpace(indent));
1676
1677        if (deprecated) {
1678            fprintf(fp, "%s@Deprecated\n", indentStr);
1679        }
1680
1681        fprintf(fp,
1682                "%spublic static final int[] %s = {\n"
1683                "%s",
1684                indentStr, nclassName.string(),
1685                getIndentSpace(indent+1));
1686
1687        for (a=0; a<NA; a++) {
1688            if (a != 0) {
1689                if ((a&3) == 0) {
1690                    fprintf(fp, ",\n%s", getIndentSpace(indent+1));
1691                } else {
1692                    fprintf(fp, ", ");
1693                }
1694            }
1695            fprintf(fp, "0x%08x", idents[a]);
1696        }
1697
1698        fprintf(fp, "\n%s};\n", indentStr);
1699
1700        for (a=0; a<NA; a++) {
1701            ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1702            if (pos >= 0) {
1703                const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1704                if (!publicFlags.itemAt(a) && !includePrivate) {
1705                    continue;
1706                }
1707                String8 name8(sym.name);
1708                String16 comment(sym.comment);
1709                String16 typeComment;
1710                if (comment.size() <= 0) {
1711                    comment = getAttributeComment(assets, name8, &typeComment);
1712                } else {
1713                    getAttributeComment(assets, name8, &typeComment);
1714                }
1715                String16 name(name8);
1716                if (fixupSymbol(&name) != NO_ERROR) {
1717                    hasErrors = true;
1718                }
1719
1720                uint32_t typeSpecFlags = 0;
1721                String16 name16(sym.name);
1722                assets->getIncludedResources().identifierForName(
1723                    name16.string(), name16.size(),
1724                    attr16.string(), attr16.size(),
1725                    package16.string(), package16.size(), &typeSpecFlags);
1726                //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
1727                //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
1728                const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
1729
1730                bool deprecated = false;
1731
1732                fprintf(fp, "%s/**\n", indentStr);
1733                if (comment.size() > 0) {
1734                    String8 cmt(comment);
1735                    fprintf(fp, "%s  <p>\n%s  @attr description\n", indentStr, indentStr);
1736                    fprintf(fp, "%s  %s\n", indentStr, cmt.string());
1737                    if (strstr(cmt.string(), "@deprecated") != NULL) {
1738                        deprecated = true;
1739                    }
1740                } else {
1741                    fprintf(fp,
1742                            "%s  <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
1743                            "%s  attribute's value can be found in the {@link #%s} array.\n",
1744                            indentStr,
1745                            pub ? assets->getPackage().string()
1746                                : assets->getSymbolsPrivatePackage().string(),
1747                            String8(name).string(),
1748                            indentStr, nclassName.string());
1749                }
1750                if (typeComment.size() > 0) {
1751                    String8 cmt(typeComment);
1752                    fprintf(fp, "\n\n%s  %s\n", indentStr, cmt.string());
1753                    if (strstr(cmt.string(), "@deprecated") != NULL) {
1754                        deprecated = true;
1755                    }
1756                }
1757                if (comment.size() > 0) {
1758                    if (pub) {
1759                        fprintf(fp,
1760                                "%s  <p>This corresponds to the global attribute"
1761                                "%s  resource symbol {@link %s.R.attr#%s}.\n",
1762                                indentStr, indentStr,
1763                                assets->getPackage().string(),
1764                                String8(name).string());
1765                    } else {
1766                        fprintf(fp,
1767                                "%s  <p>This is a private symbol.\n", indentStr);
1768                    }
1769                }
1770                fprintf(fp, "%s  @attr name %s:%s\n", indentStr,
1771                        "android", String8(name).string());
1772                fprintf(fp, "%s*/\n", indentStr);
1773                if (deprecated) {
1774                    fprintf(fp, "%s@Deprecated\n", indentStr);
1775                }
1776                fprintf(fp,
1777                        "%spublic static final int %s_%s = %d;\n",
1778                        indentStr, nclassName.string(),
1779                        String8(name).string(), (int)pos);
1780            }
1781        }
1782    }
1783
1784    indent--;
1785    fprintf(fp, "%s};\n", getIndentSpace(indent));
1786    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
1787}
1788
1789static status_t writeSymbolClass(
1790    FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
1791    const sp<AaptSymbols>& symbols, const String8& className, int indent,
1792    bool nonConstantId)
1793{
1794    fprintf(fp, "%spublic %sfinal class %s {\n",
1795            getIndentSpace(indent),
1796            indent != 0 ? "static " : "", className.string());
1797    indent++;
1798
1799    size_t i;
1800    status_t err = NO_ERROR;
1801
1802    const char * id_format = nonConstantId ?
1803            "%spublic static int %s=0x%08x;\n" :
1804            "%spublic static final int %s=0x%08x;\n";
1805
1806    size_t N = symbols->getSymbols().size();
1807    for (i=0; i<N; i++) {
1808        const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
1809        if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
1810            continue;
1811        }
1812        if (!includePrivate && !sym.isPublic) {
1813            continue;
1814        }
1815        String16 name(sym.name);
1816        String8 realName(name);
1817        if (fixupSymbol(&name) != NO_ERROR) {
1818            return UNKNOWN_ERROR;
1819        }
1820        String16 comment(sym.comment);
1821        bool haveComment = false;
1822        bool deprecated = false;
1823        if (comment.size() > 0) {
1824            haveComment = true;
1825            String8 cmt(comment);
1826            fprintf(fp,
1827                    "%s/** %s\n",
1828                    getIndentSpace(indent), cmt.string());
1829            if (strstr(cmt.string(), "@deprecated") != NULL) {
1830                deprecated = true;
1831            }
1832        } else if (sym.isPublic && !includePrivate) {
1833            sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
1834                assets->getPackage().string(), className.string(),
1835                String8(sym.name).string());
1836        }
1837        String16 typeComment(sym.typeComment);
1838        if (typeComment.size() > 0) {
1839            String8 cmt(typeComment);
1840            if (!haveComment) {
1841                haveComment = true;
1842                fprintf(fp,
1843                        "%s/** %s\n", getIndentSpace(indent), cmt.string());
1844            } else {
1845                fprintf(fp,
1846                        "%s %s\n", getIndentSpace(indent), cmt.string());
1847            }
1848            if (strstr(cmt.string(), "@deprecated") != NULL) {
1849                deprecated = true;
1850            }
1851        }
1852        if (haveComment) {
1853            fprintf(fp,"%s */\n", getIndentSpace(indent));
1854        }
1855        if (deprecated) {
1856            fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
1857        }
1858        fprintf(fp, id_format,
1859                getIndentSpace(indent),
1860                String8(name).string(), (int)sym.int32Val);
1861    }
1862
1863    for (i=0; i<N; i++) {
1864        const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
1865        if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) {
1866            continue;
1867        }
1868        if (!includePrivate && !sym.isPublic) {
1869            continue;
1870        }
1871        String16 name(sym.name);
1872        if (fixupSymbol(&name) != NO_ERROR) {
1873            return UNKNOWN_ERROR;
1874        }
1875        String16 comment(sym.comment);
1876        bool deprecated = false;
1877        if (comment.size() > 0) {
1878            String8 cmt(comment);
1879            fprintf(fp,
1880                    "%s/** %s\n"
1881                     "%s */\n",
1882                    getIndentSpace(indent), cmt.string(),
1883                    getIndentSpace(indent));
1884            if (strstr(cmt.string(), "@deprecated") != NULL) {
1885                deprecated = true;
1886            }
1887        } else if (sym.isPublic && !includePrivate) {
1888            sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
1889                assets->getPackage().string(), className.string(),
1890                String8(sym.name).string());
1891        }
1892        if (deprecated) {
1893            fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
1894        }
1895        fprintf(fp, "%spublic static final String %s=\"%s\";\n",
1896                getIndentSpace(indent),
1897                String8(name).string(), sym.stringVal.string());
1898    }
1899
1900    sp<AaptSymbols> styleableSymbols;
1901
1902    N = symbols->getNestedSymbols().size();
1903    for (i=0; i<N; i++) {
1904        sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
1905        String8 nclassName(symbols->getNestedSymbols().keyAt(i));
1906        if (nclassName == "styleable") {
1907            styleableSymbols = nsymbols;
1908        } else {
1909            err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
1910        }
1911        if (err != NO_ERROR) {
1912            return err;
1913        }
1914    }
1915
1916    if (styleableSymbols != NULL) {
1917        err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate);
1918        if (err != NO_ERROR) {
1919            return err;
1920        }
1921    }
1922
1923    indent--;
1924    fprintf(fp, "%s}\n", getIndentSpace(indent));
1925    return NO_ERROR;
1926}
1927
1928status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
1929    const String8& package, bool includePrivate)
1930{
1931    if (!bundle->getRClassDir()) {
1932        return NO_ERROR;
1933    }
1934
1935    const size_t N = assets->getSymbols().size();
1936    for (size_t i=0; i<N; i++) {
1937        sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
1938        String8 className(assets->getSymbols().keyAt(i));
1939        String8 dest(bundle->getRClassDir());
1940        if (bundle->getMakePackageDirs()) {
1941            String8 pkg(package);
1942            const char* last = pkg.string();
1943            const char* s = last-1;
1944            do {
1945                s++;
1946                if (s > last && (*s == '.' || *s == 0)) {
1947                    String8 part(last, s-last);
1948                    dest.appendPath(part);
1949#ifdef HAVE_MS_C_RUNTIME
1950                    _mkdir(dest.string());
1951#else
1952                    mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
1953#endif
1954                    last = s+1;
1955                }
1956            } while (*s);
1957        }
1958        dest.appendPath(className);
1959        dest.append(".java");
1960        FILE* fp = fopen(dest.string(), "w+");
1961        if (fp == NULL) {
1962            fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
1963                    dest.string(), strerror(errno));
1964            return UNKNOWN_ERROR;
1965        }
1966        if (bundle->getVerbose()) {
1967            printf("  Writing symbols for class %s.\n", className.string());
1968        }
1969
1970        fprintf(fp,
1971        "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
1972        " *\n"
1973        " * This class was automatically generated by the\n"
1974        " * aapt tool from the resource data it found.  It\n"
1975        " * should not be modified by hand.\n"
1976        " */\n"
1977        "\n"
1978        "package %s;\n\n", package.string());
1979
1980        status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0, bundle->getNonConstantId());
1981        if (err != NO_ERROR) {
1982            return err;
1983        }
1984        fclose(fp);
1985
1986        // If we were asked to generate a dependency file, we'll go ahead and add this R.java
1987        // as a target in the dependency file right next to it.
1988        if (bundle->getGenDependencies()) {
1989            // Add this R.java to the dependency file
1990            String8 dependencyFile(bundle->getRClassDir());
1991            dependencyFile.appendPath("R.java.d");
1992
1993            fp = fopen(dependencyFile.string(), "a");
1994            fprintf(fp,"%s \\\n", dest.string());
1995            fclose(fp);
1996        }
1997    }
1998
1999    return NO_ERROR;
2000}
2001
2002
2003
2004class ProguardKeepSet
2005{
2006public:
2007    // { rule --> { file locations } }
2008    KeyedVector<String8, SortedVector<String8> > rules;
2009
2010    void add(const String8& rule, const String8& where);
2011};
2012
2013void ProguardKeepSet::add(const String8& rule, const String8& where)
2014{
2015    ssize_t index = rules.indexOfKey(rule);
2016    if (index < 0) {
2017        index = rules.add(rule, SortedVector<String8>());
2018    }
2019    rules.editValueAt(index).add(where);
2020}
2021
2022void
2023addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
2024        const char* pkg, const String8& srcName, int line)
2025{
2026    String8 className(inClassName);
2027    if (pkg != NULL) {
2028        // asdf     --> package.asdf
2029        // .asdf  .a.b  --> package.asdf package.a.b
2030        // asdf.adsf --> asdf.asdf
2031        const char* p = className.string();
2032        const char* q = strchr(p, '.');
2033        if (p == q) {
2034            className = pkg;
2035            className.append(inClassName);
2036        } else if (q == NULL) {
2037            className = pkg;
2038            className.append(".");
2039            className.append(inClassName);
2040        }
2041    }
2042
2043    String8 rule("-keep class ");
2044    rule += className;
2045    rule += " { <init>(...); }";
2046
2047    String8 location("view ");
2048    location += srcName;
2049    char lineno[20];
2050    sprintf(lineno, ":%d", line);
2051    location += lineno;
2052
2053    keep->add(rule, location);
2054}
2055
2056status_t
2057writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
2058{
2059    status_t err;
2060    ResXMLTree tree;
2061    size_t len;
2062    ResXMLTree::event_code_t code;
2063    int depth = 0;
2064    bool inApplication = false;
2065    String8 error;
2066    sp<AaptGroup> assGroup;
2067    sp<AaptFile> assFile;
2068    String8 pkg;
2069
2070    // First, look for a package file to parse.  This is required to
2071    // be able to generate the resource information.
2072    assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml"));
2073    if (assGroup == NULL) {
2074        fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
2075        return -1;
2076    }
2077
2078    if (assGroup->getFiles().size() != 1) {
2079        fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
2080                assGroup->getFiles().valueAt(0)->getPrintableSource().string());
2081    }
2082
2083    assFile = assGroup->getFiles().valueAt(0);
2084
2085    err = parseXMLResource(assFile, &tree);
2086    if (err != NO_ERROR) {
2087        return err;
2088    }
2089
2090    tree.restart();
2091
2092    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2093        if (code == ResXMLTree::END_TAG) {
2094            if (/* name == "Application" && */ depth == 2) {
2095                inApplication = false;
2096            }
2097            depth--;
2098            continue;
2099        }
2100        if (code != ResXMLTree::START_TAG) {
2101            continue;
2102        }
2103        depth++;
2104        String8 tag(tree.getElementName(&len));
2105        // printf("Depth %d tag %s\n", depth, tag.string());
2106        bool keepTag = false;
2107        if (depth == 1) {
2108            if (tag != "manifest") {
2109                fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
2110                return -1;
2111            }
2112            pkg = getAttribute(tree, NULL, "package", NULL);
2113        } else if (depth == 2) {
2114            if (tag == "application") {
2115                inApplication = true;
2116                keepTag = true;
2117
2118                String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
2119                        "backupAgent", &error);
2120                if (agent.length() > 0) {
2121                    addProguardKeepRule(keep, agent, pkg.string(),
2122                            assFile->getPrintableSource(), tree.getLineNumber());
2123                }
2124            } else if (tag == "instrumentation") {
2125                keepTag = true;
2126            }
2127        }
2128        if (!keepTag && inApplication && depth == 3) {
2129            if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
2130                keepTag = true;
2131            }
2132        }
2133        if (keepTag) {
2134            String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
2135                    "name", &error);
2136            if (error != "") {
2137                fprintf(stderr, "ERROR: %s\n", error.string());
2138                return -1;
2139            }
2140            if (name.length() > 0) {
2141                addProguardKeepRule(keep, name, pkg.string(),
2142                        assFile->getPrintableSource(), tree.getLineNumber());
2143            }
2144        }
2145    }
2146
2147    return NO_ERROR;
2148}
2149
2150struct NamespaceAttributePair {
2151    const char* ns;
2152    const char* attr;
2153
2154    NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {}
2155    NamespaceAttributePair() : ns(NULL), attr(NULL) {}
2156};
2157
2158status_t
2159writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
2160        const char* startTag, const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs)
2161{
2162    status_t err;
2163    ResXMLTree tree;
2164    size_t len;
2165    ResXMLTree::event_code_t code;
2166
2167    err = parseXMLResource(layoutFile, &tree);
2168    if (err != NO_ERROR) {
2169        return err;
2170    }
2171
2172    tree.restart();
2173
2174    if (startTag != NULL) {
2175        bool haveStart = false;
2176        while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2177            if (code != ResXMLTree::START_TAG) {
2178                continue;
2179            }
2180            String8 tag(tree.getElementName(&len));
2181            if (tag == startTag) {
2182                haveStart = true;
2183            }
2184            break;
2185        }
2186        if (!haveStart) {
2187            return NO_ERROR;
2188        }
2189    }
2190
2191    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2192        if (code != ResXMLTree::START_TAG) {
2193            continue;
2194        }
2195        String8 tag(tree.getElementName(&len));
2196
2197        // If there is no '.', we'll assume that it's one of the built in names.
2198        if (strchr(tag.string(), '.')) {
2199            addProguardKeepRule(keep, tag, NULL,
2200                    layoutFile->getPrintableSource(), tree.getLineNumber());
2201        } else if (tagAttrPairs != NULL) {
2202            ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
2203            if (tagIndex >= 0) {
2204                const NamespaceAttributePair& nsAttr = tagAttrPairs->valueAt(tagIndex);
2205                ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
2206                if (attrIndex < 0) {
2207                    // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
2208                    //        layoutFile->getPrintableSource().string(), tree.getLineNumber(),
2209                    //        tag.string(), nsAttr.ns, nsAttr.attr);
2210                } else {
2211                    size_t len;
2212                    addProguardKeepRule(keep,
2213                                        String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
2214                                        layoutFile->getPrintableSource(), tree.getLineNumber());
2215                }
2216            }
2217        }
2218    }
2219
2220    return NO_ERROR;
2221}
2222
2223static void addTagAttrPair(KeyedVector<String8, NamespaceAttributePair>* dest,
2224        const char* tag, const char* ns, const char* attr) {
2225    dest->add(String8(tag), NamespaceAttributePair(ns, attr));
2226}
2227
2228status_t
2229writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
2230{
2231    status_t err;
2232
2233    // tag:attribute pairs that should be checked in layout files.
2234    KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
2235    addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
2236    addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
2237    addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
2238
2239    // tag:attribute pairs that should be checked in xml files.
2240    KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
2241    addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
2242    addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
2243
2244    const Vector<sp<AaptDir> >& dirs = assets->resDirs();
2245    const size_t K = dirs.size();
2246    for (size_t k=0; k<K; k++) {
2247        const sp<AaptDir>& d = dirs.itemAt(k);
2248        const String8& dirName = d->getLeaf();
2249        const char* startTag = NULL;
2250        const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs = NULL;
2251        if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
2252            tagAttrPairs = &kLayoutTagAttrPairs;
2253        } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
2254            startTag = "PreferenceScreen";
2255            tagAttrPairs = &kXmlTagAttrPairs;
2256        } else {
2257            continue;
2258        }
2259
2260        const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
2261        const size_t N = groups.size();
2262        for (size_t i=0; i<N; i++) {
2263            const sp<AaptGroup>& group = groups.valueAt(i);
2264            const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
2265            const size_t M = files.size();
2266            for (size_t j=0; j<M; j++) {
2267                err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
2268                if (err < 0) {
2269                    return err;
2270                }
2271            }
2272        }
2273    }
2274    // Handle the overlays
2275    sp<AaptAssets> overlay = assets->getOverlay();
2276    if (overlay.get()) {
2277        return writeProguardForLayouts(keep, overlay);
2278    }
2279    return NO_ERROR;
2280}
2281
2282status_t
2283writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
2284{
2285    status_t err = -1;
2286
2287    if (!bundle->getProguardFile()) {
2288        return NO_ERROR;
2289    }
2290
2291    ProguardKeepSet keep;
2292
2293    err = writeProguardForAndroidManifest(&keep, assets);
2294    if (err < 0) {
2295        return err;
2296    }
2297
2298    err = writeProguardForLayouts(&keep, assets);
2299    if (err < 0) {
2300        return err;
2301    }
2302
2303    FILE* fp = fopen(bundle->getProguardFile(), "w+");
2304    if (fp == NULL) {
2305        fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
2306                bundle->getProguardFile(), strerror(errno));
2307        return UNKNOWN_ERROR;
2308    }
2309
2310    const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules;
2311    const size_t N = rules.size();
2312    for (size_t i=0; i<N; i++) {
2313        const SortedVector<String8>& locations = rules.valueAt(i);
2314        const size_t M = locations.size();
2315        for (size_t j=0; j<M; j++) {
2316            fprintf(fp, "# %s\n", locations.itemAt(j).string());
2317        }
2318        fprintf(fp, "%s\n\n", rules.keyAt(i).string());
2319    }
2320    fclose(fp);
2321
2322    return err;
2323}
2324
2325// Loops through the string paths and writes them to the file pointer
2326// Each file path is written on its own line with a terminating backslash.
2327status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
2328{
2329    status_t deps = -1;
2330    for (size_t file_i = 0; file_i < files->size(); ++file_i) {
2331        // Add the full file path to the dependency file
2332        fprintf(fp, "%s \\\n", files->itemAt(file_i).string());
2333        deps++;
2334    }
2335    return deps;
2336}
2337
2338status_t
2339writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw)
2340{
2341    status_t deps = -1;
2342    deps += writePathsToFile(assets->getFullResPaths(), fp);
2343    if (includeRaw) {
2344        deps += writePathsToFile(assets->getFullAssetPaths(), fp);
2345    }
2346    return deps;
2347}
2348