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