AaptAssets.cpp revision d96e3dfa02b203b1fc826e80d6f9aa074ba9c250
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
1840AaptAssets::AaptAssets()
1841    : AaptDir(String8(), String8()),
1842      mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
1843{
1844}
1845
1846const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
1847    if (mChanged) {
1848    }
1849    return mGroupEntries;
1850}
1851
1852status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
1853{
1854    mChanged = true;
1855    return AaptDir::addFile(name, file);
1856}
1857
1858sp<AaptFile> AaptAssets::addFile(
1859        const String8& filePath, const AaptGroupEntry& entry,
1860        const String8& srcDir, sp<AaptGroup>* outGroup,
1861        const String8& resType)
1862{
1863    sp<AaptDir> dir = this;
1864    sp<AaptGroup> group;
1865    sp<AaptFile> file;
1866    String8 root, remain(filePath), partialPath;
1867    while (remain.length() > 0) {
1868        root = remain.walkPath(&remain);
1869        partialPath.appendPath(root);
1870
1871        const String8 rootStr(root);
1872
1873        if (remain.length() == 0) {
1874            ssize_t i = dir->getFiles().indexOfKey(rootStr);
1875            if (i >= 0) {
1876                group = dir->getFiles().valueAt(i);
1877            } else {
1878                group = new AaptGroup(rootStr, filePath);
1879                status_t res = dir->addFile(rootStr, group);
1880                if (res != NO_ERROR) {
1881                    return NULL;
1882                }
1883            }
1884            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1885            status_t res = group->addFile(file);
1886            if (res != NO_ERROR) {
1887                return NULL;
1888            }
1889            break;
1890
1891        } else {
1892            ssize_t i = dir->getDirs().indexOfKey(rootStr);
1893            if (i >= 0) {
1894                dir = dir->getDirs().valueAt(i);
1895            } else {
1896                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1897                status_t res = dir->addDir(rootStr, subdir);
1898                if (res != NO_ERROR) {
1899                    return NULL;
1900                }
1901                dir = subdir;
1902            }
1903        }
1904    }
1905
1906    mGroupEntries.add(entry);
1907    if (outGroup) *outGroup = group;
1908    return file;
1909}
1910
1911void AaptAssets::addResource(const String8& leafName, const String8& path,
1912                const sp<AaptFile>& file, const String8& resType)
1913{
1914    sp<AaptDir> res = AaptDir::makeDir(kResString);
1915    String8 dirname = file->getGroupEntry().toDirName(resType);
1916    sp<AaptDir> subdir = res->makeDir(dirname);
1917    sp<AaptGroup> grr = new AaptGroup(leafName, path);
1918    grr->addFile(file);
1919
1920    subdir->addFile(leafName, grr);
1921}
1922
1923
1924ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1925{
1926    int count;
1927    int totalCount = 0;
1928    FileType type;
1929    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1930    const size_t dirCount =resDirs.size();
1931    sp<AaptAssets> current = this;
1932
1933    const int N = bundle->getFileSpecCount();
1934
1935    /*
1936     * If a package manifest was specified, include that first.
1937     */
1938    if (bundle->getAndroidManifestFile() != NULL) {
1939        // place at root of zip.
1940        String8 srcFile(bundle->getAndroidManifestFile());
1941        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1942                NULL, String8());
1943        totalCount++;
1944    }
1945
1946    /*
1947     * If a directory of custom assets was supplied, slurp 'em up.
1948     */
1949    if (bundle->getAssetSourceDir()) {
1950        const char* assetDir = bundle->getAssetSourceDir();
1951
1952        FileType type = getFileType(assetDir);
1953        if (type == kFileTypeNonexistent) {
1954            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1955            return UNKNOWN_ERROR;
1956        }
1957        if (type != kFileTypeDirectory) {
1958            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1959            return UNKNOWN_ERROR;
1960        }
1961
1962        String8 assetRoot(assetDir);
1963        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1964        AaptGroupEntry group;
1965        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1966                                            String8(), mFullAssetPaths);
1967        if (count < 0) {
1968            totalCount = count;
1969            goto bail;
1970        }
1971        if (count > 0) {
1972            mGroupEntries.add(group);
1973        }
1974        totalCount += count;
1975
1976        if (bundle->getVerbose())
1977            printf("Found %d custom asset file%s in %s\n",
1978                   count, (count==1) ? "" : "s", assetDir);
1979    }
1980
1981    /*
1982     * If a directory of resource-specific assets was supplied, slurp 'em up.
1983     */
1984    for (size_t i=0; i<dirCount; i++) {
1985        const char *res = resDirs[i];
1986        if (res) {
1987            type = getFileType(res);
1988            if (type == kFileTypeNonexistent) {
1989                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1990                return UNKNOWN_ERROR;
1991            }
1992            if (type == kFileTypeDirectory) {
1993                if (i>0) {
1994                    sp<AaptAssets> nextOverlay = new AaptAssets();
1995                    current->setOverlay(nextOverlay);
1996                    current = nextOverlay;
1997                    current->setFullResPaths(mFullResPaths);
1998                }
1999                count = current->slurpResourceTree(bundle, String8(res));
2000
2001                if (count < 0) {
2002                    totalCount = count;
2003                    goto bail;
2004                }
2005                totalCount += count;
2006            }
2007            else {
2008                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2009                return UNKNOWN_ERROR;
2010            }
2011        }
2012
2013    }
2014    /*
2015     * Now do any additional raw files.
2016     */
2017    for (int arg=0; arg<N; arg++) {
2018        const char* assetDir = bundle->getFileSpecEntry(arg);
2019
2020        FileType type = getFileType(assetDir);
2021        if (type == kFileTypeNonexistent) {
2022            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2023            return UNKNOWN_ERROR;
2024        }
2025        if (type != kFileTypeDirectory) {
2026            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2027            return UNKNOWN_ERROR;
2028        }
2029
2030        String8 assetRoot(assetDir);
2031
2032        if (bundle->getVerbose())
2033            printf("Processing raw dir '%s'\n", (const char*) assetDir);
2034
2035        /*
2036         * Do a recursive traversal of subdir tree.  We don't make any
2037         * guarantees about ordering, so we're okay with an inorder search
2038         * using whatever order the OS happens to hand back to us.
2039         */
2040        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2041        if (count < 0) {
2042            /* failure; report error and remove archive */
2043            totalCount = count;
2044            goto bail;
2045        }
2046        totalCount += count;
2047
2048        if (bundle->getVerbose())
2049            printf("Found %d asset file%s in %s\n",
2050                   count, (count==1) ? "" : "s", assetDir);
2051    }
2052
2053    count = validate();
2054    if (count != NO_ERROR) {
2055        totalCount = count;
2056        goto bail;
2057    }
2058
2059    count = filter(bundle);
2060    if (count != NO_ERROR) {
2061        totalCount = count;
2062        goto bail;
2063    }
2064
2065bail:
2066    return totalCount;
2067}
2068
2069ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2070                                    const AaptGroupEntry& kind,
2071                                    const String8& resType,
2072                                    sp<FilePathStore>& fullResPaths)
2073{
2074    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2075    if (res > 0) {
2076        mGroupEntries.add(kind);
2077    }
2078
2079    return res;
2080}
2081
2082ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2083{
2084    ssize_t err = 0;
2085
2086    DIR* dir = opendir(srcDir.string());
2087    if (dir == NULL) {
2088        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2089        return UNKNOWN_ERROR;
2090    }
2091
2092    status_t count = 0;
2093
2094    /*
2095     * Run through the directory, looking for dirs that match the
2096     * expected pattern.
2097     */
2098    while (1) {
2099        struct dirent* entry = readdir(dir);
2100        if (entry == NULL) {
2101            break;
2102        }
2103
2104        if (isHidden(srcDir.string(), entry->d_name)) {
2105            continue;
2106        }
2107
2108        String8 subdirName(srcDir);
2109        subdirName.appendPath(entry->d_name);
2110
2111        AaptGroupEntry group;
2112        String8 resType;
2113        bool b = group.initFromDirName(entry->d_name, &resType);
2114        if (!b) {
2115            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2116                    entry->d_name);
2117            err = -1;
2118            continue;
2119        }
2120
2121        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2122            int maxResInt = atoi(bundle->getMaxResVersion());
2123            const char *verString = group.getVersionString().string();
2124            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2125            if (dirVersionInt > maxResInt) {
2126              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2127              continue;
2128            }
2129        }
2130
2131        FileType type = getFileType(subdirName.string());
2132
2133        if (type == kFileTypeDirectory) {
2134            sp<AaptDir> dir = makeDir(resType);
2135            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2136                                                resType, mFullResPaths);
2137            if (res < 0) {
2138                count = res;
2139                goto bail;
2140            }
2141            if (res > 0) {
2142                mGroupEntries.add(group);
2143                count += res;
2144            }
2145
2146            // Only add this directory if we don't already have a resource dir
2147            // for the current type.  This ensures that we only add the dir once
2148            // for all configs.
2149            sp<AaptDir> rdir = resDir(resType);
2150            if (rdir == NULL) {
2151                mResDirs.add(dir);
2152            }
2153        } else {
2154            if (bundle->getVerbose()) {
2155                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2156            }
2157        }
2158    }
2159
2160bail:
2161    closedir(dir);
2162    dir = NULL;
2163
2164    if (err != 0) {
2165        return err;
2166    }
2167    return count;
2168}
2169
2170ssize_t
2171AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2172{
2173    int count = 0;
2174    SortedVector<AaptGroupEntry> entries;
2175
2176    ZipFile* zip = new ZipFile;
2177    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2178    if (err != NO_ERROR) {
2179        fprintf(stderr, "error opening zip file %s\n", filename);
2180        count = err;
2181        delete zip;
2182        return -1;
2183    }
2184
2185    const int N = zip->getNumEntries();
2186    for (int i=0; i<N; i++) {
2187        ZipEntry* entry = zip->getEntryByIndex(i);
2188        if (entry->getDeleted()) {
2189            continue;
2190        }
2191
2192        String8 entryName(entry->getFileName());
2193
2194        String8 dirName = entryName.getPathDir();
2195        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2196
2197        String8 resType;
2198        AaptGroupEntry kind;
2199
2200        String8 remain;
2201        if (entryName.walkPath(&remain) == kResourceDir) {
2202            // these are the resources, pull their type out of the directory name
2203            kind.initFromDirName(remain.walkPath().string(), &resType);
2204        } else {
2205            // these are untyped and don't have an AaptGroupEntry
2206        }
2207        if (entries.indexOf(kind) < 0) {
2208            entries.add(kind);
2209            mGroupEntries.add(kind);
2210        }
2211
2212        // use the one from the zip file if they both exist.
2213        dir->removeFile(entryName.getPathLeaf());
2214
2215        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2216        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2217        if (err != NO_ERROR) {
2218            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2219            count = err;
2220            goto bail;
2221        }
2222        file->setCompressionMethod(entry->getCompressionMethod());
2223
2224#if 0
2225        if (entryName == "AndroidManifest.xml") {
2226            printf("AndroidManifest.xml\n");
2227        }
2228        printf("\n\nfile: %s\n", entryName.string());
2229#endif
2230
2231        size_t len = entry->getUncompressedLen();
2232        void* data = zip->uncompress(entry);
2233        void* buf = file->editData(len);
2234        memcpy(buf, data, len);
2235
2236#if 0
2237        const int OFF = 0;
2238        const unsigned char* p = (unsigned char*)data;
2239        const unsigned char* end = p+len;
2240        p += OFF;
2241        for (int i=0; i<32 && p < end; i++) {
2242            printf("0x%03x ", i*0x10 + OFF);
2243            for (int j=0; j<0x10 && p < end; j++) {
2244                printf(" %02x", *p);
2245                p++;
2246            }
2247            printf("\n");
2248        }
2249#endif
2250
2251        free(data);
2252
2253        count++;
2254    }
2255
2256bail:
2257    delete zip;
2258    return count;
2259}
2260
2261status_t AaptAssets::filter(Bundle* bundle)
2262{
2263    ResourceFilter reqFilter;
2264    status_t err = reqFilter.parse(bundle->getConfigurations());
2265    if (err != NO_ERROR) {
2266        return err;
2267    }
2268
2269    ResourceFilter prefFilter;
2270    err = prefFilter.parse(bundle->getPreferredConfigurations());
2271    if (err != NO_ERROR) {
2272        return err;
2273    }
2274
2275    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2276        return NO_ERROR;
2277    }
2278
2279    if (bundle->getVerbose()) {
2280        if (!reqFilter.isEmpty()) {
2281            printf("Applying required filter: %s\n",
2282                    bundle->getConfigurations());
2283        }
2284        if (!prefFilter.isEmpty()) {
2285            printf("Applying preferred filter: %s\n",
2286                    bundle->getPreferredConfigurations());
2287        }
2288    }
2289
2290    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2291    const size_t ND = resdirs.size();
2292    for (size_t i=0; i<ND; i++) {
2293        const sp<AaptDir>& dir = resdirs.itemAt(i);
2294        if (dir->getLeaf() == kValuesDir) {
2295            // The "value" dir is special since a single file defines
2296            // multiple resources, so we can not do filtering on the
2297            // files themselves.
2298            continue;
2299        }
2300        if (dir->getLeaf() == kMipmapDir) {
2301            // We also skip the "mipmap" directory, since the point of this
2302            // is to include all densities without stripping.  If you put
2303            // other configurations in here as well they won't be stripped
2304            // either...  So don't do that.  Seriously.  What is wrong with you?
2305            continue;
2306        }
2307
2308        const size_t NG = dir->getFiles().size();
2309        for (size_t j=0; j<NG; j++) {
2310            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2311
2312            // First remove any configurations we know we don't need.
2313            for (size_t k=0; k<grp->getFiles().size(); k++) {
2314                sp<AaptFile> file = grp->getFiles().valueAt(k);
2315                if (k == 0 && grp->getFiles().size() == 1) {
2316                    // If this is the only file left, we need to keep it.
2317                    // Otherwise the resource IDs we are using will be inconsistent
2318                    // with what we get when not stripping.  Sucky, but at least
2319                    // for now we can rely on the back-end doing another filtering
2320                    // pass to take this out and leave us with this resource name
2321                    // containing no entries.
2322                    continue;
2323                }
2324                if (file->getPath().getPathExtension() == ".xml") {
2325                    // We can't remove .xml files at this point, because when
2326                    // we parse them they may add identifier resources, so
2327                    // removing them can cause our resource identifiers to
2328                    // become inconsistent.
2329                    continue;
2330                }
2331                const ResTable_config& config(file->getGroupEntry().toParams());
2332                if (!reqFilter.match(config)) {
2333                    if (bundle->getVerbose()) {
2334                        printf("Pruning unneeded resource: %s\n",
2335                                file->getPrintableSource().string());
2336                    }
2337                    grp->removeFile(k);
2338                    k--;
2339                }
2340            }
2341
2342            // Quick check: no preferred filters, nothing more to do.
2343            if (prefFilter.isEmpty()) {
2344                continue;
2345            }
2346
2347            // Now deal with preferred configurations.
2348            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2349                for (size_t k=0; k<grp->getFiles().size(); k++) {
2350                    sp<AaptFile> file = grp->getFiles().valueAt(k);
2351                    if (k == 0 && grp->getFiles().size() == 1) {
2352                        // If this is the only file left, we need to keep it.
2353                        // Otherwise the resource IDs we are using will be inconsistent
2354                        // with what we get when not stripping.  Sucky, but at least
2355                        // for now we can rely on the back-end doing another filtering
2356                        // pass to take this out and leave us with this resource name
2357                        // containing no entries.
2358                        continue;
2359                    }
2360                    if (file->getPath().getPathExtension() == ".xml") {
2361                        // We can't remove .xml files at this point, because when
2362                        // we parse them they may add identifier resources, so
2363                        // removing them can cause our resource identifiers to
2364                        // become inconsistent.
2365                        continue;
2366                    }
2367                    const ResTable_config& config(file->getGroupEntry().toParams());
2368                    if (!prefFilter.match(axis, config)) {
2369                        // This is a resource we would prefer not to have.  Check
2370                        // to see if have a similar variation that we would like
2371                        // to have and, if so, we can drop it.
2372                        for (size_t m=0; m<grp->getFiles().size(); m++) {
2373                            if (m == k) continue;
2374                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2375                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2376                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2377                                if (prefFilter.match(axis, mconfig)) {
2378                                    if (bundle->getVerbose()) {
2379                                        printf("Pruning unneeded resource: %s\n",
2380                                                file->getPrintableSource().string());
2381                                    }
2382                                    grp->removeFile(k);
2383                                    k--;
2384                                    break;
2385                                }
2386                            }
2387                        }
2388                    }
2389                }
2390            }
2391        }
2392    }
2393
2394    return NO_ERROR;
2395}
2396
2397sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2398{
2399    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2400    if (sym == NULL) {
2401        sym = new AaptSymbols();
2402        mSymbols.add(name, sym);
2403    }
2404    return sym;
2405}
2406
2407status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2408{
2409    if (!mHaveIncludedAssets) {
2410        // Add in all includes.
2411        const Vector<const char*>& incl = bundle->getPackageIncludes();
2412        const size_t N=incl.size();
2413        for (size_t i=0; i<N; i++) {
2414            if (bundle->getVerbose())
2415                printf("Including resources from package: %s\n", incl[i]);
2416            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2417                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2418                        incl[i]);
2419                return UNKNOWN_ERROR;
2420            }
2421        }
2422        mHaveIncludedAssets = true;
2423    }
2424
2425    return NO_ERROR;
2426}
2427
2428status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2429{
2430    const ResTable& res = getIncludedResources();
2431    // XXX dirty!
2432    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2433}
2434
2435const ResTable& AaptAssets::getIncludedResources() const
2436{
2437    return mIncludedAssets.getResources(false);
2438}
2439
2440void AaptAssets::print(const String8& prefix) const
2441{
2442    String8 innerPrefix(prefix);
2443    innerPrefix.append("  ");
2444    String8 innerInnerPrefix(innerPrefix);
2445    innerInnerPrefix.append("  ");
2446    printf("%sConfigurations:\n", prefix.string());
2447    const size_t N=mGroupEntries.size();
2448    for (size_t i=0; i<N; i++) {
2449        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2450        printf("%s %s\n", prefix.string(),
2451                cname != "" ? cname.string() : "(default)");
2452    }
2453
2454    printf("\n%sFiles:\n", prefix.string());
2455    AaptDir::print(innerPrefix);
2456
2457    printf("\n%sResource Dirs:\n", prefix.string());
2458    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2459    const size_t NR = resdirs.size();
2460    for (size_t i=0; i<NR; i++) {
2461        const sp<AaptDir>& d = resdirs.itemAt(i);
2462        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
2463        d->print(innerInnerPrefix);
2464    }
2465}
2466
2467sp<AaptDir> AaptAssets::resDir(const String8& name) const
2468{
2469    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2470    const size_t N = resdirs.size();
2471    for (size_t i=0; i<N; i++) {
2472        const sp<AaptDir>& d = resdirs.itemAt(i);
2473        if (d->getLeaf() == name) {
2474            return d;
2475        }
2476    }
2477    return NULL;
2478}
2479
2480bool
2481valid_symbol_name(const String8& symbol)
2482{
2483    static char const * const KEYWORDS[] = {
2484        "abstract", "assert", "boolean", "break",
2485        "byte", "case", "catch", "char", "class", "const", "continue",
2486        "default", "do", "double", "else", "enum", "extends", "final",
2487        "finally", "float", "for", "goto", "if", "implements", "import",
2488        "instanceof", "int", "interface", "long", "native", "new", "package",
2489        "private", "protected", "public", "return", "short", "static",
2490        "strictfp", "super", "switch", "synchronized", "this", "throw",
2491        "throws", "transient", "try", "void", "volatile", "while",
2492        "true", "false", "null",
2493        NULL
2494    };
2495    const char*const* k = KEYWORDS;
2496    const char*const s = symbol.string();
2497    while (*k) {
2498        if (0 == strcmp(s, *k)) {
2499            return false;
2500        }
2501        k++;
2502    }
2503    return true;
2504}
2505