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