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