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