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