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    } else if (strcmp(name, "appliance") == 0) {
1061      if (out) out->uiMode =
1062              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1063              | ResTable_config::UI_MODE_TYPE_APPLIANCE;
1064        return true;
1065    }
1066
1067    return false;
1068}
1069
1070bool AaptGroupEntry::getUiModeNightName(const char* name,
1071                                          ResTable_config* out)
1072{
1073    if (strcmp(name, kWildcardName) == 0) {
1074        if (out) out->uiMode =
1075                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1076                | ResTable_config::UI_MODE_NIGHT_ANY;
1077        return true;
1078    } else if (strcmp(name, "night") == 0) {
1079        if (out) out->uiMode =
1080                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1081                | ResTable_config::UI_MODE_NIGHT_YES;
1082        return true;
1083    } else if (strcmp(name, "notnight") == 0) {
1084      if (out) out->uiMode =
1085              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1086              | ResTable_config::UI_MODE_NIGHT_NO;
1087        return true;
1088    }
1089
1090    return false;
1091}
1092
1093bool AaptGroupEntry::getDensityName(const char* name,
1094                                    ResTable_config* out)
1095{
1096    if (strcmp(name, kWildcardName) == 0) {
1097        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
1098        return true;
1099    }
1100
1101    if (strcmp(name, "nodpi") == 0) {
1102        if (out) out->density = ResTable_config::DENSITY_NONE;
1103        return true;
1104    }
1105
1106    if (strcmp(name, "ldpi") == 0) {
1107        if (out) out->density = ResTable_config::DENSITY_LOW;
1108        return true;
1109    }
1110
1111    if (strcmp(name, "mdpi") == 0) {
1112        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1113        return true;
1114    }
1115
1116    if (strcmp(name, "tvdpi") == 0) {
1117        if (out) out->density = ResTable_config::DENSITY_TV;
1118        return true;
1119    }
1120
1121    if (strcmp(name, "hdpi") == 0) {
1122        if (out) out->density = ResTable_config::DENSITY_HIGH;
1123        return true;
1124    }
1125
1126    if (strcmp(name, "xhdpi") == 0) {
1127        if (out) out->density = ResTable_config::DENSITY_XHIGH;
1128        return true;
1129    }
1130
1131    if (strcmp(name, "xxhdpi") == 0) {
1132        if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1133        return true;
1134    }
1135
1136    char* c = (char*)name;
1137    while (*c >= '0' && *c <= '9') {
1138        c++;
1139    }
1140
1141    // check that we have 'dpi' after the last digit.
1142    if (toupper(c[0]) != 'D' ||
1143            toupper(c[1]) != 'P' ||
1144            toupper(c[2]) != 'I' ||
1145            c[3] != 0) {
1146        return false;
1147    }
1148
1149    // temporarily replace the first letter with \0 to
1150    // use atoi.
1151    char tmp = c[0];
1152    c[0] = '\0';
1153
1154    int d = atoi(name);
1155    c[0] = tmp;
1156
1157    if (d != 0) {
1158        if (out) out->density = d;
1159        return true;
1160    }
1161
1162    return false;
1163}
1164
1165bool AaptGroupEntry::getTouchscreenName(const char* name,
1166                                        ResTable_config* out)
1167{
1168    if (strcmp(name, kWildcardName) == 0) {
1169        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1170        return true;
1171    } else if (strcmp(name, "notouch") == 0) {
1172        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1173        return true;
1174    } else if (strcmp(name, "stylus") == 0) {
1175        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1176        return true;
1177    } else if (strcmp(name, "finger") == 0) {
1178        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1179        return true;
1180    }
1181
1182    return false;
1183}
1184
1185bool AaptGroupEntry::getKeysHiddenName(const char* name,
1186                                       ResTable_config* out)
1187{
1188    uint8_t mask = 0;
1189    uint8_t value = 0;
1190    if (strcmp(name, kWildcardName) == 0) {
1191        mask = ResTable_config::MASK_KEYSHIDDEN;
1192        value = ResTable_config::KEYSHIDDEN_ANY;
1193    } else if (strcmp(name, "keysexposed") == 0) {
1194        mask = ResTable_config::MASK_KEYSHIDDEN;
1195        value = ResTable_config::KEYSHIDDEN_NO;
1196    } else if (strcmp(name, "keyshidden") == 0) {
1197        mask = ResTable_config::MASK_KEYSHIDDEN;
1198        value = ResTable_config::KEYSHIDDEN_YES;
1199    } else if (strcmp(name, "keyssoft") == 0) {
1200        mask = ResTable_config::MASK_KEYSHIDDEN;
1201        value = ResTable_config::KEYSHIDDEN_SOFT;
1202    }
1203
1204    if (mask != 0) {
1205        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1206        return true;
1207    }
1208
1209    return false;
1210}
1211
1212bool AaptGroupEntry::getKeyboardName(const char* name,
1213                                        ResTable_config* out)
1214{
1215    if (strcmp(name, kWildcardName) == 0) {
1216        if (out) out->keyboard = out->KEYBOARD_ANY;
1217        return true;
1218    } else if (strcmp(name, "nokeys") == 0) {
1219        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1220        return true;
1221    } else if (strcmp(name, "qwerty") == 0) {
1222        if (out) out->keyboard = out->KEYBOARD_QWERTY;
1223        return true;
1224    } else if (strcmp(name, "12key") == 0) {
1225        if (out) out->keyboard = out->KEYBOARD_12KEY;
1226        return true;
1227    }
1228
1229    return false;
1230}
1231
1232bool AaptGroupEntry::getNavHiddenName(const char* name,
1233                                       ResTable_config* out)
1234{
1235    uint8_t mask = 0;
1236    uint8_t value = 0;
1237    if (strcmp(name, kWildcardName) == 0) {
1238        mask = ResTable_config::MASK_NAVHIDDEN;
1239        value = ResTable_config::NAVHIDDEN_ANY;
1240    } else if (strcmp(name, "navexposed") == 0) {
1241        mask = ResTable_config::MASK_NAVHIDDEN;
1242        value = ResTable_config::NAVHIDDEN_NO;
1243    } else if (strcmp(name, "navhidden") == 0) {
1244        mask = ResTable_config::MASK_NAVHIDDEN;
1245        value = ResTable_config::NAVHIDDEN_YES;
1246    }
1247
1248    if (mask != 0) {
1249        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1250        return true;
1251    }
1252
1253    return false;
1254}
1255
1256bool AaptGroupEntry::getNavigationName(const char* name,
1257                                     ResTable_config* out)
1258{
1259    if (strcmp(name, kWildcardName) == 0) {
1260        if (out) out->navigation = out->NAVIGATION_ANY;
1261        return true;
1262    } else if (strcmp(name, "nonav") == 0) {
1263        if (out) out->navigation = out->NAVIGATION_NONAV;
1264        return true;
1265    } else if (strcmp(name, "dpad") == 0) {
1266        if (out) out->navigation = out->NAVIGATION_DPAD;
1267        return true;
1268    } else if (strcmp(name, "trackball") == 0) {
1269        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1270        return true;
1271    } else if (strcmp(name, "wheel") == 0) {
1272        if (out) out->navigation = out->NAVIGATION_WHEEL;
1273        return true;
1274    }
1275
1276    return false;
1277}
1278
1279bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
1280{
1281    if (strcmp(name, kWildcardName) == 0) {
1282        if (out) {
1283            out->screenWidth = out->SCREENWIDTH_ANY;
1284            out->screenHeight = out->SCREENHEIGHT_ANY;
1285        }
1286        return true;
1287    }
1288
1289    const char* x = name;
1290    while (*x >= '0' && *x <= '9') x++;
1291    if (x == name || *x != 'x') return false;
1292    String8 xName(name, x-name);
1293    x++;
1294
1295    const char* y = x;
1296    while (*y >= '0' && *y <= '9') y++;
1297    if (y == name || *y != 0) return false;
1298    String8 yName(x, y-x);
1299
1300    uint16_t w = (uint16_t)atoi(xName.string());
1301    uint16_t h = (uint16_t)atoi(yName.string());
1302    if (w < h) {
1303        return false;
1304    }
1305
1306    if (out) {
1307        out->screenWidth = w;
1308        out->screenHeight = h;
1309    }
1310
1311    return true;
1312}
1313
1314bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1315{
1316    if (strcmp(name, kWildcardName) == 0) {
1317        if (out) {
1318            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1319        }
1320        return true;
1321    }
1322
1323    if (*name != 's') return false;
1324    name++;
1325    if (*name != 'w') return false;
1326    name++;
1327    const char* x = name;
1328    while (*x >= '0' && *x <= '9') x++;
1329    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1330    String8 xName(name, x-name);
1331
1332    if (out) {
1333        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1334    }
1335
1336    return true;
1337}
1338
1339bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1340{
1341    if (strcmp(name, kWildcardName) == 0) {
1342        if (out) {
1343            out->screenWidthDp = out->SCREENWIDTH_ANY;
1344        }
1345        return true;
1346    }
1347
1348    if (*name != 'w') return false;
1349    name++;
1350    const char* x = name;
1351    while (*x >= '0' && *x <= '9') x++;
1352    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1353    String8 xName(name, x-name);
1354
1355    if (out) {
1356        out->screenWidthDp = (uint16_t)atoi(xName.string());
1357    }
1358
1359    return true;
1360}
1361
1362bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1363{
1364    if (strcmp(name, kWildcardName) == 0) {
1365        if (out) {
1366            out->screenHeightDp = out->SCREENWIDTH_ANY;
1367        }
1368        return true;
1369    }
1370
1371    if (*name != 'h') return false;
1372    name++;
1373    const char* x = name;
1374    while (*x >= '0' && *x <= '9') x++;
1375    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1376    String8 xName(name, x-name);
1377
1378    if (out) {
1379        out->screenHeightDp = (uint16_t)atoi(xName.string());
1380    }
1381
1382    return true;
1383}
1384
1385bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
1386{
1387    if (strcmp(name, kWildcardName) == 0) {
1388        if (out) {
1389            out->sdkVersion = out->SDKVERSION_ANY;
1390            out->minorVersion = out->MINORVERSION_ANY;
1391        }
1392        return true;
1393    }
1394
1395    if (*name != 'v') {
1396        return false;
1397    }
1398
1399    name++;
1400    const char* s = name;
1401    while (*s >= '0' && *s <= '9') s++;
1402    if (s == name || *s != 0) return false;
1403    String8 sdkName(name, s-name);
1404
1405    if (out) {
1406        out->sdkVersion = (uint16_t)atoi(sdkName.string());
1407        out->minorVersion = 0;
1408    }
1409
1410    return true;
1411}
1412
1413int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1414{
1415    int v = mcc.compare(o.mcc);
1416    if (v == 0) v = mnc.compare(o.mnc);
1417    if (v == 0) v = locale.compare(o.locale);
1418    if (v == 0) v = vendor.compare(o.vendor);
1419    if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
1420    if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1421    if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
1422    if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1423    if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1424    if (v == 0) v = orientation.compare(o.orientation);
1425    if (v == 0) v = uiModeType.compare(o.uiModeType);
1426    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1427    if (v == 0) v = density.compare(o.density);
1428    if (v == 0) v = touchscreen.compare(o.touchscreen);
1429    if (v == 0) v = keysHidden.compare(o.keysHidden);
1430    if (v == 0) v = keyboard.compare(o.keyboard);
1431    if (v == 0) v = navHidden.compare(o.navHidden);
1432    if (v == 0) v = navigation.compare(o.navigation);
1433    if (v == 0) v = screenSize.compare(o.screenSize);
1434    if (v == 0) v = version.compare(o.version);
1435    return v;
1436}
1437
1438const ResTable_config& AaptGroupEntry::toParams() const
1439{
1440    if (!mParamsChanged) {
1441        return mParams;
1442    }
1443
1444    mParamsChanged = false;
1445    ResTable_config& params(mParams);
1446    memset(&params, 0, sizeof(params));
1447    getMccName(mcc.string(), &params);
1448    getMncName(mnc.string(), &params);
1449    getLocaleName(locale.string(), &params);
1450    getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
1451    getScreenWidthDpName(screenWidthDp.string(), &params);
1452    getScreenHeightDpName(screenHeightDp.string(), &params);
1453    getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1454    getScreenLayoutLongName(screenLayoutLong.string(), &params);
1455    getOrientationName(orientation.string(), &params);
1456    getUiModeTypeName(uiModeType.string(), &params);
1457    getUiModeNightName(uiModeNight.string(), &params);
1458    getDensityName(density.string(), &params);
1459    getTouchscreenName(touchscreen.string(), &params);
1460    getKeysHiddenName(keysHidden.string(), &params);
1461    getKeyboardName(keyboard.string(), &params);
1462    getNavHiddenName(navHidden.string(), &params);
1463    getNavigationName(navigation.string(), &params);
1464    getScreenSizeName(screenSize.string(), &params);
1465    getVersionName(version.string(), &params);
1466
1467    // Fix up version number based on specified parameters.
1468    int minSdk = 0;
1469    if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1470            || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
1471            || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
1472        minSdk = SDK_HONEYCOMB_MR2;
1473    } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1474                != ResTable_config::UI_MODE_TYPE_ANY
1475            ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1476                != ResTable_config::UI_MODE_NIGHT_ANY) {
1477        minSdk = SDK_FROYO;
1478    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1479                != ResTable_config::SCREENSIZE_ANY
1480            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1481                != ResTable_config::SCREENLONG_ANY
1482            || params.density != ResTable_config::DENSITY_DEFAULT) {
1483        minSdk = SDK_DONUT;
1484    }
1485
1486    if (minSdk > params.sdkVersion) {
1487        params.sdkVersion = minSdk;
1488    }
1489
1490    return params;
1491}
1492
1493// =========================================================================
1494// =========================================================================
1495// =========================================================================
1496
1497void* AaptFile::editData(size_t size)
1498{
1499    if (size <= mBufferSize) {
1500        mDataSize = size;
1501        return mData;
1502    }
1503    size_t allocSize = (size*3)/2;
1504    void* buf = realloc(mData, allocSize);
1505    if (buf == NULL) {
1506        return NULL;
1507    }
1508    mData = buf;
1509    mDataSize = size;
1510    mBufferSize = allocSize;
1511    return buf;
1512}
1513
1514void* AaptFile::editData(size_t* outSize)
1515{
1516    if (outSize) {
1517        *outSize = mDataSize;
1518    }
1519    return mData;
1520}
1521
1522void* AaptFile::padData(size_t wordSize)
1523{
1524    const size_t extra = mDataSize%wordSize;
1525    if (extra == 0) {
1526        return mData;
1527    }
1528
1529    size_t initial = mDataSize;
1530    void* data = editData(initial+(wordSize-extra));
1531    if (data != NULL) {
1532        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1533    }
1534    return data;
1535}
1536
1537status_t AaptFile::writeData(const void* data, size_t size)
1538{
1539    size_t end = mDataSize;
1540    size_t total = size + end;
1541    void* buf = editData(total);
1542    if (buf == NULL) {
1543        return UNKNOWN_ERROR;
1544    }
1545    memcpy(((char*)buf)+end, data, size);
1546    return NO_ERROR;
1547}
1548
1549void AaptFile::clearData()
1550{
1551    if (mData != NULL) free(mData);
1552    mData = NULL;
1553    mDataSize = 0;
1554    mBufferSize = 0;
1555}
1556
1557String8 AaptFile::getPrintableSource() const
1558{
1559    if (hasData()) {
1560        String8 name(mGroupEntry.toDirName(String8()));
1561        name.appendPath(mPath);
1562        name.append(" #generated");
1563        return name;
1564    }
1565    return mSourceFile;
1566}
1567
1568// =========================================================================
1569// =========================================================================
1570// =========================================================================
1571
1572status_t AaptGroup::addFile(const sp<AaptFile>& file)
1573{
1574    if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1575        file->mPath = mPath;
1576        mFiles.add(file->getGroupEntry(), file);
1577        return NO_ERROR;
1578    }
1579
1580#if 0
1581    printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1582            file->getSourceFile().string(),
1583            file->getGroupEntry().toDirName(String8()).string(),
1584            mLeaf.string(), mPath.string());
1585#endif
1586
1587    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1588                                               getPrintableSource().string());
1589    return UNKNOWN_ERROR;
1590}
1591
1592void AaptGroup::removeFile(size_t index)
1593{
1594	mFiles.removeItemsAt(index);
1595}
1596
1597void AaptGroup::print(const String8& prefix) const
1598{
1599    printf("%s%s\n", prefix.string(), getPath().string());
1600    const size_t N=mFiles.size();
1601    size_t i;
1602    for (i=0; i<N; i++) {
1603        sp<AaptFile> file = mFiles.valueAt(i);
1604        const AaptGroupEntry& e = file->getGroupEntry();
1605        if (file->hasData()) {
1606            printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
1607                    (int)file->getSize());
1608        } else {
1609            printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1610                    file->getPrintableSource().string());
1611        }
1612        //printf("%s  File Group Entry: %s\n", prefix.string(),
1613        //        file->getGroupEntry().toDirName(String8()).string());
1614    }
1615}
1616
1617String8 AaptGroup::getPrintableSource() const
1618{
1619    if (mFiles.size() > 0) {
1620        // Arbitrarily pull the first source file out of the list.
1621        return mFiles.valueAt(0)->getPrintableSource();
1622    }
1623
1624    // Should never hit this case, but to be safe...
1625    return getPath();
1626
1627}
1628
1629// =========================================================================
1630// =========================================================================
1631// =========================================================================
1632
1633status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1634{
1635    if (mFiles.indexOfKey(name) >= 0) {
1636        return ALREADY_EXISTS;
1637    }
1638    mFiles.add(name, file);
1639    return NO_ERROR;
1640}
1641
1642status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1643{
1644    if (mDirs.indexOfKey(name) >= 0) {
1645        return ALREADY_EXISTS;
1646    }
1647    mDirs.add(name, dir);
1648    return NO_ERROR;
1649}
1650
1651sp<AaptDir> AaptDir::makeDir(const String8& path)
1652{
1653    String8 name;
1654    String8 remain = path;
1655
1656    sp<AaptDir> subdir = this;
1657    while (name = remain.walkPath(&remain), remain != "") {
1658        subdir = subdir->makeDir(name);
1659    }
1660
1661    ssize_t i = subdir->mDirs.indexOfKey(name);
1662    if (i >= 0) {
1663        return subdir->mDirs.valueAt(i);
1664    }
1665    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1666    subdir->mDirs.add(name, dir);
1667    return dir;
1668}
1669
1670void AaptDir::removeFile(const String8& name)
1671{
1672    mFiles.removeItem(name);
1673}
1674
1675void AaptDir::removeDir(const String8& name)
1676{
1677    mDirs.removeItem(name);
1678}
1679
1680status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1681{
1682    sp<AaptGroup> group;
1683    if (mFiles.indexOfKey(leafName) >= 0) {
1684        group = mFiles.valueFor(leafName);
1685    } else {
1686        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1687        mFiles.add(leafName, group);
1688    }
1689
1690    return group->addFile(file);
1691}
1692
1693ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1694                            const AaptGroupEntry& kind, const String8& resType,
1695                            sp<FilePathStore>& fullResPaths)
1696{
1697    Vector<String8> fileNames;
1698    {
1699        DIR* dir = NULL;
1700
1701        dir = opendir(srcDir.string());
1702        if (dir == NULL) {
1703            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1704            return UNKNOWN_ERROR;
1705        }
1706
1707        /*
1708         * Slurp the filenames out of the directory.
1709         */
1710        while (1) {
1711            struct dirent* entry;
1712
1713            entry = readdir(dir);
1714            if (entry == NULL)
1715                break;
1716
1717            if (isHidden(srcDir.string(), entry->d_name))
1718                continue;
1719
1720            String8 name(entry->d_name);
1721            fileNames.add(name);
1722            // Add fully qualified path for dependency purposes
1723            // if we're collecting them
1724            if (fullResPaths != NULL) {
1725                fullResPaths->add(srcDir.appendPathCopy(name));
1726            }
1727        }
1728        closedir(dir);
1729    }
1730
1731    ssize_t count = 0;
1732
1733    /*
1734     * Stash away the files and recursively descend into subdirectories.
1735     */
1736    const size_t N = fileNames.size();
1737    size_t i;
1738    for (i = 0; i < N; i++) {
1739        String8 pathName(srcDir);
1740        FileType type;
1741
1742        pathName.appendPath(fileNames[i].string());
1743        type = getFileType(pathName.string());
1744        if (type == kFileTypeDirectory) {
1745            sp<AaptDir> subdir;
1746            bool notAdded = false;
1747            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1748                subdir = mDirs.valueFor(fileNames[i]);
1749            } else {
1750                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1751                notAdded = true;
1752            }
1753            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1754                                                resType, fullResPaths);
1755            if (res < NO_ERROR) {
1756                return res;
1757            }
1758            if (res > 0 && notAdded) {
1759                mDirs.add(fileNames[i], subdir);
1760            }
1761            count += res;
1762        } else if (type == kFileTypeRegular) {
1763            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1764            status_t err = addLeafFile(fileNames[i], file);
1765            if (err != NO_ERROR) {
1766                return err;
1767            }
1768
1769            count++;
1770
1771        } else {
1772            if (bundle->getVerbose())
1773                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
1774        }
1775    }
1776
1777    return count;
1778}
1779
1780status_t AaptDir::validate() const
1781{
1782    const size_t NF = mFiles.size();
1783    const size_t ND = mDirs.size();
1784    size_t i;
1785    for (i = 0; i < NF; i++) {
1786        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1787            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1788                    "Invalid filename.  Unable to add.");
1789            return UNKNOWN_ERROR;
1790        }
1791
1792        size_t j;
1793        for (j = i+1; j < NF; j++) {
1794            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1795                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
1796                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1797                        "File is case-insensitive equivalent to: %s",
1798                        mFiles.valueAt(j)->getPrintableSource().string());
1799                return UNKNOWN_ERROR;
1800            }
1801
1802            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1803            // (this is mostly caught by the "marked" stuff, below)
1804        }
1805
1806        for (j = 0; j < ND; j++) {
1807            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1808                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1809                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1810                        "File conflicts with dir from: %s",
1811                        mDirs.valueAt(j)->getPrintableSource().string());
1812                return UNKNOWN_ERROR;
1813            }
1814        }
1815    }
1816
1817    for (i = 0; i < ND; i++) {
1818        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1819            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1820                    "Invalid directory name, unable to add.");
1821            return UNKNOWN_ERROR;
1822        }
1823
1824        size_t j;
1825        for (j = i+1; j < ND; j++) {
1826            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1827                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1828                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1829                        "Directory is case-insensitive equivalent to: %s",
1830                        mDirs.valueAt(j)->getPrintableSource().string());
1831                return UNKNOWN_ERROR;
1832            }
1833        }
1834
1835        status_t err = mDirs.valueAt(i)->validate();
1836        if (err != NO_ERROR) {
1837            return err;
1838        }
1839    }
1840
1841    return NO_ERROR;
1842}
1843
1844void AaptDir::print(const String8& prefix) const
1845{
1846    const size_t ND=getDirs().size();
1847    size_t i;
1848    for (i=0; i<ND; i++) {
1849        getDirs().valueAt(i)->print(prefix);
1850    }
1851
1852    const size_t NF=getFiles().size();
1853    for (i=0; i<NF; i++) {
1854        getFiles().valueAt(i)->print(prefix);
1855    }
1856}
1857
1858String8 AaptDir::getPrintableSource() const
1859{
1860    if (mFiles.size() > 0) {
1861        // Arbitrarily pull the first file out of the list as the source dir.
1862        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1863    }
1864    if (mDirs.size() > 0) {
1865        // Or arbitrarily pull the first dir out of the list as the source dir.
1866        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1867    }
1868
1869    // Should never hit this case, but to be safe...
1870    return mPath;
1871
1872}
1873
1874// =========================================================================
1875// =========================================================================
1876// =========================================================================
1877
1878status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
1879{
1880    status_t err = NO_ERROR;
1881    size_t N = javaSymbols->mSymbols.size();
1882    for (size_t i=0; i<N; i++) {
1883        const String8& name = javaSymbols->mSymbols.keyAt(i);
1884        const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
1885        ssize_t pos = mSymbols.indexOfKey(name);
1886        if (pos < 0) {
1887            entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
1888            err = UNKNOWN_ERROR;
1889            continue;
1890        }
1891        //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
1892        //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
1893        mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
1894    }
1895
1896    N = javaSymbols->mNestedSymbols.size();
1897    for (size_t i=0; i<N; i++) {
1898        const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
1899        const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
1900        ssize_t pos = mNestedSymbols.indexOfKey(name);
1901        if (pos < 0) {
1902            SourcePos pos;
1903            pos.error("Java symbol dir %s not defined\n", name.string());
1904            err = UNKNOWN_ERROR;
1905            continue;
1906        }
1907        //printf("**** applying java symbols in dir %s\n", name.string());
1908        status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1909        if (myerr != NO_ERROR) {
1910            err = myerr;
1911        }
1912    }
1913
1914    return err;
1915}
1916
1917// =========================================================================
1918// =========================================================================
1919// =========================================================================
1920
1921AaptAssets::AaptAssets()
1922    : AaptDir(String8(), String8()),
1923      mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
1924{
1925}
1926
1927const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
1928    if (mChanged) {
1929    }
1930    return mGroupEntries;
1931}
1932
1933status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
1934{
1935    mChanged = true;
1936    return AaptDir::addFile(name, file);
1937}
1938
1939sp<AaptFile> AaptAssets::addFile(
1940        const String8& filePath, const AaptGroupEntry& entry,
1941        const String8& srcDir, sp<AaptGroup>* outGroup,
1942        const String8& resType)
1943{
1944    sp<AaptDir> dir = this;
1945    sp<AaptGroup> group;
1946    sp<AaptFile> file;
1947    String8 root, remain(filePath), partialPath;
1948    while (remain.length() > 0) {
1949        root = remain.walkPath(&remain);
1950        partialPath.appendPath(root);
1951
1952        const String8 rootStr(root);
1953
1954        if (remain.length() == 0) {
1955            ssize_t i = dir->getFiles().indexOfKey(rootStr);
1956            if (i >= 0) {
1957                group = dir->getFiles().valueAt(i);
1958            } else {
1959                group = new AaptGroup(rootStr, filePath);
1960                status_t res = dir->addFile(rootStr, group);
1961                if (res != NO_ERROR) {
1962                    return NULL;
1963                }
1964            }
1965            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1966            status_t res = group->addFile(file);
1967            if (res != NO_ERROR) {
1968                return NULL;
1969            }
1970            break;
1971
1972        } else {
1973            ssize_t i = dir->getDirs().indexOfKey(rootStr);
1974            if (i >= 0) {
1975                dir = dir->getDirs().valueAt(i);
1976            } else {
1977                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1978                status_t res = dir->addDir(rootStr, subdir);
1979                if (res != NO_ERROR) {
1980                    return NULL;
1981                }
1982                dir = subdir;
1983            }
1984        }
1985    }
1986
1987    mGroupEntries.add(entry);
1988    if (outGroup) *outGroup = group;
1989    return file;
1990}
1991
1992void AaptAssets::addResource(const String8& leafName, const String8& path,
1993                const sp<AaptFile>& file, const String8& resType)
1994{
1995    sp<AaptDir> res = AaptDir::makeDir(kResString);
1996    String8 dirname = file->getGroupEntry().toDirName(resType);
1997    sp<AaptDir> subdir = res->makeDir(dirname);
1998    sp<AaptGroup> grr = new AaptGroup(leafName, path);
1999    grr->addFile(file);
2000
2001    subdir->addFile(leafName, grr);
2002}
2003
2004
2005ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2006{
2007    int count;
2008    int totalCount = 0;
2009    FileType type;
2010    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2011    const size_t dirCount =resDirs.size();
2012    sp<AaptAssets> current = this;
2013
2014    const int N = bundle->getFileSpecCount();
2015
2016    /*
2017     * If a package manifest was specified, include that first.
2018     */
2019    if (bundle->getAndroidManifestFile() != NULL) {
2020        // place at root of zip.
2021        String8 srcFile(bundle->getAndroidManifestFile());
2022        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2023                NULL, String8());
2024        totalCount++;
2025    }
2026
2027    /*
2028     * If a directory of custom assets was supplied, slurp 'em up.
2029     */
2030    if (bundle->getAssetSourceDir()) {
2031        const char* assetDir = bundle->getAssetSourceDir();
2032
2033        FileType type = getFileType(assetDir);
2034        if (type == kFileTypeNonexistent) {
2035            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
2036            return UNKNOWN_ERROR;
2037        }
2038        if (type != kFileTypeDirectory) {
2039            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2040            return UNKNOWN_ERROR;
2041        }
2042
2043        String8 assetRoot(assetDir);
2044        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2045        AaptGroupEntry group;
2046        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
2047                                            String8(), mFullAssetPaths);
2048        if (count < 0) {
2049            totalCount = count;
2050            goto bail;
2051        }
2052        if (count > 0) {
2053            mGroupEntries.add(group);
2054        }
2055        totalCount += count;
2056
2057        if (bundle->getVerbose())
2058            printf("Found %d custom asset file%s in %s\n",
2059                   count, (count==1) ? "" : "s", assetDir);
2060    }
2061
2062    /*
2063     * If a directory of resource-specific assets was supplied, slurp 'em up.
2064     */
2065    for (size_t i=0; i<dirCount; i++) {
2066        const char *res = resDirs[i];
2067        if (res) {
2068            type = getFileType(res);
2069            if (type == kFileTypeNonexistent) {
2070                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2071                return UNKNOWN_ERROR;
2072            }
2073            if (type == kFileTypeDirectory) {
2074                if (i>0) {
2075                    sp<AaptAssets> nextOverlay = new AaptAssets();
2076                    current->setOverlay(nextOverlay);
2077                    current = nextOverlay;
2078                    current->setFullResPaths(mFullResPaths);
2079                }
2080                count = current->slurpResourceTree(bundle, String8(res));
2081
2082                if (count < 0) {
2083                    totalCount = count;
2084                    goto bail;
2085                }
2086                totalCount += count;
2087            }
2088            else {
2089                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2090                return UNKNOWN_ERROR;
2091            }
2092        }
2093
2094    }
2095    /*
2096     * Now do any additional raw files.
2097     */
2098    for (int arg=0; arg<N; arg++) {
2099        const char* assetDir = bundle->getFileSpecEntry(arg);
2100
2101        FileType type = getFileType(assetDir);
2102        if (type == kFileTypeNonexistent) {
2103            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2104            return UNKNOWN_ERROR;
2105        }
2106        if (type != kFileTypeDirectory) {
2107            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2108            return UNKNOWN_ERROR;
2109        }
2110
2111        String8 assetRoot(assetDir);
2112
2113        if (bundle->getVerbose())
2114            printf("Processing raw dir '%s'\n", (const char*) assetDir);
2115
2116        /*
2117         * Do a recursive traversal of subdir tree.  We don't make any
2118         * guarantees about ordering, so we're okay with an inorder search
2119         * using whatever order the OS happens to hand back to us.
2120         */
2121        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2122        if (count < 0) {
2123            /* failure; report error and remove archive */
2124            totalCount = count;
2125            goto bail;
2126        }
2127        totalCount += count;
2128
2129        if (bundle->getVerbose())
2130            printf("Found %d asset file%s in %s\n",
2131                   count, (count==1) ? "" : "s", assetDir);
2132    }
2133
2134    count = validate();
2135    if (count != NO_ERROR) {
2136        totalCount = count;
2137        goto bail;
2138    }
2139
2140    count = filter(bundle);
2141    if (count != NO_ERROR) {
2142        totalCount = count;
2143        goto bail;
2144    }
2145
2146bail:
2147    return totalCount;
2148}
2149
2150ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2151                                    const AaptGroupEntry& kind,
2152                                    const String8& resType,
2153                                    sp<FilePathStore>& fullResPaths)
2154{
2155    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2156    if (res > 0) {
2157        mGroupEntries.add(kind);
2158    }
2159
2160    return res;
2161}
2162
2163ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2164{
2165    ssize_t err = 0;
2166
2167    DIR* dir = opendir(srcDir.string());
2168    if (dir == NULL) {
2169        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2170        return UNKNOWN_ERROR;
2171    }
2172
2173    status_t count = 0;
2174
2175    /*
2176     * Run through the directory, looking for dirs that match the
2177     * expected pattern.
2178     */
2179    while (1) {
2180        struct dirent* entry = readdir(dir);
2181        if (entry == NULL) {
2182            break;
2183        }
2184
2185        if (isHidden(srcDir.string(), entry->d_name)) {
2186            continue;
2187        }
2188
2189        String8 subdirName(srcDir);
2190        subdirName.appendPath(entry->d_name);
2191
2192        AaptGroupEntry group;
2193        String8 resType;
2194        bool b = group.initFromDirName(entry->d_name, &resType);
2195        if (!b) {
2196            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2197                    entry->d_name);
2198            err = -1;
2199            continue;
2200        }
2201
2202        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2203            int maxResInt = atoi(bundle->getMaxResVersion());
2204            const char *verString = group.getVersionString().string();
2205            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2206            if (dirVersionInt > maxResInt) {
2207              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2208              continue;
2209            }
2210        }
2211
2212        FileType type = getFileType(subdirName.string());
2213
2214        if (type == kFileTypeDirectory) {
2215            sp<AaptDir> dir = makeDir(resType);
2216            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2217                                                resType, mFullResPaths);
2218            if (res < 0) {
2219                count = res;
2220                goto bail;
2221            }
2222            if (res > 0) {
2223                mGroupEntries.add(group);
2224                count += res;
2225            }
2226
2227            // Only add this directory if we don't already have a resource dir
2228            // for the current type.  This ensures that we only add the dir once
2229            // for all configs.
2230            sp<AaptDir> rdir = resDir(resType);
2231            if (rdir == NULL) {
2232                mResDirs.add(dir);
2233            }
2234        } else {
2235            if (bundle->getVerbose()) {
2236                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2237            }
2238        }
2239    }
2240
2241bail:
2242    closedir(dir);
2243    dir = NULL;
2244
2245    if (err != 0) {
2246        return err;
2247    }
2248    return count;
2249}
2250
2251ssize_t
2252AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2253{
2254    int count = 0;
2255    SortedVector<AaptGroupEntry> entries;
2256
2257    ZipFile* zip = new ZipFile;
2258    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2259    if (err != NO_ERROR) {
2260        fprintf(stderr, "error opening zip file %s\n", filename);
2261        count = err;
2262        delete zip;
2263        return -1;
2264    }
2265
2266    const int N = zip->getNumEntries();
2267    for (int i=0; i<N; i++) {
2268        ZipEntry* entry = zip->getEntryByIndex(i);
2269        if (entry->getDeleted()) {
2270            continue;
2271        }
2272
2273        String8 entryName(entry->getFileName());
2274
2275        String8 dirName = entryName.getPathDir();
2276        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2277
2278        String8 resType;
2279        AaptGroupEntry kind;
2280
2281        String8 remain;
2282        if (entryName.walkPath(&remain) == kResourceDir) {
2283            // these are the resources, pull their type out of the directory name
2284            kind.initFromDirName(remain.walkPath().string(), &resType);
2285        } else {
2286            // these are untyped and don't have an AaptGroupEntry
2287        }
2288        if (entries.indexOf(kind) < 0) {
2289            entries.add(kind);
2290            mGroupEntries.add(kind);
2291        }
2292
2293        // use the one from the zip file if they both exist.
2294        dir->removeFile(entryName.getPathLeaf());
2295
2296        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2297        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2298        if (err != NO_ERROR) {
2299            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2300            count = err;
2301            goto bail;
2302        }
2303        file->setCompressionMethod(entry->getCompressionMethod());
2304
2305#if 0
2306        if (entryName == "AndroidManifest.xml") {
2307            printf("AndroidManifest.xml\n");
2308        }
2309        printf("\n\nfile: %s\n", entryName.string());
2310#endif
2311
2312        size_t len = entry->getUncompressedLen();
2313        void* data = zip->uncompress(entry);
2314        void* buf = file->editData(len);
2315        memcpy(buf, data, len);
2316
2317#if 0
2318        const int OFF = 0;
2319        const unsigned char* p = (unsigned char*)data;
2320        const unsigned char* end = p+len;
2321        p += OFF;
2322        for (int i=0; i<32 && p < end; i++) {
2323            printf("0x%03x ", i*0x10 + OFF);
2324            for (int j=0; j<0x10 && p < end; j++) {
2325                printf(" %02x", *p);
2326                p++;
2327            }
2328            printf("\n");
2329        }
2330#endif
2331
2332        free(data);
2333
2334        count++;
2335    }
2336
2337bail:
2338    delete zip;
2339    return count;
2340}
2341
2342status_t AaptAssets::filter(Bundle* bundle)
2343{
2344    ResourceFilter reqFilter;
2345    status_t err = reqFilter.parse(bundle->getConfigurations());
2346    if (err != NO_ERROR) {
2347        return err;
2348    }
2349
2350    ResourceFilter prefFilter;
2351    err = prefFilter.parse(bundle->getPreferredConfigurations());
2352    if (err != NO_ERROR) {
2353        return err;
2354    }
2355
2356    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2357        return NO_ERROR;
2358    }
2359
2360    if (bundle->getVerbose()) {
2361        if (!reqFilter.isEmpty()) {
2362            printf("Applying required filter: %s\n",
2363                    bundle->getConfigurations());
2364        }
2365        if (!prefFilter.isEmpty()) {
2366            printf("Applying preferred filter: %s\n",
2367                    bundle->getPreferredConfigurations());
2368        }
2369    }
2370
2371    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2372    const size_t ND = resdirs.size();
2373    for (size_t i=0; i<ND; i++) {
2374        const sp<AaptDir>& dir = resdirs.itemAt(i);
2375        if (dir->getLeaf() == kValuesDir) {
2376            // The "value" dir is special since a single file defines
2377            // multiple resources, so we can not do filtering on the
2378            // files themselves.
2379            continue;
2380        }
2381        if (dir->getLeaf() == kMipmapDir) {
2382            // We also skip the "mipmap" directory, since the point of this
2383            // is to include all densities without stripping.  If you put
2384            // other configurations in here as well they won't be stripped
2385            // either...  So don't do that.  Seriously.  What is wrong with you?
2386            continue;
2387        }
2388
2389        const size_t NG = dir->getFiles().size();
2390        for (size_t j=0; j<NG; j++) {
2391            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2392
2393            // First remove any configurations we know we don't need.
2394            for (size_t k=0; k<grp->getFiles().size(); k++) {
2395                sp<AaptFile> file = grp->getFiles().valueAt(k);
2396                if (k == 0 && grp->getFiles().size() == 1) {
2397                    // If this is the only file left, we need to keep it.
2398                    // Otherwise the resource IDs we are using will be inconsistent
2399                    // with what we get when not stripping.  Sucky, but at least
2400                    // for now we can rely on the back-end doing another filtering
2401                    // pass to take this out and leave us with this resource name
2402                    // containing no entries.
2403                    continue;
2404                }
2405                if (file->getPath().getPathExtension() == ".xml") {
2406                    // We can't remove .xml files at this point, because when
2407                    // we parse them they may add identifier resources, so
2408                    // removing them can cause our resource identifiers to
2409                    // become inconsistent.
2410                    continue;
2411                }
2412                const ResTable_config& config(file->getGroupEntry().toParams());
2413                if (!reqFilter.match(config)) {
2414                    if (bundle->getVerbose()) {
2415                        printf("Pruning unneeded resource: %s\n",
2416                                file->getPrintableSource().string());
2417                    }
2418                    grp->removeFile(k);
2419                    k--;
2420                }
2421            }
2422
2423            // Quick check: no preferred filters, nothing more to do.
2424            if (prefFilter.isEmpty()) {
2425                continue;
2426            }
2427
2428            // Now deal with preferred configurations.
2429            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2430                for (size_t k=0; k<grp->getFiles().size(); k++) {
2431                    sp<AaptFile> file = grp->getFiles().valueAt(k);
2432                    if (k == 0 && grp->getFiles().size() == 1) {
2433                        // If this is the only file left, we need to keep it.
2434                        // Otherwise the resource IDs we are using will be inconsistent
2435                        // with what we get when not stripping.  Sucky, but at least
2436                        // for now we can rely on the back-end doing another filtering
2437                        // pass to take this out and leave us with this resource name
2438                        // containing no entries.
2439                        continue;
2440                    }
2441                    if (file->getPath().getPathExtension() == ".xml") {
2442                        // We can't remove .xml files at this point, because when
2443                        // we parse them they may add identifier resources, so
2444                        // removing them can cause our resource identifiers to
2445                        // become inconsistent.
2446                        continue;
2447                    }
2448                    const ResTable_config& config(file->getGroupEntry().toParams());
2449                    if (!prefFilter.match(axis, config)) {
2450                        // This is a resource we would prefer not to have.  Check
2451                        // to see if have a similar variation that we would like
2452                        // to have and, if so, we can drop it.
2453                        for (size_t m=0; m<grp->getFiles().size(); m++) {
2454                            if (m == k) continue;
2455                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2456                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2457                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2458                                if (prefFilter.match(axis, mconfig)) {
2459                                    if (bundle->getVerbose()) {
2460                                        printf("Pruning unneeded resource: %s\n",
2461                                                file->getPrintableSource().string());
2462                                    }
2463                                    grp->removeFile(k);
2464                                    k--;
2465                                    break;
2466                                }
2467                            }
2468                        }
2469                    }
2470                }
2471            }
2472        }
2473    }
2474
2475    return NO_ERROR;
2476}
2477
2478sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2479{
2480    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2481    if (sym == NULL) {
2482        sym = new AaptSymbols();
2483        mSymbols.add(name, sym);
2484    }
2485    return sym;
2486}
2487
2488sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2489{
2490    sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2491    if (sym == NULL) {
2492        sym = new AaptSymbols();
2493        mJavaSymbols.add(name, sym);
2494    }
2495    return sym;
2496}
2497
2498status_t AaptAssets::applyJavaSymbols()
2499{
2500    size_t N = mJavaSymbols.size();
2501    for (size_t i=0; i<N; i++) {
2502        const String8& name = mJavaSymbols.keyAt(i);
2503        const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2504        ssize_t pos = mSymbols.indexOfKey(name);
2505        if (pos < 0) {
2506            SourcePos pos;
2507            pos.error("Java symbol dir %s not defined\n", name.string());
2508            return UNKNOWN_ERROR;
2509        }
2510        //printf("**** applying java symbols in dir %s\n", name.string());
2511        status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2512        if (err != NO_ERROR) {
2513            return err;
2514        }
2515    }
2516
2517    return NO_ERROR;
2518}
2519
2520bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2521    //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2522    //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2523    //        sym.isJavaSymbol ? 1 : 0);
2524    if (!mHavePrivateSymbols) return true;
2525    if (sym.isPublic) return true;
2526    if (includePrivate && sym.isJavaSymbol) return true;
2527    return false;
2528}
2529
2530status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2531{
2532    if (!mHaveIncludedAssets) {
2533        // Add in all includes.
2534        const Vector<const char*>& incl = bundle->getPackageIncludes();
2535        const size_t N=incl.size();
2536        for (size_t i=0; i<N; i++) {
2537            if (bundle->getVerbose())
2538                printf("Including resources from package: %s\n", incl[i]);
2539            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2540                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2541                        incl[i]);
2542                return UNKNOWN_ERROR;
2543            }
2544        }
2545        mHaveIncludedAssets = true;
2546    }
2547
2548    return NO_ERROR;
2549}
2550
2551status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2552{
2553    const ResTable& res = getIncludedResources();
2554    // XXX dirty!
2555    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2556}
2557
2558const ResTable& AaptAssets::getIncludedResources() const
2559{
2560    return mIncludedAssets.getResources(false);
2561}
2562
2563void AaptAssets::print(const String8& prefix) const
2564{
2565    String8 innerPrefix(prefix);
2566    innerPrefix.append("  ");
2567    String8 innerInnerPrefix(innerPrefix);
2568    innerInnerPrefix.append("  ");
2569    printf("%sConfigurations:\n", prefix.string());
2570    const size_t N=mGroupEntries.size();
2571    for (size_t i=0; i<N; i++) {
2572        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2573        printf("%s %s\n", prefix.string(),
2574                cname != "" ? cname.string() : "(default)");
2575    }
2576
2577    printf("\n%sFiles:\n", prefix.string());
2578    AaptDir::print(innerPrefix);
2579
2580    printf("\n%sResource Dirs:\n", prefix.string());
2581    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2582    const size_t NR = resdirs.size();
2583    for (size_t i=0; i<NR; i++) {
2584        const sp<AaptDir>& d = resdirs.itemAt(i);
2585        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
2586        d->print(innerInnerPrefix);
2587    }
2588}
2589
2590sp<AaptDir> AaptAssets::resDir(const String8& name) const
2591{
2592    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2593    const size_t N = resdirs.size();
2594    for (size_t i=0; i<N; i++) {
2595        const sp<AaptDir>& d = resdirs.itemAt(i);
2596        if (d->getLeaf() == name) {
2597            return d;
2598        }
2599    }
2600    return NULL;
2601}
2602
2603bool
2604valid_symbol_name(const String8& symbol)
2605{
2606    static char const * const KEYWORDS[] = {
2607        "abstract", "assert", "boolean", "break",
2608        "byte", "case", "catch", "char", "class", "const", "continue",
2609        "default", "do", "double", "else", "enum", "extends", "final",
2610        "finally", "float", "for", "goto", "if", "implements", "import",
2611        "instanceof", "int", "interface", "long", "native", "new", "package",
2612        "private", "protected", "public", "return", "short", "static",
2613        "strictfp", "super", "switch", "synchronized", "this", "throw",
2614        "throws", "transient", "try", "void", "volatile", "while",
2615        "true", "false", "null",
2616        NULL
2617    };
2618    const char*const* k = KEYWORDS;
2619    const char*const s = symbol.string();
2620    while (*k) {
2621        if (0 == strcmp(s, *k)) {
2622            return false;
2623        }
2624        k++;
2625    }
2626    return true;
2627}
2628