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