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