AaptAssets.cpp revision 0a0454fdcc7aeac6e57f9466da8f39bcf5f3f6ec
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    } else if (strcmp(name, "watch") == 0) {
1346      if (out) out->uiMode =
1347              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1348              | ResTable_config::UI_MODE_TYPE_WATCH;
1349        return true;
1350    }
1351
1352    return false;
1353}
1354
1355bool AaptGroupEntry::getUiModeNightName(const char* name,
1356                                          ResTable_config* out)
1357{
1358    if (strcmp(name, kWildcardName) == 0) {
1359        if (out) out->uiMode =
1360                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1361                | ResTable_config::UI_MODE_NIGHT_ANY;
1362        return true;
1363    } else if (strcmp(name, "night") == 0) {
1364        if (out) out->uiMode =
1365                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1366                | ResTable_config::UI_MODE_NIGHT_YES;
1367        return true;
1368    } else if (strcmp(name, "notnight") == 0) {
1369      if (out) out->uiMode =
1370              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1371              | ResTable_config::UI_MODE_NIGHT_NO;
1372        return true;
1373    }
1374
1375    return false;
1376}
1377
1378bool AaptGroupEntry::getDensityName(const char* name,
1379                                    ResTable_config* out)
1380{
1381    if (strcmp(name, kWildcardName) == 0) {
1382        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
1383        return true;
1384    }
1385
1386    if (strcmp(name, "nodpi") == 0) {
1387        if (out) out->density = ResTable_config::DENSITY_NONE;
1388        return true;
1389    }
1390
1391    if (strcmp(name, "ldpi") == 0) {
1392        if (out) out->density = ResTable_config::DENSITY_LOW;
1393        return true;
1394    }
1395
1396    if (strcmp(name, "mdpi") == 0) {
1397        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1398        return true;
1399    }
1400
1401    if (strcmp(name, "tvdpi") == 0) {
1402        if (out) out->density = ResTable_config::DENSITY_TV;
1403        return true;
1404    }
1405
1406    if (strcmp(name, "hdpi") == 0) {
1407        if (out) out->density = ResTable_config::DENSITY_HIGH;
1408        return true;
1409    }
1410
1411    if (strcmp(name, "xhdpi") == 0) {
1412        if (out) out->density = ResTable_config::DENSITY_XHIGH;
1413        return true;
1414    }
1415
1416    if (strcmp(name, "xxhdpi") == 0) {
1417        if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1418        return true;
1419    }
1420
1421    if (strcmp(name, "xxxhdpi") == 0) {
1422        if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
1423        return true;
1424    }
1425
1426    char* c = (char*)name;
1427    while (*c >= '0' && *c <= '9') {
1428        c++;
1429    }
1430
1431    // check that we have 'dpi' after the last digit.
1432    if (toupper(c[0]) != 'D' ||
1433            toupper(c[1]) != 'P' ||
1434            toupper(c[2]) != 'I' ||
1435            c[3] != 0) {
1436        return false;
1437    }
1438
1439    // temporarily replace the first letter with \0 to
1440    // use atoi.
1441    char tmp = c[0];
1442    c[0] = '\0';
1443
1444    int d = atoi(name);
1445    c[0] = tmp;
1446
1447    if (d != 0) {
1448        if (out) out->density = d;
1449        return true;
1450    }
1451
1452    return false;
1453}
1454
1455bool AaptGroupEntry::getTouchscreenName(const char* name,
1456                                        ResTable_config* out)
1457{
1458    if (strcmp(name, kWildcardName) == 0) {
1459        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1460        return true;
1461    } else if (strcmp(name, "notouch") == 0) {
1462        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1463        return true;
1464    } else if (strcmp(name, "stylus") == 0) {
1465        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1466        return true;
1467    } else if (strcmp(name, "finger") == 0) {
1468        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1469        return true;
1470    }
1471
1472    return false;
1473}
1474
1475bool AaptGroupEntry::getKeysHiddenName(const char* name,
1476                                       ResTable_config* out)
1477{
1478    uint8_t mask = 0;
1479    uint8_t value = 0;
1480    if (strcmp(name, kWildcardName) == 0) {
1481        mask = ResTable_config::MASK_KEYSHIDDEN;
1482        value = ResTable_config::KEYSHIDDEN_ANY;
1483    } else if (strcmp(name, "keysexposed") == 0) {
1484        mask = ResTable_config::MASK_KEYSHIDDEN;
1485        value = ResTable_config::KEYSHIDDEN_NO;
1486    } else if (strcmp(name, "keyshidden") == 0) {
1487        mask = ResTable_config::MASK_KEYSHIDDEN;
1488        value = ResTable_config::KEYSHIDDEN_YES;
1489    } else if (strcmp(name, "keyssoft") == 0) {
1490        mask = ResTable_config::MASK_KEYSHIDDEN;
1491        value = ResTable_config::KEYSHIDDEN_SOFT;
1492    }
1493
1494    if (mask != 0) {
1495        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1496        return true;
1497    }
1498
1499    return false;
1500}
1501
1502bool AaptGroupEntry::getKeyboardName(const char* name,
1503                                        ResTable_config* out)
1504{
1505    if (strcmp(name, kWildcardName) == 0) {
1506        if (out) out->keyboard = out->KEYBOARD_ANY;
1507        return true;
1508    } else if (strcmp(name, "nokeys") == 0) {
1509        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1510        return true;
1511    } else if (strcmp(name, "qwerty") == 0) {
1512        if (out) out->keyboard = out->KEYBOARD_QWERTY;
1513        return true;
1514    } else if (strcmp(name, "12key") == 0) {
1515        if (out) out->keyboard = out->KEYBOARD_12KEY;
1516        return true;
1517    }
1518
1519    return false;
1520}
1521
1522bool AaptGroupEntry::getNavHiddenName(const char* name,
1523                                       ResTable_config* out)
1524{
1525    uint8_t mask = 0;
1526    uint8_t value = 0;
1527    if (strcmp(name, kWildcardName) == 0) {
1528        mask = ResTable_config::MASK_NAVHIDDEN;
1529        value = ResTable_config::NAVHIDDEN_ANY;
1530    } else if (strcmp(name, "navexposed") == 0) {
1531        mask = ResTable_config::MASK_NAVHIDDEN;
1532        value = ResTable_config::NAVHIDDEN_NO;
1533    } else if (strcmp(name, "navhidden") == 0) {
1534        mask = ResTable_config::MASK_NAVHIDDEN;
1535        value = ResTable_config::NAVHIDDEN_YES;
1536    }
1537
1538    if (mask != 0) {
1539        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1540        return true;
1541    }
1542
1543    return false;
1544}
1545
1546bool AaptGroupEntry::getNavigationName(const char* name,
1547                                     ResTable_config* out)
1548{
1549    if (strcmp(name, kWildcardName) == 0) {
1550        if (out) out->navigation = out->NAVIGATION_ANY;
1551        return true;
1552    } else if (strcmp(name, "nonav") == 0) {
1553        if (out) out->navigation = out->NAVIGATION_NONAV;
1554        return true;
1555    } else if (strcmp(name, "dpad") == 0) {
1556        if (out) out->navigation = out->NAVIGATION_DPAD;
1557        return true;
1558    } else if (strcmp(name, "trackball") == 0) {
1559        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1560        return true;
1561    } else if (strcmp(name, "wheel") == 0) {
1562        if (out) out->navigation = out->NAVIGATION_WHEEL;
1563        return true;
1564    }
1565
1566    return false;
1567}
1568
1569bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
1570{
1571    if (strcmp(name, kWildcardName) == 0) {
1572        if (out) {
1573            out->screenWidth = out->SCREENWIDTH_ANY;
1574            out->screenHeight = out->SCREENHEIGHT_ANY;
1575        }
1576        return true;
1577    }
1578
1579    const char* x = name;
1580    while (*x >= '0' && *x <= '9') x++;
1581    if (x == name || *x != 'x') return false;
1582    String8 xName(name, x-name);
1583    x++;
1584
1585    const char* y = x;
1586    while (*y >= '0' && *y <= '9') y++;
1587    if (y == name || *y != 0) return false;
1588    String8 yName(x, y-x);
1589
1590    uint16_t w = (uint16_t)atoi(xName.string());
1591    uint16_t h = (uint16_t)atoi(yName.string());
1592    if (w < h) {
1593        return false;
1594    }
1595
1596    if (out) {
1597        out->screenWidth = w;
1598        out->screenHeight = h;
1599    }
1600
1601    return true;
1602}
1603
1604bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1605{
1606    if (strcmp(name, kWildcardName) == 0) {
1607        if (out) {
1608            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1609        }
1610        return true;
1611    }
1612
1613    if (*name != 's') return false;
1614    name++;
1615    if (*name != 'w') return false;
1616    name++;
1617    const char* x = name;
1618    while (*x >= '0' && *x <= '9') x++;
1619    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1620    String8 xName(name, x-name);
1621
1622    if (out) {
1623        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1624    }
1625
1626    return true;
1627}
1628
1629bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1630{
1631    if (strcmp(name, kWildcardName) == 0) {
1632        if (out) {
1633            out->screenWidthDp = out->SCREENWIDTH_ANY;
1634        }
1635        return true;
1636    }
1637
1638    if (*name != 'w') return false;
1639    name++;
1640    const char* x = name;
1641    while (*x >= '0' && *x <= '9') x++;
1642    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1643    String8 xName(name, x-name);
1644
1645    if (out) {
1646        out->screenWidthDp = (uint16_t)atoi(xName.string());
1647    }
1648
1649    return true;
1650}
1651
1652bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1653{
1654    if (strcmp(name, kWildcardName) == 0) {
1655        if (out) {
1656            out->screenHeightDp = out->SCREENWIDTH_ANY;
1657        }
1658        return true;
1659    }
1660
1661    if (*name != 'h') return false;
1662    name++;
1663    const char* x = name;
1664    while (*x >= '0' && *x <= '9') x++;
1665    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1666    String8 xName(name, x-name);
1667
1668    if (out) {
1669        out->screenHeightDp = (uint16_t)atoi(xName.string());
1670    }
1671
1672    return true;
1673}
1674
1675bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
1676{
1677    if (strcmp(name, kWildcardName) == 0) {
1678        if (out) {
1679            out->sdkVersion = out->SDKVERSION_ANY;
1680            out->minorVersion = out->MINORVERSION_ANY;
1681        }
1682        return true;
1683    }
1684
1685    if (*name != 'v') {
1686        return false;
1687    }
1688
1689    name++;
1690    const char* s = name;
1691    while (*s >= '0' && *s <= '9') s++;
1692    if (s == name || *s != 0) return false;
1693    String8 sdkName(name, s-name);
1694
1695    if (out) {
1696        out->sdkVersion = (uint16_t)atoi(sdkName.string());
1697        out->minorVersion = 0;
1698    }
1699
1700    return true;
1701}
1702
1703int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1704{
1705    int v = mcc.compare(o.mcc);
1706    if (v == 0) v = mnc.compare(o.mnc);
1707    if (v == 0) v = locale.compare(o.locale);
1708    if (v == 0) v = layoutDirection.compare(o.layoutDirection);
1709    if (v == 0) v = vendor.compare(o.vendor);
1710    if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
1711    if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1712    if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
1713    if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1714    if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1715    if (v == 0) v = orientation.compare(o.orientation);
1716    if (v == 0) v = uiModeType.compare(o.uiModeType);
1717    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1718    if (v == 0) v = density.compare(o.density);
1719    if (v == 0) v = touchscreen.compare(o.touchscreen);
1720    if (v == 0) v = keysHidden.compare(o.keysHidden);
1721    if (v == 0) v = keyboard.compare(o.keyboard);
1722    if (v == 0) v = navHidden.compare(o.navHidden);
1723    if (v == 0) v = navigation.compare(o.navigation);
1724    if (v == 0) v = screenSize.compare(o.screenSize);
1725    if (v == 0) v = version.compare(o.version);
1726    return v;
1727}
1728
1729const ResTable_config AaptGroupEntry::toParams() const
1730{
1731    if (!mParamsChanged) {
1732        return mParams;
1733    }
1734
1735    mParamsChanged = false;
1736    ResTable_config& params = mParams;
1737    memset(&params, 0, sizeof(ResTable_config));
1738    getMccName(mcc.string(), &params);
1739    getMncName(mnc.string(), &params);
1740    locale.writeTo(&params);
1741    getLayoutDirectionName(layoutDirection.string(), &params);
1742    getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
1743    getScreenWidthDpName(screenWidthDp.string(), &params);
1744    getScreenHeightDpName(screenHeightDp.string(), &params);
1745    getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1746    getScreenLayoutLongName(screenLayoutLong.string(), &params);
1747    getOrientationName(orientation.string(), &params);
1748    getUiModeTypeName(uiModeType.string(), &params);
1749    getUiModeNightName(uiModeNight.string(), &params);
1750    getDensityName(density.string(), &params);
1751    getTouchscreenName(touchscreen.string(), &params);
1752    getKeysHiddenName(keysHidden.string(), &params);
1753    getKeyboardName(keyboard.string(), &params);
1754    getNavHiddenName(navHidden.string(), &params);
1755    getNavigationName(navigation.string(), &params);
1756    getScreenSizeName(screenSize.string(), &params);
1757    getVersionName(version.string(), &params);
1758
1759    // Fix up version number based on specified parameters.
1760    int minSdk = 0;
1761    if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1762            || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
1763            || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
1764        minSdk = SDK_HONEYCOMB_MR2;
1765    } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1766                != ResTable_config::UI_MODE_TYPE_ANY
1767            ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1768                != ResTable_config::UI_MODE_NIGHT_ANY) {
1769        minSdk = SDK_FROYO;
1770    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1771                != ResTable_config::SCREENSIZE_ANY
1772            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1773                != ResTable_config::SCREENLONG_ANY
1774            || params.density != ResTable_config::DENSITY_DEFAULT) {
1775        minSdk = SDK_DONUT;
1776    }
1777
1778    if (minSdk > params.sdkVersion) {
1779        params.sdkVersion = minSdk;
1780    }
1781
1782    return params;
1783}
1784
1785// =========================================================================
1786// =========================================================================
1787// =========================================================================
1788
1789void* AaptFile::editData(size_t size)
1790{
1791    if (size <= mBufferSize) {
1792        mDataSize = size;
1793        return mData;
1794    }
1795    size_t allocSize = (size*3)/2;
1796    void* buf = realloc(mData, allocSize);
1797    if (buf == NULL) {
1798        return NULL;
1799    }
1800    mData = buf;
1801    mDataSize = size;
1802    mBufferSize = allocSize;
1803    return buf;
1804}
1805
1806void* AaptFile::editDataInRange(size_t offset, size_t size)
1807{
1808    return (void*)(((uint8_t*) editData(offset + size)) + offset);
1809}
1810
1811void* AaptFile::editData(size_t* outSize)
1812{
1813    if (outSize) {
1814        *outSize = mDataSize;
1815    }
1816    return mData;
1817}
1818
1819void* AaptFile::padData(size_t wordSize)
1820{
1821    const size_t extra = mDataSize%wordSize;
1822    if (extra == 0) {
1823        return mData;
1824    }
1825
1826    size_t initial = mDataSize;
1827    void* data = editData(initial+(wordSize-extra));
1828    if (data != NULL) {
1829        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1830    }
1831    return data;
1832}
1833
1834status_t AaptFile::writeData(const void* data, size_t size)
1835{
1836    size_t end = mDataSize;
1837    size_t total = size + end;
1838    void* buf = editData(total);
1839    if (buf == NULL) {
1840        return UNKNOWN_ERROR;
1841    }
1842    memcpy(((char*)buf)+end, data, size);
1843    return NO_ERROR;
1844}
1845
1846void AaptFile::clearData()
1847{
1848    if (mData != NULL) free(mData);
1849    mData = NULL;
1850    mDataSize = 0;
1851    mBufferSize = 0;
1852}
1853
1854String8 AaptFile::getPrintableSource() const
1855{
1856    if (hasData()) {
1857        String8 name(mGroupEntry.toDirName(String8()));
1858        name.appendPath(mPath);
1859        name.append(" #generated");
1860        return name;
1861    }
1862    return mSourceFile;
1863}
1864
1865// =========================================================================
1866// =========================================================================
1867// =========================================================================
1868
1869status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
1870{
1871    ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
1872    if (index >= 0 && overwriteDuplicate) {
1873        fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
1874                mFiles[index]->getSourceFile().string(),
1875                file->getSourceFile().string());
1876        removeFile(index);
1877        index = -1;
1878    }
1879
1880    if (index < 0) {
1881        file->mPath = mPath;
1882        mFiles.add(file->getGroupEntry(), file);
1883        return NO_ERROR;
1884    }
1885
1886#if 0
1887    printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1888            file->getSourceFile().string(),
1889            file->getGroupEntry().toDirName(String8()).string(),
1890            mLeaf.string(), mPath.string());
1891#endif
1892
1893    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1894                                               getPrintableSource().string());
1895    return UNKNOWN_ERROR;
1896}
1897
1898void AaptGroup::removeFile(size_t index)
1899{
1900	mFiles.removeItemsAt(index);
1901}
1902
1903void AaptGroup::print(const String8& prefix) const
1904{
1905    printf("%s%s\n", prefix.string(), getPath().string());
1906    const size_t N=mFiles.size();
1907    size_t i;
1908    for (i=0; i<N; i++) {
1909        sp<AaptFile> file = mFiles.valueAt(i);
1910        const AaptGroupEntry& e = file->getGroupEntry();
1911        if (file->hasData()) {
1912            printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
1913                    (int)file->getSize());
1914        } else {
1915            printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1916                    file->getPrintableSource().string());
1917        }
1918        //printf("%s  File Group Entry: %s\n", prefix.string(),
1919        //        file->getGroupEntry().toDirName(String8()).string());
1920    }
1921}
1922
1923String8 AaptGroup::getPrintableSource() const
1924{
1925    if (mFiles.size() > 0) {
1926        // Arbitrarily pull the first source file out of the list.
1927        return mFiles.valueAt(0)->getPrintableSource();
1928    }
1929
1930    // Should never hit this case, but to be safe...
1931    return getPath();
1932
1933}
1934
1935// =========================================================================
1936// =========================================================================
1937// =========================================================================
1938
1939status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1940{
1941    if (mFiles.indexOfKey(name) >= 0) {
1942        return ALREADY_EXISTS;
1943    }
1944    mFiles.add(name, file);
1945    return NO_ERROR;
1946}
1947
1948status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1949{
1950    if (mDirs.indexOfKey(name) >= 0) {
1951        return ALREADY_EXISTS;
1952    }
1953    mDirs.add(name, dir);
1954    return NO_ERROR;
1955}
1956
1957sp<AaptDir> AaptDir::makeDir(const String8& path)
1958{
1959    String8 name;
1960    String8 remain = path;
1961
1962    sp<AaptDir> subdir = this;
1963    while (name = remain.walkPath(&remain), remain != "") {
1964        subdir = subdir->makeDir(name);
1965    }
1966
1967    ssize_t i = subdir->mDirs.indexOfKey(name);
1968    if (i >= 0) {
1969        return subdir->mDirs.valueAt(i);
1970    }
1971    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1972    subdir->mDirs.add(name, dir);
1973    return dir;
1974}
1975
1976void AaptDir::removeFile(const String8& name)
1977{
1978    mFiles.removeItem(name);
1979}
1980
1981void AaptDir::removeDir(const String8& name)
1982{
1983    mDirs.removeItem(name);
1984}
1985
1986status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
1987        const bool overwrite)
1988{
1989    sp<AaptGroup> group;
1990    if (mFiles.indexOfKey(leafName) >= 0) {
1991        group = mFiles.valueFor(leafName);
1992    } else {
1993        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1994        mFiles.add(leafName, group);
1995    }
1996
1997    return group->addFile(file, overwrite);
1998}
1999
2000ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
2001                            const AaptGroupEntry& kind, const String8& resType,
2002                            sp<FilePathStore>& fullResPaths, const bool overwrite)
2003{
2004    Vector<String8> fileNames;
2005    {
2006        DIR* dir = NULL;
2007
2008        dir = opendir(srcDir.string());
2009        if (dir == NULL) {
2010            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2011            return UNKNOWN_ERROR;
2012        }
2013
2014        /*
2015         * Slurp the filenames out of the directory.
2016         */
2017        while (1) {
2018            struct dirent* entry;
2019
2020            entry = readdir(dir);
2021            if (entry == NULL)
2022                break;
2023
2024            if (isHidden(srcDir.string(), entry->d_name))
2025                continue;
2026
2027            String8 name(entry->d_name);
2028            fileNames.add(name);
2029            // Add fully qualified path for dependency purposes
2030            // if we're collecting them
2031            if (fullResPaths != NULL) {
2032                fullResPaths->add(srcDir.appendPathCopy(name));
2033            }
2034        }
2035        closedir(dir);
2036    }
2037
2038    ssize_t count = 0;
2039
2040    /*
2041     * Stash away the files and recursively descend into subdirectories.
2042     */
2043    const size_t N = fileNames.size();
2044    size_t i;
2045    for (i = 0; i < N; i++) {
2046        String8 pathName(srcDir);
2047        FileType type;
2048
2049        pathName.appendPath(fileNames[i].string());
2050        type = getFileType(pathName.string());
2051        if (type == kFileTypeDirectory) {
2052            sp<AaptDir> subdir;
2053            bool notAdded = false;
2054            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
2055                subdir = mDirs.valueFor(fileNames[i]);
2056            } else {
2057                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
2058                notAdded = true;
2059            }
2060            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
2061                                                resType, fullResPaths, overwrite);
2062            if (res < NO_ERROR) {
2063                return res;
2064            }
2065            if (res > 0 && notAdded) {
2066                mDirs.add(fileNames[i], subdir);
2067            }
2068            count += res;
2069        } else if (type == kFileTypeRegular) {
2070            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
2071            status_t err = addLeafFile(fileNames[i], file, overwrite);
2072            if (err != NO_ERROR) {
2073                return err;
2074            }
2075
2076            count++;
2077
2078        } else {
2079            if (bundle->getVerbose())
2080                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
2081        }
2082    }
2083
2084    return count;
2085}
2086
2087status_t AaptDir::validate() const
2088{
2089    const size_t NF = mFiles.size();
2090    const size_t ND = mDirs.size();
2091    size_t i;
2092    for (i = 0; i < NF; i++) {
2093        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
2094            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2095                    "Invalid filename.  Unable to add.");
2096            return UNKNOWN_ERROR;
2097        }
2098
2099        size_t j;
2100        for (j = i+1; j < NF; j++) {
2101            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2102                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
2103                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2104                        "File is case-insensitive equivalent to: %s",
2105                        mFiles.valueAt(j)->getPrintableSource().string());
2106                return UNKNOWN_ERROR;
2107            }
2108
2109            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
2110            // (this is mostly caught by the "marked" stuff, below)
2111        }
2112
2113        for (j = 0; j < ND; j++) {
2114            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2115                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
2116                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2117                        "File conflicts with dir from: %s",
2118                        mDirs.valueAt(j)->getPrintableSource().string());
2119                return UNKNOWN_ERROR;
2120            }
2121        }
2122    }
2123
2124    for (i = 0; i < ND; i++) {
2125        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
2126            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2127                    "Invalid directory name, unable to add.");
2128            return UNKNOWN_ERROR;
2129        }
2130
2131        size_t j;
2132        for (j = i+1; j < ND; j++) {
2133            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
2134                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
2135                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2136                        "Directory is case-insensitive equivalent to: %s",
2137                        mDirs.valueAt(j)->getPrintableSource().string());
2138                return UNKNOWN_ERROR;
2139            }
2140        }
2141
2142        status_t err = mDirs.valueAt(i)->validate();
2143        if (err != NO_ERROR) {
2144            return err;
2145        }
2146    }
2147
2148    return NO_ERROR;
2149}
2150
2151void AaptDir::print(const String8& prefix) const
2152{
2153    const size_t ND=getDirs().size();
2154    size_t i;
2155    for (i=0; i<ND; i++) {
2156        getDirs().valueAt(i)->print(prefix);
2157    }
2158
2159    const size_t NF=getFiles().size();
2160    for (i=0; i<NF; i++) {
2161        getFiles().valueAt(i)->print(prefix);
2162    }
2163}
2164
2165String8 AaptDir::getPrintableSource() const
2166{
2167    if (mFiles.size() > 0) {
2168        // Arbitrarily pull the first file out of the list as the source dir.
2169        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
2170    }
2171    if (mDirs.size() > 0) {
2172        // Or arbitrarily pull the first dir out of the list as the source dir.
2173        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
2174    }
2175
2176    // Should never hit this case, but to be safe...
2177    return mPath;
2178
2179}
2180
2181// =========================================================================
2182// =========================================================================
2183// =========================================================================
2184
2185status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
2186{
2187    status_t err = NO_ERROR;
2188    size_t N = javaSymbols->mSymbols.size();
2189    for (size_t i=0; i<N; i++) {
2190        const String8& name = javaSymbols->mSymbols.keyAt(i);
2191        const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
2192        ssize_t pos = mSymbols.indexOfKey(name);
2193        if (pos < 0) {
2194            entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
2195            err = UNKNOWN_ERROR;
2196            continue;
2197        }
2198        //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
2199        //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
2200        mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
2201    }
2202
2203    N = javaSymbols->mNestedSymbols.size();
2204    for (size_t i=0; i<N; i++) {
2205        const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
2206        const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
2207        ssize_t pos = mNestedSymbols.indexOfKey(name);
2208        if (pos < 0) {
2209            SourcePos pos;
2210            pos.error("Java symbol dir %s not defined\n", name.string());
2211            err = UNKNOWN_ERROR;
2212            continue;
2213        }
2214        //printf("**** applying java symbols in dir %s\n", name.string());
2215        status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2216        if (myerr != NO_ERROR) {
2217            err = myerr;
2218        }
2219    }
2220
2221    return err;
2222}
2223
2224// =========================================================================
2225// =========================================================================
2226// =========================================================================
2227
2228AaptAssets::AaptAssets()
2229    : AaptDir(String8(), String8()),
2230      mHavePrivateSymbols(false),
2231      mChanged(false), mHaveIncludedAssets(false),
2232      mRes(NULL)
2233{
2234}
2235
2236const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
2237    if (mChanged) {
2238    }
2239    return mGroupEntries;
2240}
2241
2242status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
2243{
2244    mChanged = true;
2245    return AaptDir::addFile(name, file);
2246}
2247
2248sp<AaptFile> AaptAssets::addFile(
2249        const String8& filePath, const AaptGroupEntry& entry,
2250        const String8& srcDir, sp<AaptGroup>* outGroup,
2251        const String8& resType)
2252{
2253    sp<AaptDir> dir = this;
2254    sp<AaptGroup> group;
2255    sp<AaptFile> file;
2256    String8 root, remain(filePath), partialPath;
2257    while (remain.length() > 0) {
2258        root = remain.walkPath(&remain);
2259        partialPath.appendPath(root);
2260
2261        const String8 rootStr(root);
2262
2263        if (remain.length() == 0) {
2264            ssize_t i = dir->getFiles().indexOfKey(rootStr);
2265            if (i >= 0) {
2266                group = dir->getFiles().valueAt(i);
2267            } else {
2268                group = new AaptGroup(rootStr, filePath);
2269                status_t res = dir->addFile(rootStr, group);
2270                if (res != NO_ERROR) {
2271                    return NULL;
2272                }
2273            }
2274            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2275            status_t res = group->addFile(file);
2276            if (res != NO_ERROR) {
2277                return NULL;
2278            }
2279            break;
2280
2281        } else {
2282            ssize_t i = dir->getDirs().indexOfKey(rootStr);
2283            if (i >= 0) {
2284                dir = dir->getDirs().valueAt(i);
2285            } else {
2286                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2287                status_t res = dir->addDir(rootStr, subdir);
2288                if (res != NO_ERROR) {
2289                    return NULL;
2290                }
2291                dir = subdir;
2292            }
2293        }
2294    }
2295
2296    mGroupEntries.add(entry);
2297    if (outGroup) *outGroup = group;
2298    return file;
2299}
2300
2301void AaptAssets::addResource(const String8& leafName, const String8& path,
2302                const sp<AaptFile>& file, const String8& resType)
2303{
2304    sp<AaptDir> res = AaptDir::makeDir(kResString);
2305    String8 dirname = file->getGroupEntry().toDirName(resType);
2306    sp<AaptDir> subdir = res->makeDir(dirname);
2307    sp<AaptGroup> grr = new AaptGroup(leafName, path);
2308    grr->addFile(file);
2309
2310    subdir->addFile(leafName, grr);
2311}
2312
2313
2314ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2315{
2316    int count;
2317    int totalCount = 0;
2318    FileType type;
2319    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2320    const size_t dirCount =resDirs.size();
2321    sp<AaptAssets> current = this;
2322
2323    const int N = bundle->getFileSpecCount();
2324
2325    /*
2326     * If a package manifest was specified, include that first.
2327     */
2328    if (bundle->getAndroidManifestFile() != NULL) {
2329        // place at root of zip.
2330        String8 srcFile(bundle->getAndroidManifestFile());
2331        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2332                NULL, String8());
2333        totalCount++;
2334    }
2335
2336    /*
2337     * If a directory of custom assets was supplied, slurp 'em up.
2338     */
2339    const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
2340    const int AN = assetDirs.size();
2341    for (int i = 0; i < AN; i++) {
2342        FileType type = getFileType(assetDirs[i]);
2343        if (type == kFileTypeNonexistent) {
2344            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
2345            return UNKNOWN_ERROR;
2346        }
2347        if (type != kFileTypeDirectory) {
2348            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
2349            return UNKNOWN_ERROR;
2350        }
2351
2352        String8 assetRoot(assetDirs[i]);
2353        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2354        AaptGroupEntry group;
2355        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
2356                                            String8(), mFullAssetPaths, true);
2357        if (count < 0) {
2358            totalCount = count;
2359            goto bail;
2360        }
2361        if (count > 0) {
2362            mGroupEntries.add(group);
2363        }
2364        totalCount += count;
2365
2366        if (bundle->getVerbose()) {
2367            printf("Found %d custom asset file%s in %s\n",
2368                   count, (count==1) ? "" : "s", assetDirs[i]);
2369        }
2370    }
2371
2372    /*
2373     * If a directory of resource-specific assets was supplied, slurp 'em up.
2374     */
2375    for (size_t i=0; i<dirCount; i++) {
2376        const char *res = resDirs[i];
2377        if (res) {
2378            type = getFileType(res);
2379            if (type == kFileTypeNonexistent) {
2380                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2381                return UNKNOWN_ERROR;
2382            }
2383            if (type == kFileTypeDirectory) {
2384                if (i>0) {
2385                    sp<AaptAssets> nextOverlay = new AaptAssets();
2386                    current->setOverlay(nextOverlay);
2387                    current = nextOverlay;
2388                    current->setFullResPaths(mFullResPaths);
2389                }
2390                count = current->slurpResourceTree(bundle, String8(res));
2391                if (i > 0 && count > 0) {
2392                  count = current->filter(bundle);
2393                }
2394
2395                if (count < 0) {
2396                    totalCount = count;
2397                    goto bail;
2398                }
2399                totalCount += count;
2400            }
2401            else {
2402                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2403                return UNKNOWN_ERROR;
2404            }
2405        }
2406
2407    }
2408    /*
2409     * Now do any additional raw files.
2410     */
2411    for (int arg=0; arg<N; arg++) {
2412        const char* assetDir = bundle->getFileSpecEntry(arg);
2413
2414        FileType type = getFileType(assetDir);
2415        if (type == kFileTypeNonexistent) {
2416            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2417            return UNKNOWN_ERROR;
2418        }
2419        if (type != kFileTypeDirectory) {
2420            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2421            return UNKNOWN_ERROR;
2422        }
2423
2424        String8 assetRoot(assetDir);
2425
2426        if (bundle->getVerbose())
2427            printf("Processing raw dir '%s'\n", (const char*) assetDir);
2428
2429        /*
2430         * Do a recursive traversal of subdir tree.  We don't make any
2431         * guarantees about ordering, so we're okay with an inorder search
2432         * using whatever order the OS happens to hand back to us.
2433         */
2434        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2435        if (count < 0) {
2436            /* failure; report error and remove archive */
2437            totalCount = count;
2438            goto bail;
2439        }
2440        totalCount += count;
2441
2442        if (bundle->getVerbose())
2443            printf("Found %d asset file%s in %s\n",
2444                   count, (count==1) ? "" : "s", assetDir);
2445    }
2446
2447    count = validate();
2448    if (count != NO_ERROR) {
2449        totalCount = count;
2450        goto bail;
2451    }
2452
2453    count = filter(bundle);
2454    if (count != NO_ERROR) {
2455        totalCount = count;
2456        goto bail;
2457    }
2458
2459bail:
2460    return totalCount;
2461}
2462
2463ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2464                                    const AaptGroupEntry& kind,
2465                                    const String8& resType,
2466                                    sp<FilePathStore>& fullResPaths)
2467{
2468    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2469    if (res > 0) {
2470        mGroupEntries.add(kind);
2471    }
2472
2473    return res;
2474}
2475
2476ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2477{
2478    ssize_t err = 0;
2479
2480    DIR* dir = opendir(srcDir.string());
2481    if (dir == NULL) {
2482        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2483        return UNKNOWN_ERROR;
2484    }
2485
2486    status_t count = 0;
2487
2488    /*
2489     * Run through the directory, looking for dirs that match the
2490     * expected pattern.
2491     */
2492    while (1) {
2493        struct dirent* entry = readdir(dir);
2494        if (entry == NULL) {
2495            break;
2496        }
2497
2498        if (isHidden(srcDir.string(), entry->d_name)) {
2499            continue;
2500        }
2501
2502        String8 subdirName(srcDir);
2503        subdirName.appendPath(entry->d_name);
2504
2505        AaptGroupEntry group;
2506        String8 resType;
2507        bool b = group.initFromDirName(entry->d_name, &resType);
2508        if (!b) {
2509            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2510                    entry->d_name);
2511            err = -1;
2512            continue;
2513        }
2514
2515        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2516            int maxResInt = atoi(bundle->getMaxResVersion());
2517            const char *verString = group.getVersionString().string();
2518            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2519            if (dirVersionInt > maxResInt) {
2520              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2521              continue;
2522            }
2523        }
2524
2525        FileType type = getFileType(subdirName.string());
2526
2527        if (type == kFileTypeDirectory) {
2528            sp<AaptDir> dir = makeDir(resType);
2529            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2530                                                resType, mFullResPaths);
2531            if (res < 0) {
2532                count = res;
2533                goto bail;
2534            }
2535            if (res > 0) {
2536                mGroupEntries.add(group);
2537                count += res;
2538            }
2539
2540            // Only add this directory if we don't already have a resource dir
2541            // for the current type.  This ensures that we only add the dir once
2542            // for all configs.
2543            sp<AaptDir> rdir = resDir(resType);
2544            if (rdir == NULL) {
2545                mResDirs.add(dir);
2546            }
2547        } else {
2548            if (bundle->getVerbose()) {
2549                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2550            }
2551        }
2552    }
2553
2554bail:
2555    closedir(dir);
2556    dir = NULL;
2557
2558    if (err != 0) {
2559        return err;
2560    }
2561    return count;
2562}
2563
2564ssize_t
2565AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2566{
2567    int count = 0;
2568    SortedVector<AaptGroupEntry> entries;
2569
2570    ZipFile* zip = new ZipFile;
2571    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2572    if (err != NO_ERROR) {
2573        fprintf(stderr, "error opening zip file %s\n", filename);
2574        count = err;
2575        delete zip;
2576        return -1;
2577    }
2578
2579    const int N = zip->getNumEntries();
2580    for (int i=0; i<N; i++) {
2581        ZipEntry* entry = zip->getEntryByIndex(i);
2582        if (entry->getDeleted()) {
2583            continue;
2584        }
2585
2586        String8 entryName(entry->getFileName());
2587
2588        String8 dirName = entryName.getPathDir();
2589        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2590
2591        String8 resType;
2592        AaptGroupEntry kind;
2593
2594        String8 remain;
2595        if (entryName.walkPath(&remain) == kResourceDir) {
2596            // these are the resources, pull their type out of the directory name
2597            kind.initFromDirName(remain.walkPath().string(), &resType);
2598        } else {
2599            // these are untyped and don't have an AaptGroupEntry
2600        }
2601        if (entries.indexOf(kind) < 0) {
2602            entries.add(kind);
2603            mGroupEntries.add(kind);
2604        }
2605
2606        // use the one from the zip file if they both exist.
2607        dir->removeFile(entryName.getPathLeaf());
2608
2609        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2610        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2611        if (err != NO_ERROR) {
2612            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2613            count = err;
2614            goto bail;
2615        }
2616        file->setCompressionMethod(entry->getCompressionMethod());
2617
2618#if 0
2619        if (entryName == "AndroidManifest.xml") {
2620            printf("AndroidManifest.xml\n");
2621        }
2622        printf("\n\nfile: %s\n", entryName.string());
2623#endif
2624
2625        size_t len = entry->getUncompressedLen();
2626        void* data = zip->uncompress(entry);
2627        void* buf = file->editData(len);
2628        memcpy(buf, data, len);
2629
2630#if 0
2631        const int OFF = 0;
2632        const unsigned char* p = (unsigned char*)data;
2633        const unsigned char* end = p+len;
2634        p += OFF;
2635        for (int i=0; i<32 && p < end; i++) {
2636            printf("0x%03x ", i*0x10 + OFF);
2637            for (int j=0; j<0x10 && p < end; j++) {
2638                printf(" %02x", *p);
2639                p++;
2640            }
2641            printf("\n");
2642        }
2643#endif
2644
2645        free(data);
2646
2647        count++;
2648    }
2649
2650bail:
2651    delete zip;
2652    return count;
2653}
2654
2655status_t AaptAssets::filter(Bundle* bundle)
2656{
2657    ResourceFilter reqFilter;
2658    status_t err = reqFilter.parse(bundle->getConfigurations());
2659    if (err != NO_ERROR) {
2660        return err;
2661    }
2662
2663    ResourceFilter prefFilter;
2664    err = prefFilter.parse(bundle->getPreferredConfigurations());
2665    if (err != NO_ERROR) {
2666        return err;
2667    }
2668
2669    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2670        return NO_ERROR;
2671    }
2672
2673    if (bundle->getVerbose()) {
2674        if (!reqFilter.isEmpty()) {
2675            printf("Applying required filter: %s\n",
2676                    bundle->getConfigurations());
2677        }
2678        if (!prefFilter.isEmpty()) {
2679            printf("Applying preferred filter: %s\n",
2680                    bundle->getPreferredConfigurations());
2681        }
2682    }
2683
2684    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2685    const size_t ND = resdirs.size();
2686    for (size_t i=0; i<ND; i++) {
2687        const sp<AaptDir>& dir = resdirs.itemAt(i);
2688        if (dir->getLeaf() == kValuesDir) {
2689            // The "value" dir is special since a single file defines
2690            // multiple resources, so we can not do filtering on the
2691            // files themselves.
2692            continue;
2693        }
2694        if (dir->getLeaf() == kMipmapDir) {
2695            // We also skip the "mipmap" directory, since the point of this
2696            // is to include all densities without stripping.  If you put
2697            // other configurations in here as well they won't be stripped
2698            // either...  So don't do that.  Seriously.  What is wrong with you?
2699            continue;
2700        }
2701
2702        const size_t NG = dir->getFiles().size();
2703        for (size_t j=0; j<NG; j++) {
2704            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2705
2706            // First remove any configurations we know we don't need.
2707            for (size_t k=0; k<grp->getFiles().size(); k++) {
2708                sp<AaptFile> file = grp->getFiles().valueAt(k);
2709                if (k == 0 && grp->getFiles().size() == 1) {
2710                    // If this is the only file left, we need to keep it.
2711                    // Otherwise the resource IDs we are using will be inconsistent
2712                    // with what we get when not stripping.  Sucky, but at least
2713                    // for now we can rely on the back-end doing another filtering
2714                    // pass to take this out and leave us with this resource name
2715                    // containing no entries.
2716                    continue;
2717                }
2718                if (file->getPath().getPathExtension() == ".xml") {
2719                    // We can't remove .xml files at this point, because when
2720                    // we parse them they may add identifier resources, so
2721                    // removing them can cause our resource identifiers to
2722                    // become inconsistent.
2723                    continue;
2724                }
2725                const ResTable_config& config(file->getGroupEntry().toParams());
2726                if (!reqFilter.match(config)) {
2727                    if (bundle->getVerbose()) {
2728                        printf("Pruning unneeded resource: %s\n",
2729                                file->getPrintableSource().string());
2730                    }
2731                    grp->removeFile(k);
2732                    k--;
2733                }
2734            }
2735
2736            // Quick check: no preferred filters, nothing more to do.
2737            if (prefFilter.isEmpty()) {
2738                continue;
2739            }
2740
2741            // Get the preferred density if there is one. We do not match exactly for density.
2742            // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2743            // pick xhdpi.
2744            uint32_t preferredDensity = 0;
2745            const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
2746            if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
2747                preferredDensity = (*preferredConfigs)[0].intValue;
2748            }
2749
2750            // Now deal with preferred configurations.
2751            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2752                for (size_t k=0; k<grp->getFiles().size(); k++) {
2753                    sp<AaptFile> file = grp->getFiles().valueAt(k);
2754                    if (k == 0 && grp->getFiles().size() == 1) {
2755                        // If this is the only file left, we need to keep it.
2756                        // Otherwise the resource IDs we are using will be inconsistent
2757                        // with what we get when not stripping.  Sucky, but at least
2758                        // for now we can rely on the back-end doing another filtering
2759                        // pass to take this out and leave us with this resource name
2760                        // containing no entries.
2761                        continue;
2762                    }
2763                    if (file->getPath().getPathExtension() == ".xml") {
2764                        // We can't remove .xml files at this point, because when
2765                        // we parse them they may add identifier resources, so
2766                        // removing them can cause our resource identifiers to
2767                        // become inconsistent.
2768                        continue;
2769                    }
2770                    const ResTable_config& config(file->getGroupEntry().toParams());
2771                    if (!prefFilter.match(axis, config)) {
2772                        // This is a resource we would prefer not to have.  Check
2773                        // to see if have a similar variation that we would like
2774                        // to have and, if so, we can drop it.
2775
2776                        uint32_t bestDensity = config.density;
2777
2778                        for (size_t m=0; m<grp->getFiles().size(); m++) {
2779                            if (m == k) continue;
2780                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2781                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2782                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2783                                if (axis == AXIS_DENSITY && preferredDensity > 0) {
2784                                    // See if there is a better density resource
2785                                    if (mconfig.density < bestDensity &&
2786                                            mconfig.density > preferredDensity &&
2787                                            bestDensity > preferredDensity) {
2788                                        // This density is between our best density and
2789                                        // the preferred density, therefore it is better.
2790                                        bestDensity = mconfig.density;
2791                                    } else if (mconfig.density > bestDensity &&
2792                                            bestDensity < preferredDensity) {
2793                                        // This density is better than our best density and
2794                                        // our best density was smaller than our preferred
2795                                        // density, so it is better.
2796                                        bestDensity = mconfig.density;
2797                                    }
2798                                } else if (prefFilter.match(axis, mconfig)) {
2799                                    if (bundle->getVerbose()) {
2800                                        printf("Pruning unneeded resource: %s\n",
2801                                                file->getPrintableSource().string());
2802                                    }
2803                                    grp->removeFile(k);
2804                                    k--;
2805                                    break;
2806                                }
2807                            }
2808                        }
2809
2810                        if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2811                                bestDensity != config.density) {
2812                            if (bundle->getVerbose()) {
2813                                printf("Pruning unneeded resource: %s\n",
2814                                        file->getPrintableSource().string());
2815                            }
2816                            grp->removeFile(k);
2817                            k--;
2818                        }
2819                    }
2820                }
2821            }
2822        }
2823    }
2824
2825    return NO_ERROR;
2826}
2827
2828sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2829{
2830    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2831    if (sym == NULL) {
2832        sym = new AaptSymbols();
2833        mSymbols.add(name, sym);
2834    }
2835    return sym;
2836}
2837
2838sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2839{
2840    sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2841    if (sym == NULL) {
2842        sym = new AaptSymbols();
2843        mJavaSymbols.add(name, sym);
2844    }
2845    return sym;
2846}
2847
2848status_t AaptAssets::applyJavaSymbols()
2849{
2850    size_t N = mJavaSymbols.size();
2851    for (size_t i=0; i<N; i++) {
2852        const String8& name = mJavaSymbols.keyAt(i);
2853        const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2854        ssize_t pos = mSymbols.indexOfKey(name);
2855        if (pos < 0) {
2856            SourcePos pos;
2857            pos.error("Java symbol dir %s not defined\n", name.string());
2858            return UNKNOWN_ERROR;
2859        }
2860        //printf("**** applying java symbols in dir %s\n", name.string());
2861        status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2862        if (err != NO_ERROR) {
2863            return err;
2864        }
2865    }
2866
2867    return NO_ERROR;
2868}
2869
2870bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2871    //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2872    //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2873    //        sym.isJavaSymbol ? 1 : 0);
2874    if (!mHavePrivateSymbols) return true;
2875    if (sym.isPublic) return true;
2876    if (includePrivate && sym.isJavaSymbol) return true;
2877    return false;
2878}
2879
2880status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2881{
2882    if (!mHaveIncludedAssets) {
2883        // Add in all includes.
2884        const Vector<const char*>& incl = bundle->getPackageIncludes();
2885        const size_t N=incl.size();
2886        for (size_t i=0; i<N; i++) {
2887            if (bundle->getVerbose())
2888                printf("Including resources from package: %s\n", incl[i]);
2889            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2890                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2891                        incl[i]);
2892                return UNKNOWN_ERROR;
2893            }
2894        }
2895        mHaveIncludedAssets = true;
2896    }
2897
2898    return NO_ERROR;
2899}
2900
2901status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2902{
2903    const ResTable& res = getIncludedResources();
2904    // XXX dirty!
2905    return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
2906}
2907
2908const ResTable& AaptAssets::getIncludedResources() const
2909{
2910    return mIncludedAssets.getResources(false);
2911}
2912
2913void AaptAssets::print(const String8& prefix) const
2914{
2915    String8 innerPrefix(prefix);
2916    innerPrefix.append("  ");
2917    String8 innerInnerPrefix(innerPrefix);
2918    innerInnerPrefix.append("  ");
2919    printf("%sConfigurations:\n", prefix.string());
2920    const size_t N=mGroupEntries.size();
2921    for (size_t i=0; i<N; i++) {
2922        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2923        printf("%s %s\n", prefix.string(),
2924                cname != "" ? cname.string() : "(default)");
2925    }
2926
2927    printf("\n%sFiles:\n", prefix.string());
2928    AaptDir::print(innerPrefix);
2929
2930    printf("\n%sResource Dirs:\n", prefix.string());
2931    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2932    const size_t NR = resdirs.size();
2933    for (size_t i=0; i<NR; i++) {
2934        const sp<AaptDir>& d = resdirs.itemAt(i);
2935        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
2936        d->print(innerInnerPrefix);
2937    }
2938}
2939
2940sp<AaptDir> AaptAssets::resDir(const String8& name) const
2941{
2942    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2943    const size_t N = resdirs.size();
2944    for (size_t i=0; i<N; i++) {
2945        const sp<AaptDir>& d = resdirs.itemAt(i);
2946        if (d->getLeaf() == name) {
2947            return d;
2948        }
2949    }
2950    return NULL;
2951}
2952
2953bool
2954valid_symbol_name(const String8& symbol)
2955{
2956    static char const * const KEYWORDS[] = {
2957        "abstract", "assert", "boolean", "break",
2958        "byte", "case", "catch", "char", "class", "const", "continue",
2959        "default", "do", "double", "else", "enum", "extends", "final",
2960        "finally", "float", "for", "goto", "if", "implements", "import",
2961        "instanceof", "int", "interface", "long", "native", "new", "package",
2962        "private", "protected", "public", "return", "short", "static",
2963        "strictfp", "super", "switch", "synchronized", "this", "throw",
2964        "throws", "transient", "try", "void", "volatile", "while",
2965        "true", "false", "null",
2966        NULL
2967    };
2968    const char*const* k = KEYWORDS;
2969    const char*const s = symbol.string();
2970    while (*k) {
2971        if (0 == strcmp(s, *k)) {
2972            return false;
2973        }
2974        k++;
2975    }
2976    return true;
2977}
2978