AaptAssets.cpp revision 5e77475b5d1de1fecbaf5b3e27988a255a3c399e
1//
2// Copyright 2006 The Android Open Source Project
3//
4
5#include "AaptAssets.h"
6#include "AaptConfig.h"
7#include "AaptUtil.h"
8#include "Main.h"
9#include "ResourceFilter.h"
10
11#include <utils/misc.h>
12#include <utils/SortedVector.h>
13
14#include <ctype.h>
15#include <dirent.h>
16#include <errno.h>
17
18static const char* kAssetDir = "assets";
19static const char* kResourceDir = "res";
20static const char* kValuesDir = "values";
21static const char* kMipmapDir = "mipmap";
22static const char* kInvalidChars = "/\\:";
23static const size_t kMaxAssetFileName = 100;
24
25static const String8 kResString(kResourceDir);
26
27/*
28 * Names of asset files must meet the following criteria:
29 *
30 *  - the filename length must be less than kMaxAssetFileName bytes long
31 *    (and can't be empty)
32 *  - all characters must be 7-bit printable ASCII
33 *  - none of { '/' '\\' ':' }
34 *
35 * Pass in just the filename, not the full path.
36 */
37static bool validateFileName(const char* fileName)
38{
39    const char* cp = fileName;
40    size_t len = 0;
41
42    while (*cp != '\0') {
43        if ((*cp & 0x80) != 0)
44            return false;           // reject high ASCII
45        if (*cp < 0x20 || *cp >= 0x7f)
46            return false;           // reject control chars and 0x7f
47        if (strchr(kInvalidChars, *cp) != NULL)
48            return false;           // reject path sep chars
49        cp++;
50        len++;
51    }
52
53    if (len < 1 || len > kMaxAssetFileName)
54        return false;               // reject empty or too long
55
56    return true;
57}
58
59// The default to use if no other ignore pattern is defined.
60const char * const gDefaultIgnoreAssets =
61    "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
62// The ignore pattern that can be passed via --ignore-assets in Main.cpp
63const char * gUserIgnoreAssets = NULL;
64
65static bool isHidden(const char *root, const char *path)
66{
67    // Patterns syntax:
68    // - Delimiter is :
69    // - Entry can start with the flag ! to avoid printing a warning
70    //   about the file being ignored.
71    // - Entry can have the flag "<dir>" to match only directories
72    //   or <file> to match only files. Default is to match both.
73    // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
74    //   where prefix/suffix must have at least 1 character (so that
75    //   we don't match a '*' catch-all pattern.)
76    // - The special filenames "." and ".." are always ignored.
77    // - Otherwise the full string is matched.
78    // - match is not case-sensitive.
79
80    if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
81        return true;
82    }
83
84    const char *delim = ":";
85    const char *p = gUserIgnoreAssets;
86    if (!p || !p[0]) {
87        p = getenv("ANDROID_AAPT_IGNORE");
88    }
89    if (!p || !p[0]) {
90        p = gDefaultIgnoreAssets;
91    }
92    char *patterns = strdup(p);
93
94    bool ignore = false;
95    bool chatty = true;
96    char *matchedPattern = NULL;
97
98    String8 fullPath(root);
99    fullPath.appendPath(path);
100    FileType type = getFileType(fullPath);
101
102    int plen = strlen(path);
103
104    // Note: we don't have strtok_r under mingw.
105    for(char *token = strtok(patterns, delim);
106            !ignore && token != NULL;
107            token = strtok(NULL, delim)) {
108        chatty = token[0] != '!';
109        if (!chatty) token++; // skip !
110        if (strncasecmp(token, "<dir>" , 5) == 0) {
111            if (type != kFileTypeDirectory) continue;
112            token += 5;
113        }
114        if (strncasecmp(token, "<file>", 6) == 0) {
115            if (type != kFileTypeRegular) continue;
116            token += 6;
117        }
118
119        matchedPattern = token;
120        int n = strlen(token);
121
122        if (token[0] == '*') {
123            // Match *suffix
124            token++;
125            n--;
126            if (n <= plen) {
127                ignore = strncasecmp(token, path + plen - n, n) == 0;
128            }
129        } else if (n > 1 && token[n - 1] == '*') {
130            // Match prefix*
131            ignore = strncasecmp(token, path, n - 1) == 0;
132        } else {
133            ignore = strcasecmp(token, path) == 0;
134        }
135    }
136
137    if (ignore && chatty) {
138        fprintf(stderr, "    (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
139                type == kFileTypeDirectory ? "dir" : "file",
140                path,
141                matchedPattern ? matchedPattern : "");
142    }
143
144    free(patterns);
145    return ignore;
146}
147
148// =========================================================================
149// =========================================================================
150// =========================================================================
151
152/* static */
153inline bool isAlpha(const String8& string) {
154     const size_t length = string.length();
155     for (size_t i = 0; i < length; ++i) {
156          if (!isalpha(string[i])) {
157              return false;
158          }
159     }
160
161     return true;
162}
163
164/* static */
165inline bool isNumber(const String8& string) {
166     const size_t length = string.length();
167     for (size_t i = 0; i < length; ++i) {
168          if (!isdigit(string[i])) {
169              return false;
170          }
171     }
172
173     return true;
174}
175
176void AaptLocaleValue::setLanguage(const char* languageChars) {
177     size_t i = 0;
178     while ((*languageChars) != '\0' && i < sizeof(language)/sizeof(language[0])) {
179          language[i++] = tolower(*languageChars);
180          languageChars++;
181     }
182}
183
184void AaptLocaleValue::setRegion(const char* regionChars) {
185    size_t i = 0;
186    while ((*regionChars) != '\0' && i < sizeof(region)/sizeof(region[0])) {
187         region[i++] = toupper(*regionChars);
188         regionChars++;
189    }
190}
191
192void AaptLocaleValue::setScript(const char* scriptChars) {
193    size_t i = 0;
194    while ((*scriptChars) != '\0' && i < sizeof(script)/sizeof(script[0])) {
195         if (i == 0) {
196             script[i++] = toupper(*scriptChars);
197         } else {
198             script[i++] = tolower(*scriptChars);
199         }
200         scriptChars++;
201    }
202}
203
204void AaptLocaleValue::setVariant(const char* variantChars) {
205     size_t i = 0;
206     while ((*variantChars) != '\0' && i < sizeof(variant)/sizeof(variant[0])) {
207          variant[i++] = *variantChars;
208          variantChars++;
209     }
210}
211
212bool AaptLocaleValue::initFromFilterString(const String8& str) {
213     // A locale (as specified in the filter) is an underscore separated name such
214     // as "en_US", "en_Latn_US", or "en_US_POSIX".
215     Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_');
216
217     const int numTags = parts.size();
218     bool valid = false;
219     if (numTags >= 1) {
220         const String8& lang = parts[0];
221         if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
222             setLanguage(lang.string());
223             valid = true;
224         }
225     }
226
227     if (!valid || numTags == 1) {
228         return valid;
229     }
230
231     // At this point, valid == true && numTags > 1.
232     const String8& part2 = parts[1];
233     if ((part2.length() == 2 && isAlpha(part2)) ||
234         (part2.length() == 3 && isNumber(part2))) {
235         setRegion(part2.string());
236     } else if (part2.length() == 4 && isAlpha(part2)) {
237         setScript(part2.string());
238     } else if (part2.length() >= 5 && part2.length() <= 8) {
239         setVariant(part2.string());
240     } else {
241         valid = false;
242     }
243
244     if (!valid || numTags == 2) {
245         return valid;
246     }
247
248     // At this point, valid == true && numTags > 1.
249     const String8& part3 = parts[2];
250     if (((part3.length() == 2 && isAlpha(part3)) ||
251         (part3.length() == 3 && isNumber(part3))) && script[0]) {
252         setRegion(part3.string());
253     } else if (part3.length() >= 5 && part3.length() <= 8) {
254         setVariant(part3.string());
255     } else {
256         valid = false;
257     }
258
259     if (!valid || numTags == 3) {
260         return valid;
261     }
262
263     const String8& part4 = parts[3];
264     if (part4.length() >= 5 && part4.length() <= 8) {
265         setVariant(part4.string());
266     } else {
267         valid = false;
268     }
269
270     if (!valid || numTags > 4) {
271         return false;
272     }
273
274     return true;
275}
276
277int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
278    const int size = parts.size();
279    int currentIndex = startIndex;
280
281    String8 part = parts[currentIndex];
282    if (part[0] == 'b' && part[1] == '+') {
283        // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
284        // except that the separator is "+" and not "-".
285        Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+');
286        subtags.removeItemsAt(0);
287        if (subtags.size() == 1) {
288            setLanguage(subtags[0]);
289        } else if (subtags.size() == 2) {
290            setLanguage(subtags[0]);
291
292            // The second tag can either be a region, a variant or a script.
293            switch (subtags[1].size()) {
294                case 2:
295                case 3:
296                    setRegion(subtags[1]);
297                    break;
298                case 4:
299                    setScript(subtags[1]);
300                    break;
301                case 5:
302                case 6:
303                case 7:
304                case 8:
305                    setVariant(subtags[1]);
306                    break;
307                default:
308                    fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n",
309                            part.string());
310                    return -1;
311            }
312        } else if (subtags.size() == 3) {
313            // The language is always the first subtag.
314            setLanguage(subtags[0]);
315
316            // The second subtag can either be a script or a region code.
317            // If its size is 4, it's a script code, else it's a region code.
318            bool hasRegion = false;
319            if (subtags[1].size() == 4) {
320                setScript(subtags[1]);
321            } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
322                setRegion(subtags[1]);
323                hasRegion = true;
324            } else {
325                fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", part.string());
326                return -1;
327            }
328
329            // The third tag can either be a region code (if the second tag was
330            // a script), else a variant code.
331            if (subtags[2].size() > 4) {
332                setVariant(subtags[2]);
333            } else {
334                setRegion(subtags[2]);
335            }
336        } else if (subtags.size() == 4) {
337            setLanguage(subtags[0]);
338            setScript(subtags[1]);
339            setRegion(subtags[2]);
340            setVariant(subtags[3]);
341        } else {
342            fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name: %s\n", part.string());
343            return -1;
344        }
345
346        return ++currentIndex;
347    } else {
348        if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) {
349            setLanguage(part);
350            if (++currentIndex == size) {
351                return size;
352            }
353        } else {
354            return currentIndex;
355        }
356
357        part = parts[currentIndex];
358        if (part.string()[0] == 'r' && part.length() == 3) {
359            setRegion(part.string() + 1);
360            if (++currentIndex == size) {
361                return size;
362            }
363        }
364    }
365
366    return currentIndex;
367}
368
369
370String8 AaptLocaleValue::toDirName() const {
371    String8 dirName("");
372    if (language[0]) {
373        dirName += language;
374    } else {
375        return dirName;
376    }
377
378    if (script[0]) {
379        dirName += "-s";
380        dirName += script;
381    }
382
383    if (region[0]) {
384        dirName += "-r";
385        dirName += region;
386    }
387
388    if (variant[0]) {
389        dirName += "-v";
390        dirName += variant;
391    }
392
393    return dirName;
394}
395
396void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
397    config.unpackLanguage(language);
398    config.unpackRegion(region);
399    if (config.localeScript[0]) {
400        memcpy(script, config.localeScript, sizeof(config.localeScript));
401    }
402
403    if (config.localeVariant[0]) {
404        memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
405    }
406}
407
408void AaptLocaleValue::writeTo(ResTable_config* out) const {
409    out->packLanguage(language);
410    out->packRegion(region);
411
412    if (script[0]) {
413        memcpy(out->localeScript, script, sizeof(out->localeScript));
414    }
415
416    if (variant[0]) {
417        memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
418    }
419}
420
421bool
422AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
423{
424    const char* q = strchr(dir, '-');
425    size_t typeLen;
426    if (q != NULL) {
427        typeLen = q - dir;
428    } else {
429        typeLen = strlen(dir);
430    }
431
432    String8 type(dir, typeLen);
433    if (!isValidResourceType(type)) {
434        return false;
435    }
436
437    if (q != NULL) {
438        if (!AaptConfig::parse(String8(q + 1), &mParams)) {
439            return false;
440        }
441    }
442
443    *resType = type;
444    return true;
445}
446
447String8
448AaptGroupEntry::toDirName(const String8& resType) const
449{
450    String8 s = resType;
451    String8 params = mParams.toString();
452    if (params.length() > 0) {
453        if (s.length() > 0) {
454            s += "-";
455        }
456        s += params;
457    }
458    return s;
459}
460
461
462// =========================================================================
463// =========================================================================
464// =========================================================================
465
466void* AaptFile::editData(size_t size)
467{
468    if (size <= mBufferSize) {
469        mDataSize = size;
470        return mData;
471    }
472    size_t allocSize = (size*3)/2;
473    void* buf = realloc(mData, allocSize);
474    if (buf == NULL) {
475        return NULL;
476    }
477    mData = buf;
478    mDataSize = size;
479    mBufferSize = allocSize;
480    return buf;
481}
482
483void* AaptFile::editDataInRange(size_t offset, size_t size)
484{
485    return (void*)(((uint8_t*) editData(offset + size)) + offset);
486}
487
488void* AaptFile::editData(size_t* outSize)
489{
490    if (outSize) {
491        *outSize = mDataSize;
492    }
493    return mData;
494}
495
496void* AaptFile::padData(size_t wordSize)
497{
498    const size_t extra = mDataSize%wordSize;
499    if (extra == 0) {
500        return mData;
501    }
502
503    size_t initial = mDataSize;
504    void* data = editData(initial+(wordSize-extra));
505    if (data != NULL) {
506        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
507    }
508    return data;
509}
510
511status_t AaptFile::writeData(const void* data, size_t size)
512{
513    size_t end = mDataSize;
514    size_t total = size + end;
515    void* buf = editData(total);
516    if (buf == NULL) {
517        return UNKNOWN_ERROR;
518    }
519    memcpy(((char*)buf)+end, data, size);
520    return NO_ERROR;
521}
522
523void AaptFile::clearData()
524{
525    if (mData != NULL) free(mData);
526    mData = NULL;
527    mDataSize = 0;
528    mBufferSize = 0;
529}
530
531String8 AaptFile::getPrintableSource() const
532{
533    if (hasData()) {
534        String8 name(mGroupEntry.toDirName(String8()));
535        name.appendPath(mPath);
536        name.append(" #generated");
537        return name;
538    }
539    return mSourceFile;
540}
541
542// =========================================================================
543// =========================================================================
544// =========================================================================
545
546status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
547{
548    ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
549    if (index >= 0 && overwriteDuplicate) {
550        fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
551                mFiles[index]->getSourceFile().string(),
552                file->getSourceFile().string());
553        removeFile(index);
554        index = -1;
555    }
556
557    if (index < 0) {
558        file->mPath = mPath;
559        mFiles.add(file->getGroupEntry(), file);
560        return NO_ERROR;
561    }
562
563    // Check if the version is automatically applied. This is a common source of
564    // error.
565    ConfigDescription withoutVersion = file->getGroupEntry().toParams();
566    withoutVersion.version = 0;
567    AaptConfig::applyVersionForCompatibility(&withoutVersion);
568
569    const sp<AaptFile>& originalFile = mFiles.valueAt(index);
570    SourcePos(file->getSourceFile(), -1)
571            .error("Duplicate file.\n%s: Original is here. %s",
572                   originalFile->getPrintableSource().string(),
573                   (withoutVersion.version != 0) ? "The version qualifier may be implied." : "");
574    return UNKNOWN_ERROR;
575}
576
577void AaptGroup::removeFile(size_t index)
578{
579	mFiles.removeItemsAt(index);
580}
581
582void AaptGroup::print(const String8& prefix) const
583{
584    printf("%s%s\n", prefix.string(), getPath().string());
585    const size_t N=mFiles.size();
586    size_t i;
587    for (i=0; i<N; i++) {
588        sp<AaptFile> file = mFiles.valueAt(i);
589        const AaptGroupEntry& e = file->getGroupEntry();
590        if (file->hasData()) {
591            printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
592                    (int)file->getSize());
593        } else {
594            printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
595                    file->getPrintableSource().string());
596        }
597        //printf("%s  File Group Entry: %s\n", prefix.string(),
598        //        file->getGroupEntry().toDirName(String8()).string());
599    }
600}
601
602String8 AaptGroup::getPrintableSource() const
603{
604    if (mFiles.size() > 0) {
605        // Arbitrarily pull the first source file out of the list.
606        return mFiles.valueAt(0)->getPrintableSource();
607    }
608
609    // Should never hit this case, but to be safe...
610    return getPath();
611
612}
613
614// =========================================================================
615// =========================================================================
616// =========================================================================
617
618status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
619{
620    if (mFiles.indexOfKey(name) >= 0) {
621        return ALREADY_EXISTS;
622    }
623    mFiles.add(name, file);
624    return NO_ERROR;
625}
626
627status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
628{
629    if (mDirs.indexOfKey(name) >= 0) {
630        return ALREADY_EXISTS;
631    }
632    mDirs.add(name, dir);
633    return NO_ERROR;
634}
635
636sp<AaptDir> AaptDir::makeDir(const String8& path)
637{
638    String8 name;
639    String8 remain = path;
640
641    sp<AaptDir> subdir = this;
642    while (name = remain.walkPath(&remain), remain != "") {
643        subdir = subdir->makeDir(name);
644    }
645
646    ssize_t i = subdir->mDirs.indexOfKey(name);
647    if (i >= 0) {
648        return subdir->mDirs.valueAt(i);
649    }
650    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
651    subdir->mDirs.add(name, dir);
652    return dir;
653}
654
655void AaptDir::removeFile(const String8& name)
656{
657    mFiles.removeItem(name);
658}
659
660void AaptDir::removeDir(const String8& name)
661{
662    mDirs.removeItem(name);
663}
664
665status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
666        const bool overwrite)
667{
668    sp<AaptGroup> group;
669    if (mFiles.indexOfKey(leafName) >= 0) {
670        group = mFiles.valueFor(leafName);
671    } else {
672        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
673        mFiles.add(leafName, group);
674    }
675
676    return group->addFile(file, overwrite);
677}
678
679ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
680                            const AaptGroupEntry& kind, const String8& resType,
681                            sp<FilePathStore>& fullResPaths, const bool overwrite)
682{
683    Vector<String8> fileNames;
684    {
685        DIR* dir = NULL;
686
687        dir = opendir(srcDir.string());
688        if (dir == NULL) {
689            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
690            return UNKNOWN_ERROR;
691        }
692
693        /*
694         * Slurp the filenames out of the directory.
695         */
696        while (1) {
697            struct dirent* entry;
698
699            entry = readdir(dir);
700            if (entry == NULL)
701                break;
702
703            if (isHidden(srcDir.string(), entry->d_name))
704                continue;
705
706            String8 name(entry->d_name);
707            fileNames.add(name);
708            // Add fully qualified path for dependency purposes
709            // if we're collecting them
710            if (fullResPaths != NULL) {
711                fullResPaths->add(srcDir.appendPathCopy(name));
712            }
713        }
714        closedir(dir);
715    }
716
717    ssize_t count = 0;
718
719    /*
720     * Stash away the files and recursively descend into subdirectories.
721     */
722    const size_t N = fileNames.size();
723    size_t i;
724    for (i = 0; i < N; i++) {
725        String8 pathName(srcDir);
726        FileType type;
727
728        pathName.appendPath(fileNames[i].string());
729        type = getFileType(pathName.string());
730        if (type == kFileTypeDirectory) {
731            sp<AaptDir> subdir;
732            bool notAdded = false;
733            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
734                subdir = mDirs.valueFor(fileNames[i]);
735            } else {
736                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
737                notAdded = true;
738            }
739            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
740                                                resType, fullResPaths, overwrite);
741            if (res < NO_ERROR) {
742                return res;
743            }
744            if (res > 0 && notAdded) {
745                mDirs.add(fileNames[i], subdir);
746            }
747            count += res;
748        } else if (type == kFileTypeRegular) {
749            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
750            status_t err = addLeafFile(fileNames[i], file, overwrite);
751            if (err != NO_ERROR) {
752                return err;
753            }
754
755            count++;
756
757        } else {
758            if (bundle->getVerbose())
759                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
760        }
761    }
762
763    return count;
764}
765
766status_t AaptDir::validate() const
767{
768    const size_t NF = mFiles.size();
769    const size_t ND = mDirs.size();
770    size_t i;
771    for (i = 0; i < NF; i++) {
772        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
773            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
774                    "Invalid filename.  Unable to add.");
775            return UNKNOWN_ERROR;
776        }
777
778        size_t j;
779        for (j = i+1; j < NF; j++) {
780            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
781                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
782                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
783                        "File is case-insensitive equivalent to: %s",
784                        mFiles.valueAt(j)->getPrintableSource().string());
785                return UNKNOWN_ERROR;
786            }
787
788            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
789            // (this is mostly caught by the "marked" stuff, below)
790        }
791
792        for (j = 0; j < ND; j++) {
793            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
794                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
795                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
796                        "File conflicts with dir from: %s",
797                        mDirs.valueAt(j)->getPrintableSource().string());
798                return UNKNOWN_ERROR;
799            }
800        }
801    }
802
803    for (i = 0; i < ND; i++) {
804        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
805            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
806                    "Invalid directory name, unable to add.");
807            return UNKNOWN_ERROR;
808        }
809
810        size_t j;
811        for (j = i+1; j < ND; j++) {
812            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
813                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
814                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
815                        "Directory is case-insensitive equivalent to: %s",
816                        mDirs.valueAt(j)->getPrintableSource().string());
817                return UNKNOWN_ERROR;
818            }
819        }
820
821        status_t err = mDirs.valueAt(i)->validate();
822        if (err != NO_ERROR) {
823            return err;
824        }
825    }
826
827    return NO_ERROR;
828}
829
830void AaptDir::print(const String8& prefix) const
831{
832    const size_t ND=getDirs().size();
833    size_t i;
834    for (i=0; i<ND; i++) {
835        getDirs().valueAt(i)->print(prefix);
836    }
837
838    const size_t NF=getFiles().size();
839    for (i=0; i<NF; i++) {
840        getFiles().valueAt(i)->print(prefix);
841    }
842}
843
844String8 AaptDir::getPrintableSource() const
845{
846    if (mFiles.size() > 0) {
847        // Arbitrarily pull the first file out of the list as the source dir.
848        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
849    }
850    if (mDirs.size() > 0) {
851        // Or arbitrarily pull the first dir out of the list as the source dir.
852        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
853    }
854
855    // Should never hit this case, but to be safe...
856    return mPath;
857
858}
859
860// =========================================================================
861// =========================================================================
862// =========================================================================
863
864status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
865{
866    status_t err = NO_ERROR;
867    size_t N = javaSymbols->mSymbols.size();
868    for (size_t i=0; i<N; i++) {
869        const String8& name = javaSymbols->mSymbols.keyAt(i);
870        const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
871        ssize_t pos = mSymbols.indexOfKey(name);
872        if (pos < 0) {
873            entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
874            err = UNKNOWN_ERROR;
875            continue;
876        }
877        //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
878        //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
879        mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
880    }
881
882    N = javaSymbols->mNestedSymbols.size();
883    for (size_t i=0; i<N; i++) {
884        const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
885        const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
886        ssize_t pos = mNestedSymbols.indexOfKey(name);
887        if (pos < 0) {
888            SourcePos pos;
889            pos.error("Java symbol dir %s not defined\n", name.string());
890            err = UNKNOWN_ERROR;
891            continue;
892        }
893        //printf("**** applying java symbols in dir %s\n", name.string());
894        status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
895        if (myerr != NO_ERROR) {
896            err = myerr;
897        }
898    }
899
900    return err;
901}
902
903// =========================================================================
904// =========================================================================
905// =========================================================================
906
907AaptAssets::AaptAssets()
908    : AaptDir(String8(), String8()),
909      mHavePrivateSymbols(false),
910      mChanged(false), mHaveIncludedAssets(false),
911      mRes(NULL) {}
912
913const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
914    if (mChanged) {
915    }
916    return mGroupEntries;
917}
918
919status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
920{
921    mChanged = true;
922    return AaptDir::addFile(name, file);
923}
924
925sp<AaptFile> AaptAssets::addFile(
926        const String8& filePath, const AaptGroupEntry& entry,
927        const String8& srcDir, sp<AaptGroup>* outGroup,
928        const String8& resType)
929{
930    sp<AaptDir> dir = this;
931    sp<AaptGroup> group;
932    sp<AaptFile> file;
933    String8 root, remain(filePath), partialPath;
934    while (remain.length() > 0) {
935        root = remain.walkPath(&remain);
936        partialPath.appendPath(root);
937
938        const String8 rootStr(root);
939
940        if (remain.length() == 0) {
941            ssize_t i = dir->getFiles().indexOfKey(rootStr);
942            if (i >= 0) {
943                group = dir->getFiles().valueAt(i);
944            } else {
945                group = new AaptGroup(rootStr, filePath);
946                status_t res = dir->addFile(rootStr, group);
947                if (res != NO_ERROR) {
948                    return NULL;
949                }
950            }
951            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
952            status_t res = group->addFile(file);
953            if (res != NO_ERROR) {
954                return NULL;
955            }
956            break;
957
958        } else {
959            ssize_t i = dir->getDirs().indexOfKey(rootStr);
960            if (i >= 0) {
961                dir = dir->getDirs().valueAt(i);
962            } else {
963                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
964                status_t res = dir->addDir(rootStr, subdir);
965                if (res != NO_ERROR) {
966                    return NULL;
967                }
968                dir = subdir;
969            }
970        }
971    }
972
973    mGroupEntries.add(entry);
974    if (outGroup) *outGroup = group;
975    return file;
976}
977
978void AaptAssets::addResource(const String8& leafName, const String8& path,
979                const sp<AaptFile>& file, const String8& resType)
980{
981    sp<AaptDir> res = AaptDir::makeDir(kResString);
982    String8 dirname = file->getGroupEntry().toDirName(resType);
983    sp<AaptDir> subdir = res->makeDir(dirname);
984    sp<AaptGroup> grr = new AaptGroup(leafName, path);
985    grr->addFile(file);
986
987    subdir->addFile(leafName, grr);
988}
989
990
991ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
992{
993    int count;
994    int totalCount = 0;
995    FileType type;
996    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
997    const size_t dirCount =resDirs.size();
998    sp<AaptAssets> current = this;
999
1000    const int N = bundle->getFileSpecCount();
1001
1002    /*
1003     * If a package manifest was specified, include that first.
1004     */
1005    if (bundle->getAndroidManifestFile() != NULL) {
1006        // place at root of zip.
1007        String8 srcFile(bundle->getAndroidManifestFile());
1008        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1009                NULL, String8());
1010        totalCount++;
1011    }
1012
1013    /*
1014     * If a directory of custom assets was supplied, slurp 'em up.
1015     */
1016    const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
1017    const int AN = assetDirs.size();
1018    for (int i = 0; i < AN; i++) {
1019        FileType type = getFileType(assetDirs[i]);
1020        if (type == kFileTypeNonexistent) {
1021            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
1022            return UNKNOWN_ERROR;
1023        }
1024        if (type != kFileTypeDirectory) {
1025            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
1026            return UNKNOWN_ERROR;
1027        }
1028
1029        String8 assetRoot(assetDirs[i]);
1030        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1031        AaptGroupEntry group;
1032        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1033                                            String8(), mFullAssetPaths, true);
1034        if (count < 0) {
1035            totalCount = count;
1036            goto bail;
1037        }
1038        if (count > 0) {
1039            mGroupEntries.add(group);
1040        }
1041        totalCount += count;
1042
1043        if (bundle->getVerbose()) {
1044            printf("Found %d custom asset file%s in %s\n",
1045                   count, (count==1) ? "" : "s", assetDirs[i]);
1046        }
1047    }
1048
1049    /*
1050     * If a directory of resource-specific assets was supplied, slurp 'em up.
1051     */
1052    for (size_t i=0; i<dirCount; i++) {
1053        const char *res = resDirs[i];
1054        if (res) {
1055            type = getFileType(res);
1056            if (type == kFileTypeNonexistent) {
1057                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1058                return UNKNOWN_ERROR;
1059            }
1060            if (type == kFileTypeDirectory) {
1061                if (i>0) {
1062                    sp<AaptAssets> nextOverlay = new AaptAssets();
1063                    current->setOverlay(nextOverlay);
1064                    current = nextOverlay;
1065                    current->setFullResPaths(mFullResPaths);
1066                }
1067                count = current->slurpResourceTree(bundle, String8(res));
1068                if (i > 0 && count > 0) {
1069                  count = current->filter(bundle);
1070                }
1071
1072                if (count < 0) {
1073                    totalCount = count;
1074                    goto bail;
1075                }
1076                totalCount += count;
1077            }
1078            else {
1079                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1080                return UNKNOWN_ERROR;
1081            }
1082        }
1083
1084    }
1085    /*
1086     * Now do any additional raw files.
1087     */
1088    for (int arg=0; arg<N; arg++) {
1089        const char* assetDir = bundle->getFileSpecEntry(arg);
1090
1091        FileType type = getFileType(assetDir);
1092        if (type == kFileTypeNonexistent) {
1093            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1094            return UNKNOWN_ERROR;
1095        }
1096        if (type != kFileTypeDirectory) {
1097            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1098            return UNKNOWN_ERROR;
1099        }
1100
1101        String8 assetRoot(assetDir);
1102
1103        if (bundle->getVerbose())
1104            printf("Processing raw dir '%s'\n", (const char*) assetDir);
1105
1106        /*
1107         * Do a recursive traversal of subdir tree.  We don't make any
1108         * guarantees about ordering, so we're okay with an inorder search
1109         * using whatever order the OS happens to hand back to us.
1110         */
1111        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
1112        if (count < 0) {
1113            /* failure; report error and remove archive */
1114            totalCount = count;
1115            goto bail;
1116        }
1117        totalCount += count;
1118
1119        if (bundle->getVerbose())
1120            printf("Found %d asset file%s in %s\n",
1121                   count, (count==1) ? "" : "s", assetDir);
1122    }
1123
1124    count = validate();
1125    if (count != NO_ERROR) {
1126        totalCount = count;
1127        goto bail;
1128    }
1129
1130    count = filter(bundle);
1131    if (count != NO_ERROR) {
1132        totalCount = count;
1133        goto bail;
1134    }
1135
1136bail:
1137    return totalCount;
1138}
1139
1140ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1141                                    const AaptGroupEntry& kind,
1142                                    const String8& resType,
1143                                    sp<FilePathStore>& fullResPaths)
1144{
1145    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
1146    if (res > 0) {
1147        mGroupEntries.add(kind);
1148    }
1149
1150    return res;
1151}
1152
1153ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1154{
1155    ssize_t err = 0;
1156
1157    DIR* dir = opendir(srcDir.string());
1158    if (dir == NULL) {
1159        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1160        return UNKNOWN_ERROR;
1161    }
1162
1163    status_t count = 0;
1164
1165    /*
1166     * Run through the directory, looking for dirs that match the
1167     * expected pattern.
1168     */
1169    while (1) {
1170        struct dirent* entry = readdir(dir);
1171        if (entry == NULL) {
1172            break;
1173        }
1174
1175        if (isHidden(srcDir.string(), entry->d_name)) {
1176            continue;
1177        }
1178
1179        String8 subdirName(srcDir);
1180        subdirName.appendPath(entry->d_name);
1181
1182        AaptGroupEntry group;
1183        String8 resType;
1184        bool b = group.initFromDirName(entry->d_name, &resType);
1185        if (!b) {
1186            fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
1187                    entry->d_name);
1188            err = -1;
1189            continue;
1190        }
1191
1192        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
1193            int maxResInt = atoi(bundle->getMaxResVersion());
1194            const char *verString = group.getVersionString().string();
1195            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
1196            if (dirVersionInt > maxResInt) {
1197              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
1198              continue;
1199            }
1200        }
1201
1202        FileType type = getFileType(subdirName.string());
1203
1204        if (type == kFileTypeDirectory) {
1205            sp<AaptDir> dir = makeDir(resType);
1206            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1207                                                resType, mFullResPaths);
1208            if (res < 0) {
1209                count = res;
1210                goto bail;
1211            }
1212            if (res > 0) {
1213                mGroupEntries.add(group);
1214                count += res;
1215            }
1216
1217            // Only add this directory if we don't already have a resource dir
1218            // for the current type.  This ensures that we only add the dir once
1219            // for all configs.
1220            sp<AaptDir> rdir = resDir(resType);
1221            if (rdir == NULL) {
1222                mResDirs.add(dir);
1223            }
1224        } else {
1225            if (bundle->getVerbose()) {
1226                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
1227            }
1228        }
1229    }
1230
1231bail:
1232    closedir(dir);
1233    dir = NULL;
1234
1235    if (err != 0) {
1236        return err;
1237    }
1238    return count;
1239}
1240
1241ssize_t
1242AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename)
1243{
1244    int count = 0;
1245    SortedVector<AaptGroupEntry> entries;
1246
1247    ZipFile* zip = new ZipFile;
1248    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1249    if (err != NO_ERROR) {
1250        fprintf(stderr, "error opening zip file %s\n", filename);
1251        count = err;
1252        delete zip;
1253        return -1;
1254    }
1255
1256    const int N = zip->getNumEntries();
1257    for (int i=0; i<N; i++) {
1258        ZipEntry* entry = zip->getEntryByIndex(i);
1259        if (entry->getDeleted()) {
1260            continue;
1261        }
1262
1263        String8 entryName(entry->getFileName());
1264
1265        String8 dirName = entryName.getPathDir();
1266        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1267
1268        String8 resType;
1269        AaptGroupEntry kind;
1270
1271        String8 remain;
1272        if (entryName.walkPath(&remain) == kResourceDir) {
1273            // these are the resources, pull their type out of the directory name
1274            kind.initFromDirName(remain.walkPath().string(), &resType);
1275        } else {
1276            // these are untyped and don't have an AaptGroupEntry
1277        }
1278        if (entries.indexOf(kind) < 0) {
1279            entries.add(kind);
1280            mGroupEntries.add(kind);
1281        }
1282
1283        // use the one from the zip file if they both exist.
1284        dir->removeFile(entryName.getPathLeaf());
1285
1286        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
1287        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
1288        if (err != NO_ERROR) {
1289            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
1290            count = err;
1291            goto bail;
1292        }
1293        file->setCompressionMethod(entry->getCompressionMethod());
1294
1295#if 0
1296        if (entryName == "AndroidManifest.xml") {
1297            printf("AndroidManifest.xml\n");
1298        }
1299        printf("\n\nfile: %s\n", entryName.string());
1300#endif
1301
1302        size_t len = entry->getUncompressedLen();
1303        void* data = zip->uncompress(entry);
1304        void* buf = file->editData(len);
1305        memcpy(buf, data, len);
1306
1307#if 0
1308        const int OFF = 0;
1309        const unsigned char* p = (unsigned char*)data;
1310        const unsigned char* end = p+len;
1311        p += OFF;
1312        for (int i=0; i<32 && p < end; i++) {
1313            printf("0x%03x ", i*0x10 + OFF);
1314            for (int j=0; j<0x10 && p < end; j++) {
1315                printf(" %02x", *p);
1316                p++;
1317            }
1318            printf("\n");
1319        }
1320#endif
1321
1322        free(data);
1323
1324        count++;
1325    }
1326
1327bail:
1328    delete zip;
1329    return count;
1330}
1331
1332status_t AaptAssets::filter(Bundle* bundle)
1333{
1334    WeakResourceFilter reqFilter;
1335    status_t err = reqFilter.parse(bundle->getConfigurations());
1336    if (err != NO_ERROR) {
1337        return err;
1338    }
1339
1340    uint32_t preferredDensity = 0;
1341    if (bundle->getPreferredDensity().size() > 0) {
1342        ResTable_config preferredConfig;
1343        if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
1344            fprintf(stderr, "Error parsing preferred density: %s\n",
1345                    bundle->getPreferredDensity().string());
1346            return UNKNOWN_ERROR;
1347        }
1348        preferredDensity = preferredConfig.density;
1349    }
1350
1351    if (reqFilter.isEmpty() && preferredDensity == 0) {
1352        return NO_ERROR;
1353    }
1354
1355    if (bundle->getVerbose()) {
1356        if (!reqFilter.isEmpty()) {
1357            printf("Applying required filter: %s\n",
1358                    bundle->getConfigurations().string());
1359        }
1360        if (preferredDensity > 0) {
1361            printf("Applying preferred density filter: %s\n",
1362                    bundle->getPreferredDensity().string());
1363        }
1364    }
1365
1366    const Vector<sp<AaptDir> >& resdirs = mResDirs;
1367    const size_t ND = resdirs.size();
1368    for (size_t i=0; i<ND; i++) {
1369        const sp<AaptDir>& dir = resdirs.itemAt(i);
1370        if (dir->getLeaf() == kValuesDir) {
1371            // The "value" dir is special since a single file defines
1372            // multiple resources, so we can not do filtering on the
1373            // files themselves.
1374            continue;
1375        }
1376        if (dir->getLeaf() == kMipmapDir) {
1377            // We also skip the "mipmap" directory, since the point of this
1378            // is to include all densities without stripping.  If you put
1379            // other configurations in here as well they won't be stripped
1380            // either...  So don't do that.  Seriously.  What is wrong with you?
1381            continue;
1382        }
1383
1384        const size_t NG = dir->getFiles().size();
1385        for (size_t j=0; j<NG; j++) {
1386            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
1387
1388            // First remove any configurations we know we don't need.
1389            for (size_t k=0; k<grp->getFiles().size(); k++) {
1390                sp<AaptFile> file = grp->getFiles().valueAt(k);
1391                if (k == 0 && grp->getFiles().size() == 1) {
1392                    // If this is the only file left, we need to keep it.
1393                    // Otherwise the resource IDs we are using will be inconsistent
1394                    // with what we get when not stripping.  Sucky, but at least
1395                    // for now we can rely on the back-end doing another filtering
1396                    // pass to take this out and leave us with this resource name
1397                    // containing no entries.
1398                    continue;
1399                }
1400                if (file->getPath().getPathExtension() == ".xml") {
1401                    // We can't remove .xml files at this point, because when
1402                    // we parse them they may add identifier resources, so
1403                    // removing them can cause our resource identifiers to
1404                    // become inconsistent.
1405                    continue;
1406                }
1407                const ResTable_config& config(file->getGroupEntry().toParams());
1408                if (!reqFilter.match(config)) {
1409                    if (bundle->getVerbose()) {
1410                        printf("Pruning unneeded resource: %s\n",
1411                                file->getPrintableSource().string());
1412                    }
1413                    grp->removeFile(k);
1414                    k--;
1415                }
1416            }
1417
1418            // Quick check: no preferred filters, nothing more to do.
1419            if (preferredDensity == 0) {
1420                continue;
1421            }
1422
1423            // Get the preferred density if there is one. We do not match exactly for density.
1424            // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
1425            // pick xhdpi.
1426            for (size_t k=0; k<grp->getFiles().size(); k++) {
1427                sp<AaptFile> file = grp->getFiles().valueAt(k);
1428                if (k == 0 && grp->getFiles().size() == 1) {
1429                    // If this is the only file left, we need to keep it.
1430                    // Otherwise the resource IDs we are using will be inconsistent
1431                    // with what we get when not stripping.  Sucky, but at least
1432                    // for now we can rely on the back-end doing another filtering
1433                    // pass to take this out and leave us with this resource name
1434                    // containing no entries.
1435                    continue;
1436                }
1437                if (file->getPath().getPathExtension() == ".xml") {
1438                    // We can't remove .xml files at this point, because when
1439                    // we parse them they may add identifier resources, so
1440                    // removing them can cause our resource identifiers to
1441                    // become inconsistent.
1442                    continue;
1443                }
1444                const ResTable_config& config(file->getGroupEntry().toParams());
1445                if (config.density != 0 && config.density != preferredDensity) {
1446                    // This is a resource we would prefer not to have.  Check
1447                    // to see if have a similar variation that we would like
1448                    // to have and, if so, we can drop it.
1449                    uint32_t bestDensity = config.density;
1450
1451                    for (size_t m=0; m<grp->getFiles().size(); m++) {
1452                        if (m == k) {
1453                            continue;
1454                        }
1455
1456                        sp<AaptFile> mfile = grp->getFiles().valueAt(m);
1457                        const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
1458                        if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
1459                            // See if there is a better density resource
1460                            if (mconfig.density < bestDensity &&
1461                                    mconfig.density >= preferredDensity &&
1462                                    bestDensity > preferredDensity) {
1463                                // This density is our preferred density, or between our best density and
1464                                // the preferred density, therefore it is better.
1465                                bestDensity = mconfig.density;
1466                            } else if (mconfig.density > bestDensity &&
1467                                    bestDensity < preferredDensity) {
1468                                // This density is better than our best density and
1469                                // our best density was smaller than our preferred
1470                                // density, so it is better.
1471                                bestDensity = mconfig.density;
1472                            }
1473                        }
1474                    }
1475
1476                    if (bestDensity != config.density) {
1477                        if (bundle->getVerbose()) {
1478                            printf("Pruning unneeded resource: %s\n",
1479                                    file->getPrintableSource().string());
1480                        }
1481                        grp->removeFile(k);
1482                        k--;
1483                    }
1484                }
1485            }
1486        }
1487    }
1488
1489    return NO_ERROR;
1490}
1491
1492sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1493{
1494    sp<AaptSymbols> sym = mSymbols.valueFor(name);
1495    if (sym == NULL) {
1496        sym = new AaptSymbols();
1497        mSymbols.add(name, sym);
1498    }
1499    return sym;
1500}
1501
1502sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
1503{
1504    sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
1505    if (sym == NULL) {
1506        sym = new AaptSymbols();
1507        mJavaSymbols.add(name, sym);
1508    }
1509    return sym;
1510}
1511
1512status_t AaptAssets::applyJavaSymbols()
1513{
1514    size_t N = mJavaSymbols.size();
1515    for (size_t i=0; i<N; i++) {
1516        const String8& name = mJavaSymbols.keyAt(i);
1517        const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
1518        ssize_t pos = mSymbols.indexOfKey(name);
1519        if (pos < 0) {
1520            SourcePos pos;
1521            pos.error("Java symbol dir %s not defined\n", name.string());
1522            return UNKNOWN_ERROR;
1523        }
1524        //printf("**** applying java symbols in dir %s\n", name.string());
1525        status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1526        if (err != NO_ERROR) {
1527            return err;
1528        }
1529    }
1530
1531    return NO_ERROR;
1532}
1533
1534bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
1535    //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
1536    //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
1537    //        sym.isJavaSymbol ? 1 : 0);
1538    if (!mHavePrivateSymbols) return true;
1539    if (sym.isPublic) return true;
1540    if (includePrivate && sym.isJavaSymbol) return true;
1541    return false;
1542}
1543
1544status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1545{
1546    if (mHaveIncludedAssets) {
1547        return NO_ERROR;
1548    }
1549
1550    // Add in all includes.
1551    const Vector<String8>& includes = bundle->getPackageIncludes();
1552    const size_t packageIncludeCount = includes.size();
1553    for (size_t i = 0; i < packageIncludeCount; i++) {
1554        if (bundle->getVerbose()) {
1555            printf("Including resources from package: %s\n", includes[i].string());
1556        }
1557
1558        if (!mIncludedAssets.addAssetPath(includes[i], NULL)) {
1559            fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
1560                    includes[i].string());
1561            return UNKNOWN_ERROR;
1562        }
1563    }
1564
1565    const String8& featureOfBase = bundle->getFeatureOfPackage();
1566    if (!featureOfBase.isEmpty()) {
1567        if (bundle->getVerbose()) {
1568            printf("Including base feature resources from package: %s\n",
1569                    featureOfBase.string());
1570        }
1571
1572        if (!mIncludedAssets.addAssetPath(featureOfBase, NULL)) {
1573            fprintf(stderr, "ERROR: base feature package '%s' not found.\n",
1574                    featureOfBase.string());
1575            return UNKNOWN_ERROR;
1576        }
1577    }
1578
1579    mHaveIncludedAssets = true;
1580
1581    return NO_ERROR;
1582}
1583
1584status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
1585{
1586    const ResTable& res = getIncludedResources();
1587    // XXX dirty!
1588    return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
1589}
1590
1591const ResTable& AaptAssets::getIncludedResources() const
1592{
1593    return mIncludedAssets.getResources(false);
1594}
1595
1596AssetManager& AaptAssets::getAssetManager()
1597{
1598    return mIncludedAssets;
1599}
1600
1601void AaptAssets::print(const String8& prefix) const
1602{
1603    String8 innerPrefix(prefix);
1604    innerPrefix.append("  ");
1605    String8 innerInnerPrefix(innerPrefix);
1606    innerInnerPrefix.append("  ");
1607    printf("%sConfigurations:\n", prefix.string());
1608    const size_t N=mGroupEntries.size();
1609    for (size_t i=0; i<N; i++) {
1610        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
1611        printf("%s %s\n", prefix.string(),
1612                cname != "" ? cname.string() : "(default)");
1613    }
1614
1615    printf("\n%sFiles:\n", prefix.string());
1616    AaptDir::print(innerPrefix);
1617
1618    printf("\n%sResource Dirs:\n", prefix.string());
1619    const Vector<sp<AaptDir> >& resdirs = mResDirs;
1620    const size_t NR = resdirs.size();
1621    for (size_t i=0; i<NR; i++) {
1622        const sp<AaptDir>& d = resdirs.itemAt(i);
1623        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
1624        d->print(innerInnerPrefix);
1625    }
1626}
1627
1628sp<AaptDir> AaptAssets::resDir(const String8& name) const
1629{
1630    const Vector<sp<AaptDir> >& resdirs = mResDirs;
1631    const size_t N = resdirs.size();
1632    for (size_t i=0; i<N; i++) {
1633        const sp<AaptDir>& d = resdirs.itemAt(i);
1634        if (d->getLeaf() == name) {
1635            return d;
1636        }
1637    }
1638    return NULL;
1639}
1640
1641bool
1642valid_symbol_name(const String8& symbol)
1643{
1644    static char const * const KEYWORDS[] = {
1645        "abstract", "assert", "boolean", "break",
1646        "byte", "case", "catch", "char", "class", "const", "continue",
1647        "default", "do", "double", "else", "enum", "extends", "final",
1648        "finally", "float", "for", "goto", "if", "implements", "import",
1649        "instanceof", "int", "interface", "long", "native", "new", "package",
1650        "private", "protected", "public", "return", "short", "static",
1651        "strictfp", "super", "switch", "synchronized", "this", "throw",
1652        "throws", "transient", "try", "void", "volatile", "while",
1653        "true", "false", "null",
1654        NULL
1655    };
1656    const char*const* k = KEYWORDS;
1657    const char*const s = symbol.string();
1658    while (*k) {
1659        if (0 == strcmp(s, *k)) {
1660            return false;
1661        }
1662        k++;
1663    }
1664    return true;
1665}
1666