AaptAssets.cpp revision 788fa41482b9d398591b7db8b0b01839029611ad
1//
2// Copyright 2006 The Android Open Source Project
3//
4
5#include "AaptAssets.h"
6#include "ResourceFilter.h"
7#include "Main.h"
8
9#include <utils/misc.h>
10#include <utils/SortedVector.h>
11
12#include <ctype.h>
13#include <dirent.h>
14#include <errno.h>
15
16static const char* kDefaultLocale = "default";
17static const char* kWildcardName = "any";
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 */ void AaptLocaleValue::splitAndLowerCase(const char* const chars,
153        Vector<String8>* parts, const char separator) {
154    const char *p = chars;
155    const char *q;
156    while (NULL != (q = strchr(p, separator))) {
157         String8 val(p, q - p);
158         val.toLower();
159         parts->add(val);
160         p = q+1;
161    }
162
163    if (p < chars + strlen(chars)) {
164        String8 val(p);
165        val.toLower();
166        parts->add(val);
167    }
168}
169
170/* static */
171inline bool isAlpha(const String8& string) {
172     const size_t length = string.length();
173     for (size_t i = 0; i < length; ++i) {
174          if (!isalpha(string[i])) {
175              return false;
176          }
177     }
178
179     return true;
180}
181
182/* static */
183inline bool isNumber(const String8& string) {
184     const size_t length = string.length();
185     for (size_t i = 0; i < length; ++i) {
186          if (!isdigit(string[i])) {
187              return false;
188          }
189     }
190
191     return true;
192}
193
194void AaptLocaleValue::setLanguage(const char* languageChars) {
195     size_t i = 0;
196     while ((*languageChars) != '\0') {
197          language[i++] = tolower(*languageChars);
198          languageChars++;
199     }
200}
201
202void AaptLocaleValue::setRegion(const char* regionChars) {
203    size_t i = 0;
204    while ((*regionChars) != '\0') {
205         region[i++] = toupper(*regionChars);
206         regionChars++;
207    }
208}
209
210void AaptLocaleValue::setScript(const char* scriptChars) {
211    size_t i = 0;
212    while ((*scriptChars) != '\0') {
213         if (i == 0) {
214             script[i++] = toupper(*scriptChars);
215         } else {
216             script[i++] = tolower(*scriptChars);
217         }
218         scriptChars++;
219    }
220}
221
222void AaptLocaleValue::setVariant(const char* variantChars) {
223     size_t i = 0;
224     while ((*variantChars) != '\0') {
225          variant[i++] = *variantChars;
226          variantChars++;
227     }
228}
229
230bool AaptLocaleValue::initFromFilterString(const String8& str) {
231     // A locale (as specified in the filter) is an underscore separated name such
232     // as "en_US", "en_Latn_US", or "en_US_POSIX".
233     Vector<String8> parts;
234     splitAndLowerCase(str.string(), &parts, '_');
235
236     const int numTags = parts.size();
237     bool valid = false;
238     if (numTags >= 1) {
239         const String8& lang = parts[0];
240         if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
241             setLanguage(lang.string());
242             valid = true;
243         }
244     }
245
246     if (!valid || numTags == 1) {
247         return valid;
248     }
249
250     // At this point, valid == true && numTags > 1.
251     const String8& part2 = parts[1];
252     if ((part2.length() == 2 && isAlpha(part2)) ||
253         (part2.length() == 3 && isNumber(part2))) {
254         setRegion(part2.string());
255     } else if (part2.length() == 4 && isAlpha(part2)) {
256         setScript(part2.string());
257     } else if (part2.length() >= 5 && part2.length() <= 8) {
258         setVariant(part2.string());
259     } else {
260         valid = false;
261     }
262
263     if (!valid || numTags == 2) {
264         return valid;
265     }
266
267     // At this point, valid == true && numTags > 1.
268     const String8& part3 = parts[2];
269     if (((part3.length() == 2 && isAlpha(part3)) ||
270         (part3.length() == 3 && isNumber(part3))) && script[0]) {
271         setRegion(part3.string());
272     } else if (part3.length() >= 5 && part3.length() <= 8) {
273         setVariant(part3.string());
274     } else {
275         valid = false;
276     }
277
278     if (!valid || numTags == 3) {
279         return valid;
280     }
281
282     const String8& part4 = parts[3];
283     if (part4.length() >= 5 && part4.length() <= 8) {
284         setVariant(part4.string());
285     } else {
286         valid = false;
287     }
288
289     if (!valid || numTags > 4) {
290         return false;
291     }
292
293     return true;
294}
295
296int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
297    const int size = parts.size();
298    int currentIndex = startIndex;
299
300    String8 part = parts[currentIndex];
301    if (part[0] == 'b' && part[1] == '+') {
302        // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
303        // except that the separator is "+" and not "-".
304        Vector<String8> subtags;
305        AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+');
306        subtags.removeItemsAt(0);
307        if (subtags.size() == 1) {
308            setLanguage(subtags[0]);
309        } else if (subtags.size() == 2) {
310            setLanguage(subtags[0]);
311
312            // The second tag can either be a region, a variant or a script.
313            switch (subtags[1].size()) {
314                case 2:
315                case 3:
316                    setRegion(subtags[1]);
317                    break;
318                case 4:
319                    setScript(subtags[1]);
320                    break;
321                case 5:
322                case 6:
323                case 7:
324                case 8:
325                    setVariant(subtags[1]);
326                    break;
327                default:
328                    fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n",
329                            part.string());
330                    return -1;
331            }
332        } else if (subtags.size() == 3) {
333            // The language is always the first subtag.
334            setLanguage(subtags[0]);
335
336            // The second subtag can either be a script or a region code.
337            // If its size is 4, it's a script code, else it's a region code.
338            bool hasRegion = false;
339            if (subtags[1].size() == 4) {
340                setScript(subtags[1]);
341            } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
342                setRegion(subtags[1]);
343                hasRegion = true;
344            } else {
345                fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", part.string());
346                return -1;
347            }
348
349            // The third tag can either be a region code (if the second tag was
350            // a script), else a variant code.
351            if (subtags[2].size() > 4) {
352                setVariant(subtags[2]);
353            } else {
354                setRegion(subtags[2]);
355            }
356        } else if (subtags.size() == 4) {
357            setLanguage(subtags[0]);
358            setScript(subtags[1]);
359            setRegion(subtags[2]);
360            setVariant(subtags[3]);
361        } else {
362            fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name: %s\n", part.string());
363            return -1;
364        }
365
366        return ++currentIndex;
367    } else {
368        if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) {
369            setLanguage(part);
370            if (++currentIndex == size) {
371                return size;
372            }
373        } else {
374            return currentIndex;
375        }
376
377        part = parts[currentIndex];
378        if (part.string()[0] == 'r' && part.length() == 3) {
379            setRegion(part.string() + 1);
380            if (++currentIndex == size) {
381                return size;
382            }
383        }
384    }
385
386    return currentIndex;
387}
388
389
390String8 AaptLocaleValue::toDirName() const {
391    String8 dirName("");
392    if (language[0]) {
393        dirName += language;
394    } else {
395        return dirName;
396    }
397
398    if (script[0]) {
399        dirName += "-s";
400        dirName += script;
401    }
402
403    if (region[0]) {
404        dirName += "-r";
405        dirName += region;
406    }
407
408    if (variant[0]) {
409        dirName += "-v";
410        dirName += variant;
411    }
412
413    return dirName;
414}
415
416void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
417    config.unpackLanguage(language);
418    config.unpackRegion(region);
419    if (config.localeScript[0]) {
420        memcpy(script, config.localeScript, sizeof(config.localeScript));
421    }
422
423    if (config.localeVariant[0]) {
424        memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
425    }
426}
427
428void AaptLocaleValue::writeTo(ResTable_config* out) const {
429    out->packLanguage(language);
430    out->packRegion(region);
431
432    if (script[0]) {
433        memcpy(out->localeScript, script, sizeof(out->localeScript));
434    }
435
436    if (variant[0]) {
437        memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
438    }
439}
440
441
442/* static */ bool
443AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value)
444{
445    ResTable_config config;
446    memset(&config, 0, sizeof(ResTable_config));
447
448    // IMSI - MCC
449    if (getMccName(part.string(), &config)) {
450        *axis = AXIS_MCC;
451        value->intValue = config.mcc;
452        return true;
453    }
454
455    // IMSI - MNC
456    if (getMncName(part.string(), &config)) {
457        *axis = AXIS_MNC;
458        value->intValue = config.mnc;
459        return true;
460    }
461
462    // locale - language
463    if (value->localeValue.initFromFilterString(part)) {
464        *axis = AXIS_LOCALE;
465        return true;
466    }
467
468    // layout direction
469    if (getLayoutDirectionName(part.string(), &config)) {
470        *axis = AXIS_LAYOUTDIR;
471        value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
472        return true;
473    }
474
475    // smallest screen dp width
476    if (getSmallestScreenWidthDpName(part.string(), &config)) {
477        *axis = AXIS_SMALLESTSCREENWIDTHDP;
478        value->intValue = config.smallestScreenWidthDp;
479        return true;
480    }
481
482    // screen dp width
483    if (getScreenWidthDpName(part.string(), &config)) {
484        *axis = AXIS_SCREENWIDTHDP;
485        value->intValue = config.screenWidthDp;
486        return true;
487    }
488
489    // screen dp height
490    if (getScreenHeightDpName(part.string(), &config)) {
491        *axis = AXIS_SCREENHEIGHTDP;
492        value->intValue = config.screenHeightDp;
493        return true;
494    }
495
496    // screen layout size
497    if (getScreenLayoutSizeName(part.string(), &config)) {
498        *axis = AXIS_SCREENLAYOUTSIZE;
499        value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
500        return true;
501    }
502
503    // screen layout long
504    if (getScreenLayoutLongName(part.string(), &config)) {
505        *axis = AXIS_SCREENLAYOUTLONG;
506        value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
507        return true;
508    }
509
510    // orientation
511    if (getOrientationName(part.string(), &config)) {
512        *axis = AXIS_ORIENTATION;
513        value->intValue = config.orientation;
514        return true;
515    }
516
517    // ui mode type
518    if (getUiModeTypeName(part.string(), &config)) {
519        *axis = AXIS_UIMODETYPE;
520        value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
521        return true;
522    }
523
524    // ui mode night
525    if (getUiModeNightName(part.string(), &config)) {
526        *axis = AXIS_UIMODENIGHT;
527        value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
528        return true;
529    }
530
531    // density
532    if (getDensityName(part.string(), &config)) {
533        *axis = AXIS_DENSITY;
534        value->intValue = config.density;
535        return true;
536    }
537
538    // touchscreen
539    if (getTouchscreenName(part.string(), &config)) {
540        *axis = AXIS_TOUCHSCREEN;
541        value->intValue = config.touchscreen;
542        return true;
543    }
544
545    // keyboard hidden
546    if (getKeysHiddenName(part.string(), &config)) {
547        *axis = AXIS_KEYSHIDDEN;
548        value->intValue = config.inputFlags;
549        return true;
550    }
551
552    // keyboard
553    if (getKeyboardName(part.string(), &config)) {
554        *axis = AXIS_KEYBOARD;
555        value->intValue = config.keyboard;
556        return true;
557    }
558
559    // navigation hidden
560    if (getNavHiddenName(part.string(), &config)) {
561        *axis = AXIS_NAVHIDDEN;
562        value->intValue = config.inputFlags;
563        return 0;
564    }
565
566    // navigation
567    if (getNavigationName(part.string(), &config)) {
568        *axis = AXIS_NAVIGATION;
569        value->intValue = config.navigation;
570        return true;
571    }
572
573    // screen size
574    if (getScreenSizeName(part.string(), &config)) {
575        *axis = AXIS_SCREENSIZE;
576        value->intValue = config.screenSize;
577        return true;
578    }
579
580    // version
581    if (getVersionName(part.string(), &config)) {
582        *axis = AXIS_VERSION;
583        value->intValue = config.version;
584        return true;
585    }
586
587    return false;
588}
589
590AxisValue
591AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
592{
593    AxisValue value;
594    switch (axis) {
595        case AXIS_MCC:
596            value.intValue = config.mcc;
597            break;
598        case AXIS_MNC:
599            value.intValue = config.mnc;
600            break;
601        case AXIS_LOCALE:
602            value.localeValue.initFromResTable(config);
603            break;
604        case AXIS_LAYOUTDIR:
605            value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
606            break;
607        case AXIS_SCREENLAYOUTSIZE:
608            value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
609            break;
610        case AXIS_ORIENTATION:
611            value.intValue = config.orientation;
612            break;
613        case AXIS_UIMODETYPE:
614            value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
615            break;
616        case AXIS_UIMODENIGHT:
617            value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
618            break;
619        case AXIS_DENSITY:
620            value.intValue = config.density;
621            break;
622        case AXIS_TOUCHSCREEN:
623            value.intValue = config.touchscreen;
624            break;
625        case AXIS_KEYSHIDDEN:
626            value.intValue = config.inputFlags;
627            break;
628        case AXIS_KEYBOARD:
629            value.intValue = config.keyboard;
630            break;
631        case AXIS_NAVIGATION:
632            value.intValue = config.navigation;
633            break;
634        case AXIS_SCREENSIZE:
635            value.intValue = config.screenSize;
636            break;
637        case AXIS_SMALLESTSCREENWIDTHDP:
638            value.intValue = config.smallestScreenWidthDp;
639            break;
640        case AXIS_SCREENWIDTHDP:
641            value.intValue = config.screenWidthDp;
642            break;
643        case AXIS_SCREENHEIGHTDP:
644            value.intValue = config.screenHeightDp;
645            break;
646        case AXIS_VERSION:
647            value.intValue = config.version;
648            break;
649    }
650
651    return value;
652}
653
654bool
655AaptGroupEntry::configSameExcept(const ResTable_config& config,
656        const ResTable_config& otherConfig, int axis)
657{
658    for (int i=AXIS_START; i<=AXIS_END; i++) {
659        if (i == axis) {
660            continue;
661        }
662        if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
663            return false;
664        }
665    }
666    return true;
667}
668
669bool
670AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
671{
672    mParamsChanged = true;
673
674    Vector<String8> parts;
675    AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
676
677    String8 mcc, mnc, layoutsize, layoutlong, orient, den;
678    String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
679    String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
680
681    AaptLocaleValue locale;
682    int numLocaleComponents = 0;
683
684    const int N = parts.size();
685    int index = 0;
686    String8 part = parts[index];
687
688    // resource type
689    if (!isValidResourceType(part)) {
690        return false;
691    }
692    *resType = part;
693
694    index++;
695    if (index == N) {
696        goto success;
697    }
698    part = parts[index];
699
700    // imsi - mcc
701    if (getMccName(part.string())) {
702        mcc = part;
703
704        index++;
705        if (index == N) {
706            goto success;
707        }
708        part = parts[index];
709    } else {
710        //printf("not mcc: %s\n", part.string());
711    }
712
713    // imsi - mnc
714    if (getMncName(part.string())) {
715        mnc = part;
716
717        index++;
718        if (index == N) {
719            goto success;
720        }
721        part = parts[index];
722    } else {
723        //printf("not mnc: %s\n", part.string());
724    }
725
726    index = locale.initFromDirName(parts, index);
727    if (index == -1) {
728        return false;
729    }
730    if (index >= N){
731        goto success;
732    }
733
734    part = parts[index];
735    if (getLayoutDirectionName(part.string())) {
736        layoutDir = part;
737
738        index++;
739        if (index == N) {
740            goto success;
741        }
742        part = parts[index];
743    } else {
744        //printf("not layout direction: %s\n", part.string());
745    }
746
747    if (getSmallestScreenWidthDpName(part.string())) {
748        smallestwidthdp = part;
749
750        index++;
751        if (index == N) {
752            goto success;
753        }
754        part = parts[index];
755    } else {
756        //printf("not smallest screen width dp: %s\n", part.string());
757    }
758
759    if (getScreenWidthDpName(part.string())) {
760        widthdp = part;
761
762        index++;
763        if (index == N) {
764            goto success;
765        }
766        part = parts[index];
767    } else {
768        //printf("not screen width dp: %s\n", part.string());
769    }
770
771    if (getScreenHeightDpName(part.string())) {
772        heightdp = part;
773
774        index++;
775        if (index == N) {
776            goto success;
777        }
778        part = parts[index];
779    } else {
780        //printf("not screen height dp: %s\n", part.string());
781    }
782
783    if (getScreenLayoutSizeName(part.string())) {
784        layoutsize = part;
785
786        index++;
787        if (index == N) {
788            goto success;
789        }
790        part = parts[index];
791    } else {
792        //printf("not screen layout size: %s\n", part.string());
793    }
794
795    if (getScreenLayoutLongName(part.string())) {
796        layoutlong = part;
797
798        index++;
799        if (index == N) {
800            goto success;
801        }
802        part = parts[index];
803    } else {
804        //printf("not screen layout long: %s\n", part.string());
805    }
806
807    // orientation
808    if (getOrientationName(part.string())) {
809        orient = part;
810
811        index++;
812        if (index == N) {
813            goto success;
814        }
815        part = parts[index];
816    } else {
817        //printf("not orientation: %s\n", part.string());
818    }
819
820    // ui mode type
821    if (getUiModeTypeName(part.string())) {
822        uiModeType = part;
823
824        index++;
825        if (index == N) {
826            goto success;
827        }
828        part = parts[index];
829    } else {
830        //printf("not ui mode type: %s\n", part.string());
831    }
832
833    // ui mode night
834    if (getUiModeNightName(part.string())) {
835        uiModeNight = part;
836
837        index++;
838        if (index == N) {
839            goto success;
840        }
841        part = parts[index];
842    } else {
843        //printf("not ui mode night: %s\n", part.string());
844    }
845
846    // density
847    if (getDensityName(part.string())) {
848        den = part;
849
850        index++;
851        if (index == N) {
852            goto success;
853        }
854        part = parts[index];
855    } else {
856        //printf("not density: %s\n", part.string());
857    }
858
859    // touchscreen
860    if (getTouchscreenName(part.string())) {
861        touch = part;
862
863        index++;
864        if (index == N) {
865            goto success;
866        }
867        part = parts[index];
868    } else {
869        //printf("not touchscreen: %s\n", part.string());
870    }
871
872    // keyboard hidden
873    if (getKeysHiddenName(part.string())) {
874        keysHidden = part;
875
876        index++;
877        if (index == N) {
878            goto success;
879        }
880        part = parts[index];
881    } else {
882        //printf("not keysHidden: %s\n", part.string());
883    }
884
885    // keyboard
886    if (getKeyboardName(part.string())) {
887        key = part;
888
889        index++;
890        if (index == N) {
891            goto success;
892        }
893        part = parts[index];
894    } else {
895        //printf("not keyboard: %s\n", part.string());
896    }
897
898    // navigation hidden
899    if (getNavHiddenName(part.string())) {
900        navHidden = part;
901
902        index++;
903        if (index == N) {
904            goto success;
905        }
906        part = parts[index];
907    } else {
908        //printf("not navHidden: %s\n", part.string());
909    }
910
911    if (getNavigationName(part.string())) {
912        nav = part;
913
914        index++;
915        if (index == N) {
916            goto success;
917        }
918        part = parts[index];
919    } else {
920        //printf("not navigation: %s\n", part.string());
921    }
922
923    if (getScreenSizeName(part.string())) {
924        size = part;
925
926        index++;
927        if (index == N) {
928            goto success;
929        }
930        part = parts[index];
931    } else {
932        //printf("not screen size: %s\n", part.string());
933    }
934
935    if (getVersionName(part.string())) {
936        vers = part;
937
938        index++;
939        if (index == N) {
940            goto success;
941        }
942        part = parts[index];
943    } else {
944        //printf("not version: %s\n", part.string());
945    }
946
947    // if there are extra parts, it doesn't match
948    return false;
949
950success:
951    this->mcc = mcc;
952    this->mnc = mnc;
953    this->locale = locale;
954    this->screenLayoutSize = layoutsize;
955    this->screenLayoutLong = layoutlong;
956    this->smallestScreenWidthDp = smallestwidthdp;
957    this->screenWidthDp = widthdp;
958    this->screenHeightDp = heightdp;
959    this->orientation = orient;
960    this->uiModeType = uiModeType;
961    this->uiModeNight = uiModeNight;
962    this->density = den;
963    this->touchscreen = touch;
964    this->keysHidden = keysHidden;
965    this->keyboard = key;
966    this->navHidden = navHidden;
967    this->navigation = nav;
968    this->screenSize = size;
969    this->layoutDirection = layoutDir;
970    this->version = vers;
971
972    // what is this anyway?
973    this->vendor = "";
974
975    return true;
976}
977
978String8
979AaptGroupEntry::toString() const
980{
981    String8 s = this->mcc;
982    s += ",";
983    s += this->mnc;
984    s += ",";
985    s += locale.toDirName();
986    s += ",";
987    s += layoutDirection;
988    s += ",";
989    s += smallestScreenWidthDp;
990    s += ",";
991    s += screenWidthDp;
992    s += ",";
993    s += screenHeightDp;
994    s += ",";
995    s += screenLayoutSize;
996    s += ",";
997    s += screenLayoutLong;
998    s += ",";
999    s += this->orientation;
1000    s += ",";
1001    s += uiModeType;
1002    s += ",";
1003    s += uiModeNight;
1004    s += ",";
1005    s += density;
1006    s += ",";
1007    s += touchscreen;
1008    s += ",";
1009    s += keysHidden;
1010    s += ",";
1011    s += keyboard;
1012    s += ",";
1013    s += navHidden;
1014    s += ",";
1015    s += navigation;
1016    s += ",";
1017    s += screenSize;
1018    s += ",";
1019    s += version;
1020    return s;
1021}
1022
1023String8
1024AaptGroupEntry::toDirName(const String8& resType) const
1025{
1026    String8 s = resType;
1027    if (this->mcc != "") {
1028        if (s.length() > 0) {
1029            s += "-";
1030        }
1031        s += mcc;
1032    }
1033    if (this->mnc != "") {
1034        if (s.length() > 0) {
1035            s += "-";
1036        }
1037        s += mnc;
1038    }
1039
1040    const String8 localeComponent = locale.toDirName();
1041    if (localeComponent != "") {
1042         if (s.length() > 0) {
1043             s += "-";
1044         }
1045         s += localeComponent;
1046    }
1047
1048    if (this->layoutDirection != "") {
1049        if (s.length() > 0) {
1050            s += "-";
1051        }
1052        s += layoutDirection;
1053    }
1054    if (this->smallestScreenWidthDp != "") {
1055        if (s.length() > 0) {
1056            s += "-";
1057        }
1058        s += smallestScreenWidthDp;
1059    }
1060    if (this->screenWidthDp != "") {
1061        if (s.length() > 0) {
1062            s += "-";
1063        }
1064        s += screenWidthDp;
1065    }
1066    if (this->screenHeightDp != "") {
1067        if (s.length() > 0) {
1068            s += "-";
1069        }
1070        s += screenHeightDp;
1071    }
1072    if (this->screenLayoutSize != "") {
1073        if (s.length() > 0) {
1074            s += "-";
1075        }
1076        s += screenLayoutSize;
1077    }
1078    if (this->screenLayoutLong != "") {
1079        if (s.length() > 0) {
1080            s += "-";
1081        }
1082        s += screenLayoutLong;
1083    }
1084    if (this->orientation != "") {
1085        if (s.length() > 0) {
1086            s += "-";
1087        }
1088        s += orientation;
1089    }
1090    if (this->uiModeType != "") {
1091        if (s.length() > 0) {
1092            s += "-";
1093        }
1094        s += uiModeType;
1095    }
1096    if (this->uiModeNight != "") {
1097        if (s.length() > 0) {
1098            s += "-";
1099        }
1100        s += uiModeNight;
1101    }
1102    if (this->density != "") {
1103        if (s.length() > 0) {
1104            s += "-";
1105        }
1106        s += density;
1107    }
1108    if (this->touchscreen != "") {
1109        if (s.length() > 0) {
1110            s += "-";
1111        }
1112        s += touchscreen;
1113    }
1114    if (this->keysHidden != "") {
1115        if (s.length() > 0) {
1116            s += "-";
1117        }
1118        s += keysHidden;
1119    }
1120    if (this->keyboard != "") {
1121        if (s.length() > 0) {
1122            s += "-";
1123        }
1124        s += keyboard;
1125    }
1126    if (this->navHidden != "") {
1127        if (s.length() > 0) {
1128            s += "-";
1129        }
1130        s += navHidden;
1131    }
1132    if (this->navigation != "") {
1133        if (s.length() > 0) {
1134            s += "-";
1135        }
1136        s += navigation;
1137    }
1138    if (this->screenSize != "") {
1139        if (s.length() > 0) {
1140            s += "-";
1141        }
1142        s += screenSize;
1143    }
1144    if (this->version != "") {
1145        if (s.length() > 0) {
1146            s += "-";
1147        }
1148        s += version;
1149    }
1150
1151    return s;
1152}
1153
1154bool AaptGroupEntry::getMccName(const char* name,
1155                                    ResTable_config* out)
1156{
1157    if (strcmp(name, kWildcardName) == 0) {
1158        if (out) out->mcc = 0;
1159        return true;
1160    }
1161    const char* c = name;
1162    if (tolower(*c) != 'm') return false;
1163    c++;
1164    if (tolower(*c) != 'c') return false;
1165    c++;
1166    if (tolower(*c) != 'c') return false;
1167    c++;
1168
1169    const char* val = c;
1170
1171    while (*c >= '0' && *c <= '9') {
1172        c++;
1173    }
1174    if (*c != 0) return false;
1175    if (c-val != 3) return false;
1176
1177    int d = atoi(val);
1178    if (d != 0) {
1179        if (out) out->mcc = d;
1180        return true;
1181    }
1182
1183    return false;
1184}
1185
1186bool AaptGroupEntry::getMncName(const char* name,
1187                                    ResTable_config* out)
1188{
1189    if (strcmp(name, kWildcardName) == 0) {
1190        if (out) out->mcc = 0;
1191        return true;
1192    }
1193    const char* c = name;
1194    if (tolower(*c) != 'm') return false;
1195    c++;
1196    if (tolower(*c) != 'n') return false;
1197    c++;
1198    if (tolower(*c) != 'c') return false;
1199    c++;
1200
1201    const char* val = c;
1202
1203    while (*c >= '0' && *c <= '9') {
1204        c++;
1205    }
1206    if (*c != 0) return false;
1207    if (c-val == 0 || c-val > 3) return false;
1208
1209    if (out) {
1210        out->mnc = atoi(val);
1211        if (out->mnc == 0) {
1212            out->mnc = ACONFIGURATION_MNC_ZERO;
1213        }
1214    }
1215
1216    return true;
1217}
1218
1219bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
1220{
1221    if (strcmp(name, kWildcardName) == 0) {
1222        if (out) out->screenLayout =
1223                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1224                | ResTable_config::LAYOUTDIR_ANY;
1225        return true;
1226    } else if (strcmp(name, "ldltr") == 0) {
1227        if (out) out->screenLayout =
1228                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1229                | ResTable_config::LAYOUTDIR_LTR;
1230        return true;
1231    } else if (strcmp(name, "ldrtl") == 0) {
1232        if (out) out->screenLayout =
1233                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1234                | ResTable_config::LAYOUTDIR_RTL;
1235        return true;
1236    }
1237
1238    return false;
1239}
1240
1241bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
1242                                     ResTable_config* out)
1243{
1244    if (strcmp(name, kWildcardName) == 0) {
1245        if (out) out->screenLayout =
1246                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1247                | ResTable_config::SCREENSIZE_ANY;
1248        return true;
1249    } else if (strcmp(name, "small") == 0) {
1250        if (out) out->screenLayout =
1251                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1252                | ResTable_config::SCREENSIZE_SMALL;
1253        return true;
1254    } else if (strcmp(name, "normal") == 0) {
1255        if (out) out->screenLayout =
1256                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1257                | ResTable_config::SCREENSIZE_NORMAL;
1258        return true;
1259    } else if (strcmp(name, "large") == 0) {
1260        if (out) out->screenLayout =
1261                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1262                | ResTable_config::SCREENSIZE_LARGE;
1263        return true;
1264    } else if (strcmp(name, "xlarge") == 0) {
1265        if (out) out->screenLayout =
1266                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1267                | ResTable_config::SCREENSIZE_XLARGE;
1268        return true;
1269    }
1270
1271    return false;
1272}
1273
1274bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
1275                                     ResTable_config* out)
1276{
1277    if (strcmp(name, kWildcardName) == 0) {
1278        if (out) out->screenLayout =
1279                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1280                | ResTable_config::SCREENLONG_ANY;
1281        return true;
1282    } else if (strcmp(name, "long") == 0) {
1283        if (out) out->screenLayout =
1284                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1285                | ResTable_config::SCREENLONG_YES;
1286        return true;
1287    } else if (strcmp(name, "notlong") == 0) {
1288        if (out) out->screenLayout =
1289                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1290                | ResTable_config::SCREENLONG_NO;
1291        return true;
1292    }
1293
1294    return false;
1295}
1296
1297bool AaptGroupEntry::getOrientationName(const char* name,
1298                                        ResTable_config* out)
1299{
1300    if (strcmp(name, kWildcardName) == 0) {
1301        if (out) out->orientation = out->ORIENTATION_ANY;
1302        return true;
1303    } else if (strcmp(name, "port") == 0) {
1304        if (out) out->orientation = out->ORIENTATION_PORT;
1305        return true;
1306    } else if (strcmp(name, "land") == 0) {
1307        if (out) out->orientation = out->ORIENTATION_LAND;
1308        return true;
1309    } else if (strcmp(name, "square") == 0) {
1310        if (out) out->orientation = out->ORIENTATION_SQUARE;
1311        return true;
1312    }
1313
1314    return false;
1315}
1316
1317bool AaptGroupEntry::getUiModeTypeName(const char* name,
1318                                       ResTable_config* out)
1319{
1320    if (strcmp(name, kWildcardName) == 0) {
1321        if (out) out->uiMode =
1322                (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1323                | ResTable_config::UI_MODE_TYPE_ANY;
1324        return true;
1325    } else if (strcmp(name, "desk") == 0) {
1326      if (out) out->uiMode =
1327              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1328              | ResTable_config::UI_MODE_TYPE_DESK;
1329        return true;
1330    } else if (strcmp(name, "car") == 0) {
1331      if (out) out->uiMode =
1332              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1333              | ResTable_config::UI_MODE_TYPE_CAR;
1334        return true;
1335    } else if (strcmp(name, "television") == 0) {
1336      if (out) out->uiMode =
1337              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1338              | ResTable_config::UI_MODE_TYPE_TELEVISION;
1339        return true;
1340    } else if (strcmp(name, "appliance") == 0) {
1341      if (out) out->uiMode =
1342              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1343              | ResTable_config::UI_MODE_TYPE_APPLIANCE;
1344        return true;
1345    }
1346
1347    return false;
1348}
1349
1350bool AaptGroupEntry::getUiModeNightName(const char* name,
1351                                          ResTable_config* out)
1352{
1353    if (strcmp(name, kWildcardName) == 0) {
1354        if (out) out->uiMode =
1355                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1356                | ResTable_config::UI_MODE_NIGHT_ANY;
1357        return true;
1358    } else if (strcmp(name, "night") == 0) {
1359        if (out) out->uiMode =
1360                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1361                | ResTable_config::UI_MODE_NIGHT_YES;
1362        return true;
1363    } else if (strcmp(name, "notnight") == 0) {
1364      if (out) out->uiMode =
1365              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1366              | ResTable_config::UI_MODE_NIGHT_NO;
1367        return true;
1368    }
1369
1370    return false;
1371}
1372
1373bool AaptGroupEntry::getDensityName(const char* name,
1374                                    ResTable_config* out)
1375{
1376    if (strcmp(name, kWildcardName) == 0) {
1377        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
1378        return true;
1379    }
1380
1381    if (strcmp(name, "nodpi") == 0) {
1382        if (out) out->density = ResTable_config::DENSITY_NONE;
1383        return true;
1384    }
1385
1386    if (strcmp(name, "ldpi") == 0) {
1387        if (out) out->density = ResTable_config::DENSITY_LOW;
1388        return true;
1389    }
1390
1391    if (strcmp(name, "mdpi") == 0) {
1392        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1393        return true;
1394    }
1395
1396    if (strcmp(name, "tvdpi") == 0) {
1397        if (out) out->density = ResTable_config::DENSITY_TV;
1398        return true;
1399    }
1400
1401    if (strcmp(name, "hdpi") == 0) {
1402        if (out) out->density = ResTable_config::DENSITY_HIGH;
1403        return true;
1404    }
1405
1406    if (strcmp(name, "xhdpi") == 0) {
1407        if (out) out->density = ResTable_config::DENSITY_XHIGH;
1408        return true;
1409    }
1410
1411    if (strcmp(name, "xxhdpi") == 0) {
1412        if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1413        return true;
1414    }
1415
1416    if (strcmp(name, "xxxhdpi") == 0) {
1417        if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
1418        return true;
1419    }
1420
1421    char* c = (char*)name;
1422    while (*c >= '0' && *c <= '9') {
1423        c++;
1424    }
1425
1426    // check that we have 'dpi' after the last digit.
1427    if (toupper(c[0]) != 'D' ||
1428            toupper(c[1]) != 'P' ||
1429            toupper(c[2]) != 'I' ||
1430            c[3] != 0) {
1431        return false;
1432    }
1433
1434    // temporarily replace the first letter with \0 to
1435    // use atoi.
1436    char tmp = c[0];
1437    c[0] = '\0';
1438
1439    int d = atoi(name);
1440    c[0] = tmp;
1441
1442    if (d != 0) {
1443        if (out) out->density = d;
1444        return true;
1445    }
1446
1447    return false;
1448}
1449
1450bool AaptGroupEntry::getTouchscreenName(const char* name,
1451                                        ResTable_config* out)
1452{
1453    if (strcmp(name, kWildcardName) == 0) {
1454        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1455        return true;
1456    } else if (strcmp(name, "notouch") == 0) {
1457        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1458        return true;
1459    } else if (strcmp(name, "stylus") == 0) {
1460        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1461        return true;
1462    } else if (strcmp(name, "finger") == 0) {
1463        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1464        return true;
1465    }
1466
1467    return false;
1468}
1469
1470bool AaptGroupEntry::getKeysHiddenName(const char* name,
1471                                       ResTable_config* out)
1472{
1473    uint8_t mask = 0;
1474    uint8_t value = 0;
1475    if (strcmp(name, kWildcardName) == 0) {
1476        mask = ResTable_config::MASK_KEYSHIDDEN;
1477        value = ResTable_config::KEYSHIDDEN_ANY;
1478    } else if (strcmp(name, "keysexposed") == 0) {
1479        mask = ResTable_config::MASK_KEYSHIDDEN;
1480        value = ResTable_config::KEYSHIDDEN_NO;
1481    } else if (strcmp(name, "keyshidden") == 0) {
1482        mask = ResTable_config::MASK_KEYSHIDDEN;
1483        value = ResTable_config::KEYSHIDDEN_YES;
1484    } else if (strcmp(name, "keyssoft") == 0) {
1485        mask = ResTable_config::MASK_KEYSHIDDEN;
1486        value = ResTable_config::KEYSHIDDEN_SOFT;
1487    }
1488
1489    if (mask != 0) {
1490        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1491        return true;
1492    }
1493
1494    return false;
1495}
1496
1497bool AaptGroupEntry::getKeyboardName(const char* name,
1498                                        ResTable_config* out)
1499{
1500    if (strcmp(name, kWildcardName) == 0) {
1501        if (out) out->keyboard = out->KEYBOARD_ANY;
1502        return true;
1503    } else if (strcmp(name, "nokeys") == 0) {
1504        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1505        return true;
1506    } else if (strcmp(name, "qwerty") == 0) {
1507        if (out) out->keyboard = out->KEYBOARD_QWERTY;
1508        return true;
1509    } else if (strcmp(name, "12key") == 0) {
1510        if (out) out->keyboard = out->KEYBOARD_12KEY;
1511        return true;
1512    }
1513
1514    return false;
1515}
1516
1517bool AaptGroupEntry::getNavHiddenName(const char* name,
1518                                       ResTable_config* out)
1519{
1520    uint8_t mask = 0;
1521    uint8_t value = 0;
1522    if (strcmp(name, kWildcardName) == 0) {
1523        mask = ResTable_config::MASK_NAVHIDDEN;
1524        value = ResTable_config::NAVHIDDEN_ANY;
1525    } else if (strcmp(name, "navexposed") == 0) {
1526        mask = ResTable_config::MASK_NAVHIDDEN;
1527        value = ResTable_config::NAVHIDDEN_NO;
1528    } else if (strcmp(name, "navhidden") == 0) {
1529        mask = ResTable_config::MASK_NAVHIDDEN;
1530        value = ResTable_config::NAVHIDDEN_YES;
1531    }
1532
1533    if (mask != 0) {
1534        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1535        return true;
1536    }
1537
1538    return false;
1539}
1540
1541bool AaptGroupEntry::getNavigationName(const char* name,
1542                                     ResTable_config* out)
1543{
1544    if (strcmp(name, kWildcardName) == 0) {
1545        if (out) out->navigation = out->NAVIGATION_ANY;
1546        return true;
1547    } else if (strcmp(name, "nonav") == 0) {
1548        if (out) out->navigation = out->NAVIGATION_NONAV;
1549        return true;
1550    } else if (strcmp(name, "dpad") == 0) {
1551        if (out) out->navigation = out->NAVIGATION_DPAD;
1552        return true;
1553    } else if (strcmp(name, "trackball") == 0) {
1554        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1555        return true;
1556    } else if (strcmp(name, "wheel") == 0) {
1557        if (out) out->navigation = out->NAVIGATION_WHEEL;
1558        return true;
1559    }
1560
1561    return false;
1562}
1563
1564bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
1565{
1566    if (strcmp(name, kWildcardName) == 0) {
1567        if (out) {
1568            out->screenWidth = out->SCREENWIDTH_ANY;
1569            out->screenHeight = out->SCREENHEIGHT_ANY;
1570        }
1571        return true;
1572    }
1573
1574    const char* x = name;
1575    while (*x >= '0' && *x <= '9') x++;
1576    if (x == name || *x != 'x') return false;
1577    String8 xName(name, x-name);
1578    x++;
1579
1580    const char* y = x;
1581    while (*y >= '0' && *y <= '9') y++;
1582    if (y == name || *y != 0) return false;
1583    String8 yName(x, y-x);
1584
1585    uint16_t w = (uint16_t)atoi(xName.string());
1586    uint16_t h = (uint16_t)atoi(yName.string());
1587    if (w < h) {
1588        return false;
1589    }
1590
1591    if (out) {
1592        out->screenWidth = w;
1593        out->screenHeight = h;
1594    }
1595
1596    return true;
1597}
1598
1599bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1600{
1601    if (strcmp(name, kWildcardName) == 0) {
1602        if (out) {
1603            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1604        }
1605        return true;
1606    }
1607
1608    if (*name != 's') return false;
1609    name++;
1610    if (*name != 'w') return false;
1611    name++;
1612    const char* x = name;
1613    while (*x >= '0' && *x <= '9') x++;
1614    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1615    String8 xName(name, x-name);
1616
1617    if (out) {
1618        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1619    }
1620
1621    return true;
1622}
1623
1624bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1625{
1626    if (strcmp(name, kWildcardName) == 0) {
1627        if (out) {
1628            out->screenWidthDp = out->SCREENWIDTH_ANY;
1629        }
1630        return true;
1631    }
1632
1633    if (*name != 'w') return false;
1634    name++;
1635    const char* x = name;
1636    while (*x >= '0' && *x <= '9') x++;
1637    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1638    String8 xName(name, x-name);
1639
1640    if (out) {
1641        out->screenWidthDp = (uint16_t)atoi(xName.string());
1642    }
1643
1644    return true;
1645}
1646
1647bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1648{
1649    if (strcmp(name, kWildcardName) == 0) {
1650        if (out) {
1651            out->screenHeightDp = out->SCREENWIDTH_ANY;
1652        }
1653        return true;
1654    }
1655
1656    if (*name != 'h') return false;
1657    name++;
1658    const char* x = name;
1659    while (*x >= '0' && *x <= '9') x++;
1660    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1661    String8 xName(name, x-name);
1662
1663    if (out) {
1664        out->screenHeightDp = (uint16_t)atoi(xName.string());
1665    }
1666
1667    return true;
1668}
1669
1670bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
1671{
1672    if (strcmp(name, kWildcardName) == 0) {
1673        if (out) {
1674            out->sdkVersion = out->SDKVERSION_ANY;
1675            out->minorVersion = out->MINORVERSION_ANY;
1676        }
1677        return true;
1678    }
1679
1680    if (*name != 'v') {
1681        return false;
1682    }
1683
1684    name++;
1685    const char* s = name;
1686    while (*s >= '0' && *s <= '9') s++;
1687    if (s == name || *s != 0) return false;
1688    String8 sdkName(name, s-name);
1689
1690    if (out) {
1691        out->sdkVersion = (uint16_t)atoi(sdkName.string());
1692        out->minorVersion = 0;
1693    }
1694
1695    return true;
1696}
1697
1698int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1699{
1700    int v = mcc.compare(o.mcc);
1701    if (v == 0) v = mnc.compare(o.mnc);
1702    if (v == 0) v = locale.compare(o.locale);
1703    if (v == 0) v = layoutDirection.compare(o.layoutDirection);
1704    if (v == 0) v = vendor.compare(o.vendor);
1705    if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
1706    if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1707    if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
1708    if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1709    if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1710    if (v == 0) v = orientation.compare(o.orientation);
1711    if (v == 0) v = uiModeType.compare(o.uiModeType);
1712    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1713    if (v == 0) v = density.compare(o.density);
1714    if (v == 0) v = touchscreen.compare(o.touchscreen);
1715    if (v == 0) v = keysHidden.compare(o.keysHidden);
1716    if (v == 0) v = keyboard.compare(o.keyboard);
1717    if (v == 0) v = navHidden.compare(o.navHidden);
1718    if (v == 0) v = navigation.compare(o.navigation);
1719    if (v == 0) v = screenSize.compare(o.screenSize);
1720    if (v == 0) v = version.compare(o.version);
1721    return v;
1722}
1723
1724const ResTable_config AaptGroupEntry::toParams() const
1725{
1726    if (!mParamsChanged) {
1727        return mParams;
1728    }
1729
1730    mParamsChanged = false;
1731    ResTable_config& params = mParams;
1732    memset(&params, 0, sizeof(ResTable_config));
1733    getMccName(mcc.string(), &params);
1734    getMncName(mnc.string(), &params);
1735    locale.writeTo(&params);
1736    getLayoutDirectionName(layoutDirection.string(), &params);
1737    getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
1738    getScreenWidthDpName(screenWidthDp.string(), &params);
1739    getScreenHeightDpName(screenHeightDp.string(), &params);
1740    getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1741    getScreenLayoutLongName(screenLayoutLong.string(), &params);
1742    getOrientationName(orientation.string(), &params);
1743    getUiModeTypeName(uiModeType.string(), &params);
1744    getUiModeNightName(uiModeNight.string(), &params);
1745    getDensityName(density.string(), &params);
1746    getTouchscreenName(touchscreen.string(), &params);
1747    getKeysHiddenName(keysHidden.string(), &params);
1748    getKeyboardName(keyboard.string(), &params);
1749    getNavHiddenName(navHidden.string(), &params);
1750    getNavigationName(navigation.string(), &params);
1751    getScreenSizeName(screenSize.string(), &params);
1752    getVersionName(version.string(), &params);
1753
1754    // Fix up version number based on specified parameters.
1755    int minSdk = 0;
1756    if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1757            || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
1758            || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
1759        minSdk = SDK_HONEYCOMB_MR2;
1760    } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1761                != ResTable_config::UI_MODE_TYPE_ANY
1762            ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1763                != ResTable_config::UI_MODE_NIGHT_ANY) {
1764        minSdk = SDK_FROYO;
1765    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1766                != ResTable_config::SCREENSIZE_ANY
1767            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1768                != ResTable_config::SCREENLONG_ANY
1769            || params.density != ResTable_config::DENSITY_DEFAULT) {
1770        minSdk = SDK_DONUT;
1771    }
1772
1773    if (minSdk > params.sdkVersion) {
1774        params.sdkVersion = minSdk;
1775    }
1776
1777    return params;
1778}
1779
1780// =========================================================================
1781// =========================================================================
1782// =========================================================================
1783
1784void* AaptFile::editData(size_t size)
1785{
1786    if (size <= mBufferSize) {
1787        mDataSize = size;
1788        return mData;
1789    }
1790    size_t allocSize = (size*3)/2;
1791    void* buf = realloc(mData, allocSize);
1792    if (buf == NULL) {
1793        return NULL;
1794    }
1795    mData = buf;
1796    mDataSize = size;
1797    mBufferSize = allocSize;
1798    return buf;
1799}
1800
1801void* AaptFile::editData(size_t* outSize)
1802{
1803    if (outSize) {
1804        *outSize = mDataSize;
1805    }
1806    return mData;
1807}
1808
1809void* AaptFile::padData(size_t wordSize)
1810{
1811    const size_t extra = mDataSize%wordSize;
1812    if (extra == 0) {
1813        return mData;
1814    }
1815
1816    size_t initial = mDataSize;
1817    void* data = editData(initial+(wordSize-extra));
1818    if (data != NULL) {
1819        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1820    }
1821    return data;
1822}
1823
1824status_t AaptFile::writeData(const void* data, size_t size)
1825{
1826    size_t end = mDataSize;
1827    size_t total = size + end;
1828    void* buf = editData(total);
1829    if (buf == NULL) {
1830        return UNKNOWN_ERROR;
1831    }
1832    memcpy(((char*)buf)+end, data, size);
1833    return NO_ERROR;
1834}
1835
1836void AaptFile::clearData()
1837{
1838    if (mData != NULL) free(mData);
1839    mData = NULL;
1840    mDataSize = 0;
1841    mBufferSize = 0;
1842}
1843
1844String8 AaptFile::getPrintableSource() const
1845{
1846    if (hasData()) {
1847        String8 name(mGroupEntry.toDirName(String8()));
1848        name.appendPath(mPath);
1849        name.append(" #generated");
1850        return name;
1851    }
1852    return mSourceFile;
1853}
1854
1855// =========================================================================
1856// =========================================================================
1857// =========================================================================
1858
1859status_t AaptGroup::addFile(const sp<AaptFile>& file)
1860{
1861    if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1862        file->mPath = mPath;
1863        mFiles.add(file->getGroupEntry(), file);
1864        return NO_ERROR;
1865    }
1866
1867#if 0
1868    printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1869            file->getSourceFile().string(),
1870            file->getGroupEntry().toDirName(String8()).string(),
1871            mLeaf.string(), mPath.string());
1872#endif
1873
1874    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1875                                               getPrintableSource().string());
1876    return UNKNOWN_ERROR;
1877}
1878
1879void AaptGroup::removeFile(size_t index)
1880{
1881	mFiles.removeItemsAt(index);
1882}
1883
1884void AaptGroup::print(const String8& prefix) const
1885{
1886    printf("%s%s\n", prefix.string(), getPath().string());
1887    const size_t N=mFiles.size();
1888    size_t i;
1889    for (i=0; i<N; i++) {
1890        sp<AaptFile> file = mFiles.valueAt(i);
1891        const AaptGroupEntry& e = file->getGroupEntry();
1892        if (file->hasData()) {
1893            printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
1894                    (int)file->getSize());
1895        } else {
1896            printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1897                    file->getPrintableSource().string());
1898        }
1899        //printf("%s  File Group Entry: %s\n", prefix.string(),
1900        //        file->getGroupEntry().toDirName(String8()).string());
1901    }
1902}
1903
1904String8 AaptGroup::getPrintableSource() const
1905{
1906    if (mFiles.size() > 0) {
1907        // Arbitrarily pull the first source file out of the list.
1908        return mFiles.valueAt(0)->getPrintableSource();
1909    }
1910
1911    // Should never hit this case, but to be safe...
1912    return getPath();
1913
1914}
1915
1916// =========================================================================
1917// =========================================================================
1918// =========================================================================
1919
1920status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1921{
1922    if (mFiles.indexOfKey(name) >= 0) {
1923        return ALREADY_EXISTS;
1924    }
1925    mFiles.add(name, file);
1926    return NO_ERROR;
1927}
1928
1929status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1930{
1931    if (mDirs.indexOfKey(name) >= 0) {
1932        return ALREADY_EXISTS;
1933    }
1934    mDirs.add(name, dir);
1935    return NO_ERROR;
1936}
1937
1938sp<AaptDir> AaptDir::makeDir(const String8& path)
1939{
1940    String8 name;
1941    String8 remain = path;
1942
1943    sp<AaptDir> subdir = this;
1944    while (name = remain.walkPath(&remain), remain != "") {
1945        subdir = subdir->makeDir(name);
1946    }
1947
1948    ssize_t i = subdir->mDirs.indexOfKey(name);
1949    if (i >= 0) {
1950        return subdir->mDirs.valueAt(i);
1951    }
1952    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1953    subdir->mDirs.add(name, dir);
1954    return dir;
1955}
1956
1957void AaptDir::removeFile(const String8& name)
1958{
1959    mFiles.removeItem(name);
1960}
1961
1962void AaptDir::removeDir(const String8& name)
1963{
1964    mDirs.removeItem(name);
1965}
1966
1967status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1968{
1969    sp<AaptGroup> group;
1970    if (mFiles.indexOfKey(leafName) >= 0) {
1971        group = mFiles.valueFor(leafName);
1972    } else {
1973        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1974        mFiles.add(leafName, group);
1975    }
1976
1977    return group->addFile(file);
1978}
1979
1980ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1981                            const AaptGroupEntry& kind, const String8& resType,
1982                            sp<FilePathStore>& fullResPaths)
1983{
1984    Vector<String8> fileNames;
1985    {
1986        DIR* dir = NULL;
1987
1988        dir = opendir(srcDir.string());
1989        if (dir == NULL) {
1990            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1991            return UNKNOWN_ERROR;
1992        }
1993
1994        /*
1995         * Slurp the filenames out of the directory.
1996         */
1997        while (1) {
1998            struct dirent* entry;
1999
2000            entry = readdir(dir);
2001            if (entry == NULL)
2002                break;
2003
2004            if (isHidden(srcDir.string(), entry->d_name))
2005                continue;
2006
2007            String8 name(entry->d_name);
2008            fileNames.add(name);
2009            // Add fully qualified path for dependency purposes
2010            // if we're collecting them
2011            if (fullResPaths != NULL) {
2012                fullResPaths->add(srcDir.appendPathCopy(name));
2013            }
2014        }
2015        closedir(dir);
2016    }
2017
2018    ssize_t count = 0;
2019
2020    /*
2021     * Stash away the files and recursively descend into subdirectories.
2022     */
2023    const size_t N = fileNames.size();
2024    size_t i;
2025    for (i = 0; i < N; i++) {
2026        String8 pathName(srcDir);
2027        FileType type;
2028
2029        pathName.appendPath(fileNames[i].string());
2030        type = getFileType(pathName.string());
2031        if (type == kFileTypeDirectory) {
2032            sp<AaptDir> subdir;
2033            bool notAdded = false;
2034            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
2035                subdir = mDirs.valueFor(fileNames[i]);
2036            } else {
2037                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
2038                notAdded = true;
2039            }
2040            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
2041                                                resType, fullResPaths);
2042            if (res < NO_ERROR) {
2043                return res;
2044            }
2045            if (res > 0 && notAdded) {
2046                mDirs.add(fileNames[i], subdir);
2047            }
2048            count += res;
2049        } else if (type == kFileTypeRegular) {
2050            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
2051            status_t err = addLeafFile(fileNames[i], file);
2052            if (err != NO_ERROR) {
2053                return err;
2054            }
2055
2056            count++;
2057
2058        } else {
2059            if (bundle->getVerbose())
2060                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
2061        }
2062    }
2063
2064    return count;
2065}
2066
2067status_t AaptDir::validate() const
2068{
2069    const size_t NF = mFiles.size();
2070    const size_t ND = mDirs.size();
2071    size_t i;
2072    for (i = 0; i < NF; i++) {
2073        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
2074            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2075                    "Invalid filename.  Unable to add.");
2076            return UNKNOWN_ERROR;
2077        }
2078
2079        size_t j;
2080        for (j = i+1; j < NF; j++) {
2081            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2082                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
2083                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2084                        "File is case-insensitive equivalent to: %s",
2085                        mFiles.valueAt(j)->getPrintableSource().string());
2086                return UNKNOWN_ERROR;
2087            }
2088
2089            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
2090            // (this is mostly caught by the "marked" stuff, below)
2091        }
2092
2093        for (j = 0; j < ND; j++) {
2094            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2095                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
2096                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2097                        "File conflicts with dir from: %s",
2098                        mDirs.valueAt(j)->getPrintableSource().string());
2099                return UNKNOWN_ERROR;
2100            }
2101        }
2102    }
2103
2104    for (i = 0; i < ND; i++) {
2105        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
2106            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2107                    "Invalid directory name, unable to add.");
2108            return UNKNOWN_ERROR;
2109        }
2110
2111        size_t j;
2112        for (j = i+1; j < ND; j++) {
2113            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
2114                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
2115                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2116                        "Directory is case-insensitive equivalent to: %s",
2117                        mDirs.valueAt(j)->getPrintableSource().string());
2118                return UNKNOWN_ERROR;
2119            }
2120        }
2121
2122        status_t err = mDirs.valueAt(i)->validate();
2123        if (err != NO_ERROR) {
2124            return err;
2125        }
2126    }
2127
2128    return NO_ERROR;
2129}
2130
2131void AaptDir::print(const String8& prefix) const
2132{
2133    const size_t ND=getDirs().size();
2134    size_t i;
2135    for (i=0; i<ND; i++) {
2136        getDirs().valueAt(i)->print(prefix);
2137    }
2138
2139    const size_t NF=getFiles().size();
2140    for (i=0; i<NF; i++) {
2141        getFiles().valueAt(i)->print(prefix);
2142    }
2143}
2144
2145String8 AaptDir::getPrintableSource() const
2146{
2147    if (mFiles.size() > 0) {
2148        // Arbitrarily pull the first file out of the list as the source dir.
2149        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
2150    }
2151    if (mDirs.size() > 0) {
2152        // Or arbitrarily pull the first dir out of the list as the source dir.
2153        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
2154    }
2155
2156    // Should never hit this case, but to be safe...
2157    return mPath;
2158
2159}
2160
2161// =========================================================================
2162// =========================================================================
2163// =========================================================================
2164
2165status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
2166{
2167    status_t err = NO_ERROR;
2168    size_t N = javaSymbols->mSymbols.size();
2169    for (size_t i=0; i<N; i++) {
2170        const String8& name = javaSymbols->mSymbols.keyAt(i);
2171        const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
2172        ssize_t pos = mSymbols.indexOfKey(name);
2173        if (pos < 0) {
2174            entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
2175            err = UNKNOWN_ERROR;
2176            continue;
2177        }
2178        //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
2179        //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
2180        mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
2181    }
2182
2183    N = javaSymbols->mNestedSymbols.size();
2184    for (size_t i=0; i<N; i++) {
2185        const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
2186        const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
2187        ssize_t pos = mNestedSymbols.indexOfKey(name);
2188        if (pos < 0) {
2189            SourcePos pos;
2190            pos.error("Java symbol dir %s not defined\n", name.string());
2191            err = UNKNOWN_ERROR;
2192            continue;
2193        }
2194        //printf("**** applying java symbols in dir %s\n", name.string());
2195        status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2196        if (myerr != NO_ERROR) {
2197            err = myerr;
2198        }
2199    }
2200
2201    return err;
2202}
2203
2204// =========================================================================
2205// =========================================================================
2206// =========================================================================
2207
2208AaptAssets::AaptAssets()
2209    : AaptDir(String8(), String8()),
2210      mHavePrivateSymbols(false),
2211      mChanged(false), mHaveIncludedAssets(false),
2212      mRes(NULL)
2213{
2214}
2215
2216const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
2217    if (mChanged) {
2218    }
2219    return mGroupEntries;
2220}
2221
2222status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
2223{
2224    mChanged = true;
2225    return AaptDir::addFile(name, file);
2226}
2227
2228sp<AaptFile> AaptAssets::addFile(
2229        const String8& filePath, const AaptGroupEntry& entry,
2230        const String8& srcDir, sp<AaptGroup>* outGroup,
2231        const String8& resType)
2232{
2233    sp<AaptDir> dir = this;
2234    sp<AaptGroup> group;
2235    sp<AaptFile> file;
2236    String8 root, remain(filePath), partialPath;
2237    while (remain.length() > 0) {
2238        root = remain.walkPath(&remain);
2239        partialPath.appendPath(root);
2240
2241        const String8 rootStr(root);
2242
2243        if (remain.length() == 0) {
2244            ssize_t i = dir->getFiles().indexOfKey(rootStr);
2245            if (i >= 0) {
2246                group = dir->getFiles().valueAt(i);
2247            } else {
2248                group = new AaptGroup(rootStr, filePath);
2249                status_t res = dir->addFile(rootStr, group);
2250                if (res != NO_ERROR) {
2251                    return NULL;
2252                }
2253            }
2254            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2255            status_t res = group->addFile(file);
2256            if (res != NO_ERROR) {
2257                return NULL;
2258            }
2259            break;
2260
2261        } else {
2262            ssize_t i = dir->getDirs().indexOfKey(rootStr);
2263            if (i >= 0) {
2264                dir = dir->getDirs().valueAt(i);
2265            } else {
2266                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2267                status_t res = dir->addDir(rootStr, subdir);
2268                if (res != NO_ERROR) {
2269                    return NULL;
2270                }
2271                dir = subdir;
2272            }
2273        }
2274    }
2275
2276    mGroupEntries.add(entry);
2277    if (outGroup) *outGroup = group;
2278    return file;
2279}
2280
2281void AaptAssets::addResource(const String8& leafName, const String8& path,
2282                const sp<AaptFile>& file, const String8& resType)
2283{
2284    sp<AaptDir> res = AaptDir::makeDir(kResString);
2285    String8 dirname = file->getGroupEntry().toDirName(resType);
2286    sp<AaptDir> subdir = res->makeDir(dirname);
2287    sp<AaptGroup> grr = new AaptGroup(leafName, path);
2288    grr->addFile(file);
2289
2290    subdir->addFile(leafName, grr);
2291}
2292
2293
2294ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2295{
2296    int count;
2297    int totalCount = 0;
2298    FileType type;
2299    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2300    const size_t dirCount =resDirs.size();
2301    sp<AaptAssets> current = this;
2302
2303    const int N = bundle->getFileSpecCount();
2304
2305    /*
2306     * If a package manifest was specified, include that first.
2307     */
2308    if (bundle->getAndroidManifestFile() != NULL) {
2309        // place at root of zip.
2310        String8 srcFile(bundle->getAndroidManifestFile());
2311        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2312                NULL, String8());
2313        totalCount++;
2314    }
2315
2316    /*
2317     * If a directory of custom assets was supplied, slurp 'em up.
2318     */
2319    if (bundle->getAssetSourceDir()) {
2320        const char* assetDir = bundle->getAssetSourceDir();
2321
2322        FileType type = getFileType(assetDir);
2323        if (type == kFileTypeNonexistent) {
2324            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
2325            return UNKNOWN_ERROR;
2326        }
2327        if (type != kFileTypeDirectory) {
2328            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2329            return UNKNOWN_ERROR;
2330        }
2331
2332        String8 assetRoot(assetDir);
2333        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2334        AaptGroupEntry group;
2335        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
2336                                            String8(), mFullAssetPaths);
2337        if (count < 0) {
2338            totalCount = count;
2339            goto bail;
2340        }
2341        if (count > 0) {
2342            mGroupEntries.add(group);
2343        }
2344        totalCount += count;
2345
2346        if (bundle->getVerbose())
2347            printf("Found %d custom asset file%s in %s\n",
2348                   count, (count==1) ? "" : "s", assetDir);
2349    }
2350
2351    /*
2352     * If a directory of resource-specific assets was supplied, slurp 'em up.
2353     */
2354    for (size_t i=0; i<dirCount; i++) {
2355        const char *res = resDirs[i];
2356        if (res) {
2357            type = getFileType(res);
2358            if (type == kFileTypeNonexistent) {
2359                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2360                return UNKNOWN_ERROR;
2361            }
2362            if (type == kFileTypeDirectory) {
2363                if (i>0) {
2364                    sp<AaptAssets> nextOverlay = new AaptAssets();
2365                    current->setOverlay(nextOverlay);
2366                    current = nextOverlay;
2367                    current->setFullResPaths(mFullResPaths);
2368                }
2369                count = current->slurpResourceTree(bundle, String8(res));
2370
2371                if (count < 0) {
2372                    totalCount = count;
2373                    goto bail;
2374                }
2375                totalCount += count;
2376            }
2377            else {
2378                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2379                return UNKNOWN_ERROR;
2380            }
2381        }
2382
2383    }
2384    /*
2385     * Now do any additional raw files.
2386     */
2387    for (int arg=0; arg<N; arg++) {
2388        const char* assetDir = bundle->getFileSpecEntry(arg);
2389
2390        FileType type = getFileType(assetDir);
2391        if (type == kFileTypeNonexistent) {
2392            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2393            return UNKNOWN_ERROR;
2394        }
2395        if (type != kFileTypeDirectory) {
2396            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2397            return UNKNOWN_ERROR;
2398        }
2399
2400        String8 assetRoot(assetDir);
2401
2402        if (bundle->getVerbose())
2403            printf("Processing raw dir '%s'\n", (const char*) assetDir);
2404
2405        /*
2406         * Do a recursive traversal of subdir tree.  We don't make any
2407         * guarantees about ordering, so we're okay with an inorder search
2408         * using whatever order the OS happens to hand back to us.
2409         */
2410        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2411        if (count < 0) {
2412            /* failure; report error and remove archive */
2413            totalCount = count;
2414            goto bail;
2415        }
2416        totalCount += count;
2417
2418        if (bundle->getVerbose())
2419            printf("Found %d asset file%s in %s\n",
2420                   count, (count==1) ? "" : "s", assetDir);
2421    }
2422
2423    count = validate();
2424    if (count != NO_ERROR) {
2425        totalCount = count;
2426        goto bail;
2427    }
2428
2429    count = filter(bundle);
2430    if (count != NO_ERROR) {
2431        totalCount = count;
2432        goto bail;
2433    }
2434
2435bail:
2436    return totalCount;
2437}
2438
2439ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2440                                    const AaptGroupEntry& kind,
2441                                    const String8& resType,
2442                                    sp<FilePathStore>& fullResPaths)
2443{
2444    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2445    if (res > 0) {
2446        mGroupEntries.add(kind);
2447    }
2448
2449    return res;
2450}
2451
2452ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2453{
2454    ssize_t err = 0;
2455
2456    DIR* dir = opendir(srcDir.string());
2457    if (dir == NULL) {
2458        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2459        return UNKNOWN_ERROR;
2460    }
2461
2462    status_t count = 0;
2463
2464    /*
2465     * Run through the directory, looking for dirs that match the
2466     * expected pattern.
2467     */
2468    while (1) {
2469        struct dirent* entry = readdir(dir);
2470        if (entry == NULL) {
2471            break;
2472        }
2473
2474        if (isHidden(srcDir.string(), entry->d_name)) {
2475            continue;
2476        }
2477
2478        String8 subdirName(srcDir);
2479        subdirName.appendPath(entry->d_name);
2480
2481        AaptGroupEntry group;
2482        String8 resType;
2483        bool b = group.initFromDirName(entry->d_name, &resType);
2484        if (!b) {
2485            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2486                    entry->d_name);
2487            err = -1;
2488            continue;
2489        }
2490
2491        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2492            int maxResInt = atoi(bundle->getMaxResVersion());
2493            const char *verString = group.getVersionString().string();
2494            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2495            if (dirVersionInt > maxResInt) {
2496              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2497              continue;
2498            }
2499        }
2500
2501        FileType type = getFileType(subdirName.string());
2502
2503        if (type == kFileTypeDirectory) {
2504            sp<AaptDir> dir = makeDir(resType);
2505            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2506                                                resType, mFullResPaths);
2507            if (res < 0) {
2508                count = res;
2509                goto bail;
2510            }
2511            if (res > 0) {
2512                mGroupEntries.add(group);
2513                count += res;
2514            }
2515
2516            // Only add this directory if we don't already have a resource dir
2517            // for the current type.  This ensures that we only add the dir once
2518            // for all configs.
2519            sp<AaptDir> rdir = resDir(resType);
2520            if (rdir == NULL) {
2521                mResDirs.add(dir);
2522            }
2523        } else {
2524            if (bundle->getVerbose()) {
2525                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2526            }
2527        }
2528    }
2529
2530bail:
2531    closedir(dir);
2532    dir = NULL;
2533
2534    if (err != 0) {
2535        return err;
2536    }
2537    return count;
2538}
2539
2540ssize_t
2541AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2542{
2543    int count = 0;
2544    SortedVector<AaptGroupEntry> entries;
2545
2546    ZipFile* zip = new ZipFile;
2547    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2548    if (err != NO_ERROR) {
2549        fprintf(stderr, "error opening zip file %s\n", filename);
2550        count = err;
2551        delete zip;
2552        return -1;
2553    }
2554
2555    const int N = zip->getNumEntries();
2556    for (int i=0; i<N; i++) {
2557        ZipEntry* entry = zip->getEntryByIndex(i);
2558        if (entry->getDeleted()) {
2559            continue;
2560        }
2561
2562        String8 entryName(entry->getFileName());
2563
2564        String8 dirName = entryName.getPathDir();
2565        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2566
2567        String8 resType;
2568        AaptGroupEntry kind;
2569
2570        String8 remain;
2571        if (entryName.walkPath(&remain) == kResourceDir) {
2572            // these are the resources, pull their type out of the directory name
2573            kind.initFromDirName(remain.walkPath().string(), &resType);
2574        } else {
2575            // these are untyped and don't have an AaptGroupEntry
2576        }
2577        if (entries.indexOf(kind) < 0) {
2578            entries.add(kind);
2579            mGroupEntries.add(kind);
2580        }
2581
2582        // use the one from the zip file if they both exist.
2583        dir->removeFile(entryName.getPathLeaf());
2584
2585        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2586        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2587        if (err != NO_ERROR) {
2588            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2589            count = err;
2590            goto bail;
2591        }
2592        file->setCompressionMethod(entry->getCompressionMethod());
2593
2594#if 0
2595        if (entryName == "AndroidManifest.xml") {
2596            printf("AndroidManifest.xml\n");
2597        }
2598        printf("\n\nfile: %s\n", entryName.string());
2599#endif
2600
2601        size_t len = entry->getUncompressedLen();
2602        void* data = zip->uncompress(entry);
2603        void* buf = file->editData(len);
2604        memcpy(buf, data, len);
2605
2606#if 0
2607        const int OFF = 0;
2608        const unsigned char* p = (unsigned char*)data;
2609        const unsigned char* end = p+len;
2610        p += OFF;
2611        for (int i=0; i<32 && p < end; i++) {
2612            printf("0x%03x ", i*0x10 + OFF);
2613            for (int j=0; j<0x10 && p < end; j++) {
2614                printf(" %02x", *p);
2615                p++;
2616            }
2617            printf("\n");
2618        }
2619#endif
2620
2621        free(data);
2622
2623        count++;
2624    }
2625
2626bail:
2627    delete zip;
2628    return count;
2629}
2630
2631status_t AaptAssets::filter(Bundle* bundle)
2632{
2633    ResourceFilter reqFilter;
2634    status_t err = reqFilter.parse(bundle->getConfigurations());
2635    if (err != NO_ERROR) {
2636        return err;
2637    }
2638
2639    ResourceFilter prefFilter;
2640    err = prefFilter.parse(bundle->getPreferredConfigurations());
2641    if (err != NO_ERROR) {
2642        return err;
2643    }
2644
2645    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2646        return NO_ERROR;
2647    }
2648
2649    if (bundle->getVerbose()) {
2650        if (!reqFilter.isEmpty()) {
2651            printf("Applying required filter: %s\n",
2652                    bundle->getConfigurations());
2653        }
2654        if (!prefFilter.isEmpty()) {
2655            printf("Applying preferred filter: %s\n",
2656                    bundle->getPreferredConfigurations());
2657        }
2658    }
2659
2660    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2661    const size_t ND = resdirs.size();
2662    for (size_t i=0; i<ND; i++) {
2663        const sp<AaptDir>& dir = resdirs.itemAt(i);
2664        if (dir->getLeaf() == kValuesDir) {
2665            // The "value" dir is special since a single file defines
2666            // multiple resources, so we can not do filtering on the
2667            // files themselves.
2668            continue;
2669        }
2670        if (dir->getLeaf() == kMipmapDir) {
2671            // We also skip the "mipmap" directory, since the point of this
2672            // is to include all densities without stripping.  If you put
2673            // other configurations in here as well they won't be stripped
2674            // either...  So don't do that.  Seriously.  What is wrong with you?
2675            continue;
2676        }
2677
2678        const size_t NG = dir->getFiles().size();
2679        for (size_t j=0; j<NG; j++) {
2680            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2681
2682            // First remove any configurations we know we don't need.
2683            for (size_t k=0; k<grp->getFiles().size(); k++) {
2684                sp<AaptFile> file = grp->getFiles().valueAt(k);
2685                if (k == 0 && grp->getFiles().size() == 1) {
2686                    // If this is the only file left, we need to keep it.
2687                    // Otherwise the resource IDs we are using will be inconsistent
2688                    // with what we get when not stripping.  Sucky, but at least
2689                    // for now we can rely on the back-end doing another filtering
2690                    // pass to take this out and leave us with this resource name
2691                    // containing no entries.
2692                    continue;
2693                }
2694                if (file->getPath().getPathExtension() == ".xml") {
2695                    // We can't remove .xml files at this point, because when
2696                    // we parse them they may add identifier resources, so
2697                    // removing them can cause our resource identifiers to
2698                    // become inconsistent.
2699                    continue;
2700                }
2701                const ResTable_config& config(file->getGroupEntry().toParams());
2702                if (!reqFilter.match(config)) {
2703                    if (bundle->getVerbose()) {
2704                        printf("Pruning unneeded resource: %s\n",
2705                                file->getPrintableSource().string());
2706                    }
2707                    grp->removeFile(k);
2708                    k--;
2709                }
2710            }
2711
2712            // Quick check: no preferred filters, nothing more to do.
2713            if (prefFilter.isEmpty()) {
2714                continue;
2715            }
2716
2717            // Get the preferred density if there is one. We do not match exactly for density.
2718            // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2719            // pick xhdpi.
2720            uint32_t preferredDensity = 0;
2721            const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
2722            if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
2723                preferredDensity = (*preferredConfigs)[0].intValue;
2724            }
2725
2726            // Now deal with preferred configurations.
2727            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2728                for (size_t k=0; k<grp->getFiles().size(); k++) {
2729                    sp<AaptFile> file = grp->getFiles().valueAt(k);
2730                    if (k == 0 && grp->getFiles().size() == 1) {
2731                        // If this is the only file left, we need to keep it.
2732                        // Otherwise the resource IDs we are using will be inconsistent
2733                        // with what we get when not stripping.  Sucky, but at least
2734                        // for now we can rely on the back-end doing another filtering
2735                        // pass to take this out and leave us with this resource name
2736                        // containing no entries.
2737                        continue;
2738                    }
2739                    if (file->getPath().getPathExtension() == ".xml") {
2740                        // We can't remove .xml files at this point, because when
2741                        // we parse them they may add identifier resources, so
2742                        // removing them can cause our resource identifiers to
2743                        // become inconsistent.
2744                        continue;
2745                    }
2746                    const ResTable_config& config(file->getGroupEntry().toParams());
2747                    if (!prefFilter.match(axis, config)) {
2748                        // This is a resource we would prefer not to have.  Check
2749                        // to see if have a similar variation that we would like
2750                        // to have and, if so, we can drop it.
2751
2752                        uint32_t bestDensity = config.density;
2753
2754                        for (size_t m=0; m<grp->getFiles().size(); m++) {
2755                            if (m == k) continue;
2756                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2757                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2758                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2759                                if (axis == AXIS_DENSITY && preferredDensity > 0) {
2760                                    // See if there is a better density resource
2761                                    if (mconfig.density < bestDensity &&
2762                                            mconfig.density > preferredDensity &&
2763                                            bestDensity > preferredDensity) {
2764                                        // This density is between our best density and
2765                                        // the preferred density, therefore it is better.
2766                                        bestDensity = mconfig.density;
2767                                    } else if (mconfig.density > bestDensity &&
2768                                            bestDensity < preferredDensity) {
2769                                        // This density is better than our best density and
2770                                        // our best density was smaller than our preferred
2771                                        // density, so it is better.
2772                                        bestDensity = mconfig.density;
2773                                    }
2774                                } else if (prefFilter.match(axis, mconfig)) {
2775                                    if (bundle->getVerbose()) {
2776                                        printf("Pruning unneeded resource: %s\n",
2777                                                file->getPrintableSource().string());
2778                                    }
2779                                    grp->removeFile(k);
2780                                    k--;
2781                                    break;
2782                                }
2783                            }
2784                        }
2785
2786                        if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2787                                bestDensity != config.density) {
2788                            if (bundle->getVerbose()) {
2789                                printf("Pruning unneeded resource: %s\n",
2790                                        file->getPrintableSource().string());
2791                            }
2792                            grp->removeFile(k);
2793                            k--;
2794                        }
2795                    }
2796                }
2797            }
2798        }
2799    }
2800
2801    return NO_ERROR;
2802}
2803
2804sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2805{
2806    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2807    if (sym == NULL) {
2808        sym = new AaptSymbols();
2809        mSymbols.add(name, sym);
2810    }
2811    return sym;
2812}
2813
2814sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2815{
2816    sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2817    if (sym == NULL) {
2818        sym = new AaptSymbols();
2819        mJavaSymbols.add(name, sym);
2820    }
2821    return sym;
2822}
2823
2824status_t AaptAssets::applyJavaSymbols()
2825{
2826    size_t N = mJavaSymbols.size();
2827    for (size_t i=0; i<N; i++) {
2828        const String8& name = mJavaSymbols.keyAt(i);
2829        const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2830        ssize_t pos = mSymbols.indexOfKey(name);
2831        if (pos < 0) {
2832            SourcePos pos;
2833            pos.error("Java symbol dir %s not defined\n", name.string());
2834            return UNKNOWN_ERROR;
2835        }
2836        //printf("**** applying java symbols in dir %s\n", name.string());
2837        status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2838        if (err != NO_ERROR) {
2839            return err;
2840        }
2841    }
2842
2843    return NO_ERROR;
2844}
2845
2846bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2847    //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2848    //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2849    //        sym.isJavaSymbol ? 1 : 0);
2850    if (!mHavePrivateSymbols) return true;
2851    if (sym.isPublic) return true;
2852    if (includePrivate && sym.isJavaSymbol) return true;
2853    return false;
2854}
2855
2856status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2857{
2858    if (!mHaveIncludedAssets) {
2859        // Add in all includes.
2860        const Vector<const char*>& incl = bundle->getPackageIncludes();
2861        const size_t N=incl.size();
2862        for (size_t i=0; i<N; i++) {
2863            if (bundle->getVerbose())
2864                printf("Including resources from package: %s\n", incl[i]);
2865            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2866                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2867                        incl[i]);
2868                return UNKNOWN_ERROR;
2869            }
2870        }
2871        mHaveIncludedAssets = true;
2872    }
2873
2874    return NO_ERROR;
2875}
2876
2877status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2878{
2879    const ResTable& res = getIncludedResources();
2880    // XXX dirty!
2881    return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
2882}
2883
2884const ResTable& AaptAssets::getIncludedResources() const
2885{
2886    return mIncludedAssets.getResources(false);
2887}
2888
2889void AaptAssets::print(const String8& prefix) const
2890{
2891    String8 innerPrefix(prefix);
2892    innerPrefix.append("  ");
2893    String8 innerInnerPrefix(innerPrefix);
2894    innerInnerPrefix.append("  ");
2895    printf("%sConfigurations:\n", prefix.string());
2896    const size_t N=mGroupEntries.size();
2897    for (size_t i=0; i<N; i++) {
2898        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2899        printf("%s %s\n", prefix.string(),
2900                cname != "" ? cname.string() : "(default)");
2901    }
2902
2903    printf("\n%sFiles:\n", prefix.string());
2904    AaptDir::print(innerPrefix);
2905
2906    printf("\n%sResource Dirs:\n", prefix.string());
2907    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2908    const size_t NR = resdirs.size();
2909    for (size_t i=0; i<NR; i++) {
2910        const sp<AaptDir>& d = resdirs.itemAt(i);
2911        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
2912        d->print(innerInnerPrefix);
2913    }
2914}
2915
2916sp<AaptDir> AaptAssets::resDir(const String8& name) const
2917{
2918    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2919    const size_t N = resdirs.size();
2920    for (size_t i=0; i<N; i++) {
2921        const sp<AaptDir>& d = resdirs.itemAt(i);
2922        if (d->getLeaf() == name) {
2923            return d;
2924        }
2925    }
2926    return NULL;
2927}
2928
2929bool
2930valid_symbol_name(const String8& symbol)
2931{
2932    static char const * const KEYWORDS[] = {
2933        "abstract", "assert", "boolean", "break",
2934        "byte", "case", "catch", "char", "class", "const", "continue",
2935        "default", "do", "double", "else", "enum", "extends", "final",
2936        "finally", "float", "for", "goto", "if", "implements", "import",
2937        "instanceof", "int", "interface", "long", "native", "new", "package",
2938        "private", "protected", "public", "return", "short", "static",
2939        "strictfp", "super", "switch", "synchronized", "this", "throw",
2940        "throws", "transient", "try", "void", "volatile", "while",
2941        "true", "false", "null",
2942        NULL
2943    };
2944    const char*const* k = KEYWORDS;
2945    const char*const s = symbol.string();
2946    while (*k) {
2947        if (0 == strcmp(s, *k)) {
2948            return false;
2949        }
2950        k++;
2951    }
2952    return true;
2953}
2954