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