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