AaptAssets.cpp revision de898ff42912bd7ca1bfb099cd439562496765a4
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::editDataInRange(size_t offset, size_t size)
1802{
1803    return (void*)(((uint8_t*) editData(offset + size)) + offset);
1804}
1805
1806void* AaptFile::editData(size_t* outSize)
1807{
1808    if (outSize) {
1809        *outSize = mDataSize;
1810    }
1811    return mData;
1812}
1813
1814void* AaptFile::padData(size_t wordSize)
1815{
1816    const size_t extra = mDataSize%wordSize;
1817    if (extra == 0) {
1818        return mData;
1819    }
1820
1821    size_t initial = mDataSize;
1822    void* data = editData(initial+(wordSize-extra));
1823    if (data != NULL) {
1824        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1825    }
1826    return data;
1827}
1828
1829status_t AaptFile::writeData(const void* data, size_t size)
1830{
1831    size_t end = mDataSize;
1832    size_t total = size + end;
1833    void* buf = editData(total);
1834    if (buf == NULL) {
1835        return UNKNOWN_ERROR;
1836    }
1837    memcpy(((char*)buf)+end, data, size);
1838    return NO_ERROR;
1839}
1840
1841void AaptFile::clearData()
1842{
1843    if (mData != NULL) free(mData);
1844    mData = NULL;
1845    mDataSize = 0;
1846    mBufferSize = 0;
1847}
1848
1849String8 AaptFile::getPrintableSource() const
1850{
1851    if (hasData()) {
1852        String8 name(mGroupEntry.toDirName(String8()));
1853        name.appendPath(mPath);
1854        name.append(" #generated");
1855        return name;
1856    }
1857    return mSourceFile;
1858}
1859
1860// =========================================================================
1861// =========================================================================
1862// =========================================================================
1863
1864status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
1865{
1866    ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
1867    if (index >= 0 && overwriteDuplicate) {
1868        fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
1869                mFiles[index]->getSourceFile().string(),
1870                file->getSourceFile().string());
1871        removeFile(index);
1872        index = -1;
1873    }
1874
1875    if (index < 0) {
1876        file->mPath = mPath;
1877        mFiles.add(file->getGroupEntry(), file);
1878        return NO_ERROR;
1879    }
1880
1881#if 0
1882    printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1883            file->getSourceFile().string(),
1884            file->getGroupEntry().toDirName(String8()).string(),
1885            mLeaf.string(), mPath.string());
1886#endif
1887
1888    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1889                                               getPrintableSource().string());
1890    return UNKNOWN_ERROR;
1891}
1892
1893void AaptGroup::removeFile(size_t index)
1894{
1895	mFiles.removeItemsAt(index);
1896}
1897
1898void AaptGroup::print(const String8& prefix) const
1899{
1900    printf("%s%s\n", prefix.string(), getPath().string());
1901    const size_t N=mFiles.size();
1902    size_t i;
1903    for (i=0; i<N; i++) {
1904        sp<AaptFile> file = mFiles.valueAt(i);
1905        const AaptGroupEntry& e = file->getGroupEntry();
1906        if (file->hasData()) {
1907            printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
1908                    (int)file->getSize());
1909        } else {
1910            printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1911                    file->getPrintableSource().string());
1912        }
1913        //printf("%s  File Group Entry: %s\n", prefix.string(),
1914        //        file->getGroupEntry().toDirName(String8()).string());
1915    }
1916}
1917
1918String8 AaptGroup::getPrintableSource() const
1919{
1920    if (mFiles.size() > 0) {
1921        // Arbitrarily pull the first source file out of the list.
1922        return mFiles.valueAt(0)->getPrintableSource();
1923    }
1924
1925    // Should never hit this case, but to be safe...
1926    return getPath();
1927
1928}
1929
1930// =========================================================================
1931// =========================================================================
1932// =========================================================================
1933
1934status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1935{
1936    if (mFiles.indexOfKey(name) >= 0) {
1937        return ALREADY_EXISTS;
1938    }
1939    mFiles.add(name, file);
1940    return NO_ERROR;
1941}
1942
1943status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1944{
1945    if (mDirs.indexOfKey(name) >= 0) {
1946        return ALREADY_EXISTS;
1947    }
1948    mDirs.add(name, dir);
1949    return NO_ERROR;
1950}
1951
1952sp<AaptDir> AaptDir::makeDir(const String8& path)
1953{
1954    String8 name;
1955    String8 remain = path;
1956
1957    sp<AaptDir> subdir = this;
1958    while (name = remain.walkPath(&remain), remain != "") {
1959        subdir = subdir->makeDir(name);
1960    }
1961
1962    ssize_t i = subdir->mDirs.indexOfKey(name);
1963    if (i >= 0) {
1964        return subdir->mDirs.valueAt(i);
1965    }
1966    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1967    subdir->mDirs.add(name, dir);
1968    return dir;
1969}
1970
1971void AaptDir::removeFile(const String8& name)
1972{
1973    mFiles.removeItem(name);
1974}
1975
1976void AaptDir::removeDir(const String8& name)
1977{
1978    mDirs.removeItem(name);
1979}
1980
1981status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
1982        const bool overwrite)
1983{
1984    sp<AaptGroup> group;
1985    if (mFiles.indexOfKey(leafName) >= 0) {
1986        group = mFiles.valueFor(leafName);
1987    } else {
1988        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1989        mFiles.add(leafName, group);
1990    }
1991
1992    return group->addFile(file, overwrite);
1993}
1994
1995ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1996                            const AaptGroupEntry& kind, const String8& resType,
1997                            sp<FilePathStore>& fullResPaths, const bool overwrite)
1998{
1999    Vector<String8> fileNames;
2000    {
2001        DIR* dir = NULL;
2002
2003        dir = opendir(srcDir.string());
2004        if (dir == NULL) {
2005            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2006            return UNKNOWN_ERROR;
2007        }
2008
2009        /*
2010         * Slurp the filenames out of the directory.
2011         */
2012        while (1) {
2013            struct dirent* entry;
2014
2015            entry = readdir(dir);
2016            if (entry == NULL)
2017                break;
2018
2019            if (isHidden(srcDir.string(), entry->d_name))
2020                continue;
2021
2022            String8 name(entry->d_name);
2023            fileNames.add(name);
2024            // Add fully qualified path for dependency purposes
2025            // if we're collecting them
2026            if (fullResPaths != NULL) {
2027                fullResPaths->add(srcDir.appendPathCopy(name));
2028            }
2029        }
2030        closedir(dir);
2031    }
2032
2033    ssize_t count = 0;
2034
2035    /*
2036     * Stash away the files and recursively descend into subdirectories.
2037     */
2038    const size_t N = fileNames.size();
2039    size_t i;
2040    for (i = 0; i < N; i++) {
2041        String8 pathName(srcDir);
2042        FileType type;
2043
2044        pathName.appendPath(fileNames[i].string());
2045        type = getFileType(pathName.string());
2046        if (type == kFileTypeDirectory) {
2047            sp<AaptDir> subdir;
2048            bool notAdded = false;
2049            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
2050                subdir = mDirs.valueFor(fileNames[i]);
2051            } else {
2052                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
2053                notAdded = true;
2054            }
2055            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
2056                                                resType, fullResPaths, overwrite);
2057            if (res < NO_ERROR) {
2058                return res;
2059            }
2060            if (res > 0 && notAdded) {
2061                mDirs.add(fileNames[i], subdir);
2062            }
2063            count += res;
2064        } else if (type == kFileTypeRegular) {
2065            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
2066            status_t err = addLeafFile(fileNames[i], file, overwrite);
2067            if (err != NO_ERROR) {
2068                return err;
2069            }
2070
2071            count++;
2072
2073        } else {
2074            if (bundle->getVerbose())
2075                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
2076        }
2077    }
2078
2079    return count;
2080}
2081
2082status_t AaptDir::validate() const
2083{
2084    const size_t NF = mFiles.size();
2085    const size_t ND = mDirs.size();
2086    size_t i;
2087    for (i = 0; i < NF; i++) {
2088        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
2089            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2090                    "Invalid filename.  Unable to add.");
2091            return UNKNOWN_ERROR;
2092        }
2093
2094        size_t j;
2095        for (j = i+1; j < NF; j++) {
2096            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2097                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
2098                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2099                        "File is case-insensitive equivalent to: %s",
2100                        mFiles.valueAt(j)->getPrintableSource().string());
2101                return UNKNOWN_ERROR;
2102            }
2103
2104            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
2105            // (this is mostly caught by the "marked" stuff, below)
2106        }
2107
2108        for (j = 0; j < ND; j++) {
2109            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2110                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
2111                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2112                        "File conflicts with dir from: %s",
2113                        mDirs.valueAt(j)->getPrintableSource().string());
2114                return UNKNOWN_ERROR;
2115            }
2116        }
2117    }
2118
2119    for (i = 0; i < ND; i++) {
2120        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
2121            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2122                    "Invalid directory name, unable to add.");
2123            return UNKNOWN_ERROR;
2124        }
2125
2126        size_t j;
2127        for (j = i+1; j < ND; j++) {
2128            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
2129                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
2130                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2131                        "Directory is case-insensitive equivalent to: %s",
2132                        mDirs.valueAt(j)->getPrintableSource().string());
2133                return UNKNOWN_ERROR;
2134            }
2135        }
2136
2137        status_t err = mDirs.valueAt(i)->validate();
2138        if (err != NO_ERROR) {
2139            return err;
2140        }
2141    }
2142
2143    return NO_ERROR;
2144}
2145
2146void AaptDir::print(const String8& prefix) const
2147{
2148    const size_t ND=getDirs().size();
2149    size_t i;
2150    for (i=0; i<ND; i++) {
2151        getDirs().valueAt(i)->print(prefix);
2152    }
2153
2154    const size_t NF=getFiles().size();
2155    for (i=0; i<NF; i++) {
2156        getFiles().valueAt(i)->print(prefix);
2157    }
2158}
2159
2160String8 AaptDir::getPrintableSource() const
2161{
2162    if (mFiles.size() > 0) {
2163        // Arbitrarily pull the first file out of the list as the source dir.
2164        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
2165    }
2166    if (mDirs.size() > 0) {
2167        // Or arbitrarily pull the first dir out of the list as the source dir.
2168        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
2169    }
2170
2171    // Should never hit this case, but to be safe...
2172    return mPath;
2173
2174}
2175
2176// =========================================================================
2177// =========================================================================
2178// =========================================================================
2179
2180status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
2181{
2182    status_t err = NO_ERROR;
2183    size_t N = javaSymbols->mSymbols.size();
2184    for (size_t i=0; i<N; i++) {
2185        const String8& name = javaSymbols->mSymbols.keyAt(i);
2186        const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
2187        ssize_t pos = mSymbols.indexOfKey(name);
2188        if (pos < 0) {
2189            entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
2190            err = UNKNOWN_ERROR;
2191            continue;
2192        }
2193        //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
2194        //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
2195        mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
2196    }
2197
2198    N = javaSymbols->mNestedSymbols.size();
2199    for (size_t i=0; i<N; i++) {
2200        const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
2201        const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
2202        ssize_t pos = mNestedSymbols.indexOfKey(name);
2203        if (pos < 0) {
2204            SourcePos pos;
2205            pos.error("Java symbol dir %s not defined\n", name.string());
2206            err = UNKNOWN_ERROR;
2207            continue;
2208        }
2209        //printf("**** applying java symbols in dir %s\n", name.string());
2210        status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2211        if (myerr != NO_ERROR) {
2212            err = myerr;
2213        }
2214    }
2215
2216    return err;
2217}
2218
2219// =========================================================================
2220// =========================================================================
2221// =========================================================================
2222
2223AaptAssets::AaptAssets()
2224    : AaptDir(String8(), String8()),
2225      mHavePrivateSymbols(false),
2226      mChanged(false), mHaveIncludedAssets(false),
2227      mRes(NULL)
2228{
2229}
2230
2231const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
2232    if (mChanged) {
2233    }
2234    return mGroupEntries;
2235}
2236
2237status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
2238{
2239    mChanged = true;
2240    return AaptDir::addFile(name, file);
2241}
2242
2243sp<AaptFile> AaptAssets::addFile(
2244        const String8& filePath, const AaptGroupEntry& entry,
2245        const String8& srcDir, sp<AaptGroup>* outGroup,
2246        const String8& resType)
2247{
2248    sp<AaptDir> dir = this;
2249    sp<AaptGroup> group;
2250    sp<AaptFile> file;
2251    String8 root, remain(filePath), partialPath;
2252    while (remain.length() > 0) {
2253        root = remain.walkPath(&remain);
2254        partialPath.appendPath(root);
2255
2256        const String8 rootStr(root);
2257
2258        if (remain.length() == 0) {
2259            ssize_t i = dir->getFiles().indexOfKey(rootStr);
2260            if (i >= 0) {
2261                group = dir->getFiles().valueAt(i);
2262            } else {
2263                group = new AaptGroup(rootStr, filePath);
2264                status_t res = dir->addFile(rootStr, group);
2265                if (res != NO_ERROR) {
2266                    return NULL;
2267                }
2268            }
2269            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2270            status_t res = group->addFile(file);
2271            if (res != NO_ERROR) {
2272                return NULL;
2273            }
2274            break;
2275
2276        } else {
2277            ssize_t i = dir->getDirs().indexOfKey(rootStr);
2278            if (i >= 0) {
2279                dir = dir->getDirs().valueAt(i);
2280            } else {
2281                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2282                status_t res = dir->addDir(rootStr, subdir);
2283                if (res != NO_ERROR) {
2284                    return NULL;
2285                }
2286                dir = subdir;
2287            }
2288        }
2289    }
2290
2291    mGroupEntries.add(entry);
2292    if (outGroup) *outGroup = group;
2293    return file;
2294}
2295
2296void AaptAssets::addResource(const String8& leafName, const String8& path,
2297                const sp<AaptFile>& file, const String8& resType)
2298{
2299    sp<AaptDir> res = AaptDir::makeDir(kResString);
2300    String8 dirname = file->getGroupEntry().toDirName(resType);
2301    sp<AaptDir> subdir = res->makeDir(dirname);
2302    sp<AaptGroup> grr = new AaptGroup(leafName, path);
2303    grr->addFile(file);
2304
2305    subdir->addFile(leafName, grr);
2306}
2307
2308
2309ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2310{
2311    int count;
2312    int totalCount = 0;
2313    FileType type;
2314    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2315    const size_t dirCount =resDirs.size();
2316    sp<AaptAssets> current = this;
2317
2318    const int N = bundle->getFileSpecCount();
2319
2320    /*
2321     * If a package manifest was specified, include that first.
2322     */
2323    if (bundle->getAndroidManifestFile() != NULL) {
2324        // place at root of zip.
2325        String8 srcFile(bundle->getAndroidManifestFile());
2326        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2327                NULL, String8());
2328        totalCount++;
2329    }
2330
2331    /*
2332     * If a directory of custom assets was supplied, slurp 'em up.
2333     */
2334    const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
2335    const int AN = assetDirs.size();
2336    for (int i = 0; i < AN; i++) {
2337        FileType type = getFileType(assetDirs[i]);
2338        if (type == kFileTypeNonexistent) {
2339            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
2340            return UNKNOWN_ERROR;
2341        }
2342        if (type != kFileTypeDirectory) {
2343            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
2344            return UNKNOWN_ERROR;
2345        }
2346
2347        String8 assetRoot(assetDirs[i]);
2348        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2349        AaptGroupEntry group;
2350        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
2351                                            String8(), mFullAssetPaths, true);
2352        if (count < 0) {
2353            totalCount = count;
2354            goto bail;
2355        }
2356        if (count > 0) {
2357            mGroupEntries.add(group);
2358        }
2359        totalCount += count;
2360
2361        if (bundle->getVerbose()) {
2362            printf("Found %d custom asset file%s in %s\n",
2363                   count, (count==1) ? "" : "s", assetDirs[i]);
2364        }
2365    }
2366
2367    /*
2368     * If a directory of resource-specific assets was supplied, slurp 'em up.
2369     */
2370    for (size_t i=0; i<dirCount; i++) {
2371        const char *res = resDirs[i];
2372        if (res) {
2373            type = getFileType(res);
2374            if (type == kFileTypeNonexistent) {
2375                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2376                return UNKNOWN_ERROR;
2377            }
2378            if (type == kFileTypeDirectory) {
2379                if (i>0) {
2380                    sp<AaptAssets> nextOverlay = new AaptAssets();
2381                    current->setOverlay(nextOverlay);
2382                    current = nextOverlay;
2383                    current->setFullResPaths(mFullResPaths);
2384                }
2385                count = current->slurpResourceTree(bundle, String8(res));
2386                if (i > 0 && count > 0) {
2387                  count = current->filter(bundle);
2388                }
2389
2390                if (count < 0) {
2391                    totalCount = count;
2392                    goto bail;
2393                }
2394                totalCount += count;
2395            }
2396            else {
2397                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2398                return UNKNOWN_ERROR;
2399            }
2400        }
2401
2402    }
2403    /*
2404     * Now do any additional raw files.
2405     */
2406    for (int arg=0; arg<N; arg++) {
2407        const char* assetDir = bundle->getFileSpecEntry(arg);
2408
2409        FileType type = getFileType(assetDir);
2410        if (type == kFileTypeNonexistent) {
2411            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2412            return UNKNOWN_ERROR;
2413        }
2414        if (type != kFileTypeDirectory) {
2415            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2416            return UNKNOWN_ERROR;
2417        }
2418
2419        String8 assetRoot(assetDir);
2420
2421        if (bundle->getVerbose())
2422            printf("Processing raw dir '%s'\n", (const char*) assetDir);
2423
2424        /*
2425         * Do a recursive traversal of subdir tree.  We don't make any
2426         * guarantees about ordering, so we're okay with an inorder search
2427         * using whatever order the OS happens to hand back to us.
2428         */
2429        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2430        if (count < 0) {
2431            /* failure; report error and remove archive */
2432            totalCount = count;
2433            goto bail;
2434        }
2435        totalCount += count;
2436
2437        if (bundle->getVerbose())
2438            printf("Found %d asset file%s in %s\n",
2439                   count, (count==1) ? "" : "s", assetDir);
2440    }
2441
2442    count = validate();
2443    if (count != NO_ERROR) {
2444        totalCount = count;
2445        goto bail;
2446    }
2447
2448    count = filter(bundle);
2449    if (count != NO_ERROR) {
2450        totalCount = count;
2451        goto bail;
2452    }
2453
2454bail:
2455    return totalCount;
2456}
2457
2458ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2459                                    const AaptGroupEntry& kind,
2460                                    const String8& resType,
2461                                    sp<FilePathStore>& fullResPaths)
2462{
2463    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2464    if (res > 0) {
2465        mGroupEntries.add(kind);
2466    }
2467
2468    return res;
2469}
2470
2471ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2472{
2473    ssize_t err = 0;
2474
2475    DIR* dir = opendir(srcDir.string());
2476    if (dir == NULL) {
2477        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2478        return UNKNOWN_ERROR;
2479    }
2480
2481    status_t count = 0;
2482
2483    /*
2484     * Run through the directory, looking for dirs that match the
2485     * expected pattern.
2486     */
2487    while (1) {
2488        struct dirent* entry = readdir(dir);
2489        if (entry == NULL) {
2490            break;
2491        }
2492
2493        if (isHidden(srcDir.string(), entry->d_name)) {
2494            continue;
2495        }
2496
2497        String8 subdirName(srcDir);
2498        subdirName.appendPath(entry->d_name);
2499
2500        AaptGroupEntry group;
2501        String8 resType;
2502        bool b = group.initFromDirName(entry->d_name, &resType);
2503        if (!b) {
2504            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2505                    entry->d_name);
2506            err = -1;
2507            continue;
2508        }
2509
2510        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2511            int maxResInt = atoi(bundle->getMaxResVersion());
2512            const char *verString = group.getVersionString().string();
2513            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2514            if (dirVersionInt > maxResInt) {
2515              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2516              continue;
2517            }
2518        }
2519
2520        FileType type = getFileType(subdirName.string());
2521
2522        if (type == kFileTypeDirectory) {
2523            sp<AaptDir> dir = makeDir(resType);
2524            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2525                                                resType, mFullResPaths);
2526            if (res < 0) {
2527                count = res;
2528                goto bail;
2529            }
2530            if (res > 0) {
2531                mGroupEntries.add(group);
2532                count += res;
2533            }
2534
2535            // Only add this directory if we don't already have a resource dir
2536            // for the current type.  This ensures that we only add the dir once
2537            // for all configs.
2538            sp<AaptDir> rdir = resDir(resType);
2539            if (rdir == NULL) {
2540                mResDirs.add(dir);
2541            }
2542        } else {
2543            if (bundle->getVerbose()) {
2544                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2545            }
2546        }
2547    }
2548
2549bail:
2550    closedir(dir);
2551    dir = NULL;
2552
2553    if (err != 0) {
2554        return err;
2555    }
2556    return count;
2557}
2558
2559ssize_t
2560AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2561{
2562    int count = 0;
2563    SortedVector<AaptGroupEntry> entries;
2564
2565    ZipFile* zip = new ZipFile;
2566    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2567    if (err != NO_ERROR) {
2568        fprintf(stderr, "error opening zip file %s\n", filename);
2569        count = err;
2570        delete zip;
2571        return -1;
2572    }
2573
2574    const int N = zip->getNumEntries();
2575    for (int i=0; i<N; i++) {
2576        ZipEntry* entry = zip->getEntryByIndex(i);
2577        if (entry->getDeleted()) {
2578            continue;
2579        }
2580
2581        String8 entryName(entry->getFileName());
2582
2583        String8 dirName = entryName.getPathDir();
2584        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2585
2586        String8 resType;
2587        AaptGroupEntry kind;
2588
2589        String8 remain;
2590        if (entryName.walkPath(&remain) == kResourceDir) {
2591            // these are the resources, pull their type out of the directory name
2592            kind.initFromDirName(remain.walkPath().string(), &resType);
2593        } else {
2594            // these are untyped and don't have an AaptGroupEntry
2595        }
2596        if (entries.indexOf(kind) < 0) {
2597            entries.add(kind);
2598            mGroupEntries.add(kind);
2599        }
2600
2601        // use the one from the zip file if they both exist.
2602        dir->removeFile(entryName.getPathLeaf());
2603
2604        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2605        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2606        if (err != NO_ERROR) {
2607            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2608            count = err;
2609            goto bail;
2610        }
2611        file->setCompressionMethod(entry->getCompressionMethod());
2612
2613#if 0
2614        if (entryName == "AndroidManifest.xml") {
2615            printf("AndroidManifest.xml\n");
2616        }
2617        printf("\n\nfile: %s\n", entryName.string());
2618#endif
2619
2620        size_t len = entry->getUncompressedLen();
2621        void* data = zip->uncompress(entry);
2622        void* buf = file->editData(len);
2623        memcpy(buf, data, len);
2624
2625#if 0
2626        const int OFF = 0;
2627        const unsigned char* p = (unsigned char*)data;
2628        const unsigned char* end = p+len;
2629        p += OFF;
2630        for (int i=0; i<32 && p < end; i++) {
2631            printf("0x%03x ", i*0x10 + OFF);
2632            for (int j=0; j<0x10 && p < end; j++) {
2633                printf(" %02x", *p);
2634                p++;
2635            }
2636            printf("\n");
2637        }
2638#endif
2639
2640        free(data);
2641
2642        count++;
2643    }
2644
2645bail:
2646    delete zip;
2647    return count;
2648}
2649
2650status_t AaptAssets::filter(Bundle* bundle)
2651{
2652    ResourceFilter reqFilter;
2653    status_t err = reqFilter.parse(bundle->getConfigurations());
2654    if (err != NO_ERROR) {
2655        return err;
2656    }
2657
2658    ResourceFilter prefFilter;
2659    err = prefFilter.parse(bundle->getPreferredConfigurations());
2660    if (err != NO_ERROR) {
2661        return err;
2662    }
2663
2664    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2665        return NO_ERROR;
2666    }
2667
2668    if (bundle->getVerbose()) {
2669        if (!reqFilter.isEmpty()) {
2670            printf("Applying required filter: %s\n",
2671                    bundle->getConfigurations());
2672        }
2673        if (!prefFilter.isEmpty()) {
2674            printf("Applying preferred filter: %s\n",
2675                    bundle->getPreferredConfigurations());
2676        }
2677    }
2678
2679    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2680    const size_t ND = resdirs.size();
2681    for (size_t i=0; i<ND; i++) {
2682        const sp<AaptDir>& dir = resdirs.itemAt(i);
2683        if (dir->getLeaf() == kValuesDir) {
2684            // The "value" dir is special since a single file defines
2685            // multiple resources, so we can not do filtering on the
2686            // files themselves.
2687            continue;
2688        }
2689        if (dir->getLeaf() == kMipmapDir) {
2690            // We also skip the "mipmap" directory, since the point of this
2691            // is to include all densities without stripping.  If you put
2692            // other configurations in here as well they won't be stripped
2693            // either...  So don't do that.  Seriously.  What is wrong with you?
2694            continue;
2695        }
2696
2697        const size_t NG = dir->getFiles().size();
2698        for (size_t j=0; j<NG; j++) {
2699            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2700
2701            // First remove any configurations we know we don't need.
2702            for (size_t k=0; k<grp->getFiles().size(); k++) {
2703                sp<AaptFile> file = grp->getFiles().valueAt(k);
2704                if (k == 0 && grp->getFiles().size() == 1) {
2705                    // If this is the only file left, we need to keep it.
2706                    // Otherwise the resource IDs we are using will be inconsistent
2707                    // with what we get when not stripping.  Sucky, but at least
2708                    // for now we can rely on the back-end doing another filtering
2709                    // pass to take this out and leave us with this resource name
2710                    // containing no entries.
2711                    continue;
2712                }
2713                if (file->getPath().getPathExtension() == ".xml") {
2714                    // We can't remove .xml files at this point, because when
2715                    // we parse them they may add identifier resources, so
2716                    // removing them can cause our resource identifiers to
2717                    // become inconsistent.
2718                    continue;
2719                }
2720                const ResTable_config& config(file->getGroupEntry().toParams());
2721                if (!reqFilter.match(config)) {
2722                    if (bundle->getVerbose()) {
2723                        printf("Pruning unneeded resource: %s\n",
2724                                file->getPrintableSource().string());
2725                    }
2726                    grp->removeFile(k);
2727                    k--;
2728                }
2729            }
2730
2731            // Quick check: no preferred filters, nothing more to do.
2732            if (prefFilter.isEmpty()) {
2733                continue;
2734            }
2735
2736            // Get the preferred density if there is one. We do not match exactly for density.
2737            // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2738            // pick xhdpi.
2739            uint32_t preferredDensity = 0;
2740            const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
2741            if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
2742                preferredDensity = (*preferredConfigs)[0].intValue;
2743            }
2744
2745            // Now deal with preferred configurations.
2746            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2747                for (size_t k=0; k<grp->getFiles().size(); k++) {
2748                    sp<AaptFile> file = grp->getFiles().valueAt(k);
2749                    if (k == 0 && grp->getFiles().size() == 1) {
2750                        // If this is the only file left, we need to keep it.
2751                        // Otherwise the resource IDs we are using will be inconsistent
2752                        // with what we get when not stripping.  Sucky, but at least
2753                        // for now we can rely on the back-end doing another filtering
2754                        // pass to take this out and leave us with this resource name
2755                        // containing no entries.
2756                        continue;
2757                    }
2758                    if (file->getPath().getPathExtension() == ".xml") {
2759                        // We can't remove .xml files at this point, because when
2760                        // we parse them they may add identifier resources, so
2761                        // removing them can cause our resource identifiers to
2762                        // become inconsistent.
2763                        continue;
2764                    }
2765                    const ResTable_config& config(file->getGroupEntry().toParams());
2766                    if (!prefFilter.match(axis, config)) {
2767                        // This is a resource we would prefer not to have.  Check
2768                        // to see if have a similar variation that we would like
2769                        // to have and, if so, we can drop it.
2770
2771                        uint32_t bestDensity = config.density;
2772
2773                        for (size_t m=0; m<grp->getFiles().size(); m++) {
2774                            if (m == k) continue;
2775                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2776                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2777                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2778                                if (axis == AXIS_DENSITY && preferredDensity > 0) {
2779                                    // See if there is a better density resource
2780                                    if (mconfig.density < bestDensity &&
2781                                            mconfig.density > preferredDensity &&
2782                                            bestDensity > preferredDensity) {
2783                                        // This density is between our best density and
2784                                        // the preferred density, therefore it is better.
2785                                        bestDensity = mconfig.density;
2786                                    } else if (mconfig.density > bestDensity &&
2787                                            bestDensity < preferredDensity) {
2788                                        // This density is better than our best density and
2789                                        // our best density was smaller than our preferred
2790                                        // density, so it is better.
2791                                        bestDensity = mconfig.density;
2792                                    }
2793                                } else if (prefFilter.match(axis, mconfig)) {
2794                                    if (bundle->getVerbose()) {
2795                                        printf("Pruning unneeded resource: %s\n",
2796                                                file->getPrintableSource().string());
2797                                    }
2798                                    grp->removeFile(k);
2799                                    k--;
2800                                    break;
2801                                }
2802                            }
2803                        }
2804
2805                        if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2806                                bestDensity != config.density) {
2807                            if (bundle->getVerbose()) {
2808                                printf("Pruning unneeded resource: %s\n",
2809                                        file->getPrintableSource().string());
2810                            }
2811                            grp->removeFile(k);
2812                            k--;
2813                        }
2814                    }
2815                }
2816            }
2817        }
2818    }
2819
2820    return NO_ERROR;
2821}
2822
2823sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2824{
2825    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2826    if (sym == NULL) {
2827        sym = new AaptSymbols();
2828        mSymbols.add(name, sym);
2829    }
2830    return sym;
2831}
2832
2833sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2834{
2835    sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2836    if (sym == NULL) {
2837        sym = new AaptSymbols();
2838        mJavaSymbols.add(name, sym);
2839    }
2840    return sym;
2841}
2842
2843status_t AaptAssets::applyJavaSymbols()
2844{
2845    size_t N = mJavaSymbols.size();
2846    for (size_t i=0; i<N; i++) {
2847        const String8& name = mJavaSymbols.keyAt(i);
2848        const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2849        ssize_t pos = mSymbols.indexOfKey(name);
2850        if (pos < 0) {
2851            SourcePos pos;
2852            pos.error("Java symbol dir %s not defined\n", name.string());
2853            return UNKNOWN_ERROR;
2854        }
2855        //printf("**** applying java symbols in dir %s\n", name.string());
2856        status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2857        if (err != NO_ERROR) {
2858            return err;
2859        }
2860    }
2861
2862    return NO_ERROR;
2863}
2864
2865bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2866    //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2867    //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2868    //        sym.isJavaSymbol ? 1 : 0);
2869    if (!mHavePrivateSymbols) return true;
2870    if (sym.isPublic) return true;
2871    if (includePrivate && sym.isJavaSymbol) return true;
2872    return false;
2873}
2874
2875status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2876{
2877    if (!mHaveIncludedAssets) {
2878        // Add in all includes.
2879        const Vector<const char*>& incl = bundle->getPackageIncludes();
2880        const size_t N=incl.size();
2881        for (size_t i=0; i<N; i++) {
2882            if (bundle->getVerbose())
2883                printf("Including resources from package: %s\n", incl[i]);
2884            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2885                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2886                        incl[i]);
2887                return UNKNOWN_ERROR;
2888            }
2889        }
2890        mHaveIncludedAssets = true;
2891    }
2892
2893    return NO_ERROR;
2894}
2895
2896status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2897{
2898    const ResTable& res = getIncludedResources();
2899    // XXX dirty!
2900    return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
2901}
2902
2903const ResTable& AaptAssets::getIncludedResources() const
2904{
2905    return mIncludedAssets.getResources(false);
2906}
2907
2908void AaptAssets::print(const String8& prefix) const
2909{
2910    String8 innerPrefix(prefix);
2911    innerPrefix.append("  ");
2912    String8 innerInnerPrefix(innerPrefix);
2913    innerInnerPrefix.append("  ");
2914    printf("%sConfigurations:\n", prefix.string());
2915    const size_t N=mGroupEntries.size();
2916    for (size_t i=0; i<N; i++) {
2917        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2918        printf("%s %s\n", prefix.string(),
2919                cname != "" ? cname.string() : "(default)");
2920    }
2921
2922    printf("\n%sFiles:\n", prefix.string());
2923    AaptDir::print(innerPrefix);
2924
2925    printf("\n%sResource Dirs:\n", prefix.string());
2926    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2927    const size_t NR = resdirs.size();
2928    for (size_t i=0; i<NR; i++) {
2929        const sp<AaptDir>& d = resdirs.itemAt(i);
2930        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
2931        d->print(innerInnerPrefix);
2932    }
2933}
2934
2935sp<AaptDir> AaptAssets::resDir(const String8& name) const
2936{
2937    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2938    const size_t N = resdirs.size();
2939    for (size_t i=0; i<N; i++) {
2940        const sp<AaptDir>& d = resdirs.itemAt(i);
2941        if (d->getLeaf() == name) {
2942            return d;
2943        }
2944    }
2945    return NULL;
2946}
2947
2948bool
2949valid_symbol_name(const String8& symbol)
2950{
2951    static char const * const KEYWORDS[] = {
2952        "abstract", "assert", "boolean", "break",
2953        "byte", "case", "catch", "char", "class", "const", "continue",
2954        "default", "do", "double", "else", "enum", "extends", "final",
2955        "finally", "float", "for", "goto", "if", "implements", "import",
2956        "instanceof", "int", "interface", "long", "native", "new", "package",
2957        "private", "protected", "public", "return", "short", "static",
2958        "strictfp", "super", "switch", "synchronized", "this", "throw",
2959        "throws", "transient", "try", "void", "volatile", "while",
2960        "true", "false", "null",
2961        NULL
2962    };
2963    const char*const* k = KEYWORDS;
2964    const char*const s = symbol.string();
2965    while (*k) {
2966        if (0 == strcmp(s, *k)) {
2967            return false;
2968        }
2969        k++;
2970    }
2971    return true;
2972}
2973