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