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