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