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