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