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