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