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(¶ms, 0, sizeof(params));
1508 getMccName(mcc.string(), ¶ms);
1509 getMncName(mnc.string(), ¶ms);
1510 getLocaleName(locale.string(), ¶ms);
1511 getLayoutDirectionName(layoutDirection.string(), ¶ms);
1512 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms);
1513 getScreenWidthDpName(screenWidthDp.string(), ¶ms);
1514 getScreenHeightDpName(screenHeightDp.string(), ¶ms);
1515 getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms);
1516 getScreenLayoutLongName(screenLayoutLong.string(), ¶ms);
1517 getOrientationName(orientation.string(), ¶ms);
1518 getUiModeTypeName(uiModeType.string(), ¶ms);
1519 getUiModeNightName(uiModeNight.string(), ¶ms);
1520 getDensityName(density.string(), ¶ms);
1521 getTouchscreenName(touchscreen.string(), ¶ms);
1522 getKeysHiddenName(keysHidden.string(), ¶ms);
1523 getKeyboardName(keyboard.string(), ¶ms);
1524 getNavHiddenName(navHidden.string(), ¶ms);
1525 getNavigationName(navigation.string(), ¶ms);
1526 getScreenSizeName(screenSize.string(), ¶ms);
1527 getVersionName(version.string(), ¶ms);
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