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