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