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