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