2 // Copyright 2006 The Android Open Source Project
5 #include "AaptAssets.h"
6 #include "ResourceFilter.h"
9 #include <utils/misc.h>
10 #include <utils/SortedVector.h>
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;
25 static const String8
kResString(kResourceDir
);
28 * Names of asset files must meet the following criteria:
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 { '/' '\\' ':' }
35 * Pass in just the filename, not the full path.
37 static bool validateFileName(const char* fileName
)
39 const char* cp
= fileName
;
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
53 if (len
< 1 || len
> kMaxAssetFileName
)
54 return false; // reject empty or too long
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
;
65 static bool isHidden(const char *root
, const char *path
)
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.
80 if (strcmp(path
, ".") == 0 || strcmp(path
, "..") == 0) {
84 const char *delim
= ":";
85 const char *p
= gUserIgnoreAssets
;
87 p
= getenv("ANDROID_AAPT_IGNORE");
90 p
= gDefaultIgnoreAssets
;
92 char *patterns
= strdup(p
);
96 char *matchedPattern
= NULL
;
98 String8
fullPath(root
);
99 fullPath
.appendPath(path
);
100 FileType type
= getFileType(fullPath
);
102 int plen
= strlen(path
);
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;
114 if (strncasecmp(token
, "<file>", 6) == 0) {
115 if (type
!= kFileTypeRegular
) continue;
119 matchedPattern
= token
;
120 int n
= strlen(token
);
122 if (token
[0] == '*') {
126 ignore
= strncasecmp(token
, path
+ plen
- n
, n
) == 0;
128 } else if (n
> 1 && token
[n
- 1] == '*') {
130 ignore
= strncasecmp(token
, path
, n
- 1) == 0;
132 ignore
= strcasecmp(token
, path
) == 0;
136 if (ignore
&& chatty
) {
137 fprintf(stderr
, " (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
138 type
== kFileTypeDirectory
? "dir" : "file",
140 matchedPattern
? matchedPattern
: "");
147 // =========================================================================
148 // =========================================================================
149 // =========================================================================
152 AaptGroupEntry::parseNamePart(const String8
& part
, int* axis
, uint32_t* value
)
154 ResTable_config config
;
157 if (getMccName(part
.string(), &config
)) {
164 if (getMncName(part
.string(), &config
)) {
171 if (part
.length() == 2 && isalpha(part
[0]) && isalpha(part
[1])) {
172 *axis
= AXIS_LANGUAGE
;
173 *value
= part
[1] << 8 | part
[0];
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]);
185 // smallest screen dp width
186 if (getSmallestScreenWidthDpName(part
.string(), &config
)) {
187 *axis
= AXIS_SMALLESTSCREENWIDTHDP
;
188 *value
= config
.smallestScreenWidthDp
;
193 if (getScreenWidthDpName(part
.string(), &config
)) {
194 *axis
= AXIS_SCREENWIDTHDP
;
195 *value
= config
.screenWidthDp
;
200 if (getScreenHeightDpName(part
.string(), &config
)) {
201 *axis
= AXIS_SCREENHEIGHTDP
;
202 *value
= config
.screenHeightDp
;
206 // screen layout size
207 if (getScreenLayoutSizeName(part
.string(), &config
)) {
208 *axis
= AXIS_SCREENLAYOUTSIZE
;
209 *value
= (config
.screenLayout
&ResTable_config::MASK_SCREENSIZE
);
213 // screen layout long
214 if (getScreenLayoutLongName(part
.string(), &config
)) {
215 *axis
= AXIS_SCREENLAYOUTLONG
;
216 *value
= (config
.screenLayout
&ResTable_config::MASK_SCREENLONG
);
221 if (getOrientationName(part
.string(), &config
)) {
222 *axis
= AXIS_ORIENTATION
;
223 *value
= config
.orientation
;
228 if (getUiModeTypeName(part
.string(), &config
)) {
229 *axis
= AXIS_UIMODETYPE
;
230 *value
= (config
.uiMode
&ResTable_config::MASK_UI_MODE_TYPE
);
235 if (getUiModeNightName(part
.string(), &config
)) {
236 *axis
= AXIS_UIMODENIGHT
;
237 *value
= (config
.uiMode
&ResTable_config::MASK_UI_MODE_NIGHT
);
242 if (getDensityName(part
.string(), &config
)) {
243 *axis
= AXIS_DENSITY
;
244 *value
= config
.density
;
249 if (getTouchscreenName(part
.string(), &config
)) {
250 *axis
= AXIS_TOUCHSCREEN
;
251 *value
= config
.touchscreen
;
256 if (getKeysHiddenName(part
.string(), &config
)) {
257 *axis
= AXIS_KEYSHIDDEN
;
258 *value
= config
.inputFlags
;
263 if (getKeyboardName(part
.string(), &config
)) {
264 *axis
= AXIS_KEYBOARD
;
265 *value
= config
.keyboard
;
270 if (getNavHiddenName(part
.string(), &config
)) {
271 *axis
= AXIS_NAVHIDDEN
;
272 *value
= config
.inputFlags
;
277 if (getNavigationName(part
.string(), &config
)) {
278 *axis
= AXIS_NAVIGATION
;
279 *value
= config
.navigation
;
284 if (getScreenSizeName(part
.string(), &config
)) {
285 *axis
= AXIS_SCREENSIZE
;
286 *value
= config
.screenSize
;
291 if (getVersionName(part
.string(), &config
)) {
292 *axis
= AXIS_VERSION
;
293 *value
= config
.version
;
301 AaptGroupEntry::getConfigValueForAxis(const ResTable_config
& config
, int axis
)
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
);
320 return config
.density
;
321 case AXIS_TOUCHSCREEN
:
322 return config
.touchscreen
;
323 case AXIS_KEYSHIDDEN
:
324 return config
.inputFlags
;
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
;
338 return config
.version
;
344 AaptGroupEntry::configSameExcept(const ResTable_config
& config
,
345 const ResTable_config
& otherConfig
, int axis
)
347 for (int i
=AXIS_START
; i
<=AXIS_END
; i
++) {
351 if (getConfigValueForAxis(config
, i
) != getConfigValueForAxis(otherConfig
, i
)) {
359 AaptGroupEntry::initFromDirName(const char* dir
, String8
* resType
)
361 mParamsChanged
= true;
363 Vector
<String8
> parts
;
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
;
371 while (NULL
!= (q
= strchr(p
, '-'))) {
375 //printf("part: %s\n", parts[parts.size()-1].string());
381 //printf("part: %s\n", parts[parts.size()-1].string());
383 const int N
= parts
.size();
385 String8 part
= parts
[index
];
388 if (!isValidResourceType(part
)) {
400 if (getMccName(part
.string())) {
409 //printf("not mcc: %s\n", part.string());
413 if (getMncName(part
.string())) {
422 //printf("not mcc: %s\n", part.string());
426 if (part
.length() == 2 && isalpha(part
[0]) && isalpha(part
[1])) {
435 //printf("not language: %s\n", part.string());
440 && part
.length() == 3 && part
[0] == 'r' && part
[0] && part
[1]) {
443 loc
+= part
.string() + 1;
451 //printf("not region: %s\n", part.string());
454 if (getSmallestScreenWidthDpName(part
.string())) {
455 smallestwidthdp
= part
;
463 //printf("not smallest screen width dp: %s\n", part.string());
466 if (getScreenWidthDpName(part
.string())) {
475 //printf("not screen width dp: %s\n", part.string());
478 if (getScreenHeightDpName(part
.string())) {
487 //printf("not screen height dp: %s\n", part.string());
490 if (getScreenLayoutSizeName(part
.string())) {
499 //printf("not screen layout size: %s\n", part.string());
502 if (getScreenLayoutLongName(part
.string())) {
511 //printf("not screen layout long: %s\n", part.string());
515 if (getOrientationName(part
.string())) {
524 //printf("not orientation: %s\n", part.string());
528 if (getUiModeTypeName(part
.string())) {
537 //printf("not ui mode type: %s\n", part.string());
541 if (getUiModeNightName(part
.string())) {
550 //printf("not ui mode night: %s\n", part.string());
554 if (getDensityName(part
.string())) {
563 //printf("not density: %s\n", part.string());
567 if (getTouchscreenName(part
.string())) {
576 //printf("not touchscreen: %s\n", part.string());
580 if (getKeysHiddenName(part
.string())) {
589 //printf("not keysHidden: %s\n", part.string());
593 if (getKeyboardName(part
.string())) {
602 //printf("not keyboard: %s\n", part.string());
606 if (getNavHiddenName(part
.string())) {
615 //printf("not navHidden: %s\n", part.string());
618 if (getNavigationName(part
.string())) {
627 //printf("not navigation: %s\n", part.string());
630 if (getScreenSizeName(part
.string())) {
639 //printf("not screen size: %s\n", part.string());
642 if (getVersionName(part
.string())) {
651 //printf("not version: %s\n", part.string());
654 // if there are extra parts, it doesn't match
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
;
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
;
678 // what is this anyway?
685 AaptGroupEntry::toString() const
687 String8 s
= this->mcc
;
693 s
+= smallestScreenWidthDp
;
699 s
+= screenLayoutSize
;
701 s
+= screenLayoutLong
;
703 s
+= this->orientation
;
728 AaptGroupEntry::toDirName(const String8
& resType
) const
731 if (this->mcc
!= "") {
732 if (s
.length() > 0) {
737 if (this->mnc
!= "") {
738 if (s
.length() > 0) {
743 if (this->locale
!= "") {
744 if (s
.length() > 0) {
749 if (this->smallestScreenWidthDp
!= "") {
750 if (s
.length() > 0) {
753 s
+= smallestScreenWidthDp
;
755 if (this->screenWidthDp
!= "") {
756 if (s
.length() > 0) {
761 if (this->screenHeightDp
!= "") {
762 if (s
.length() > 0) {
767 if (this->screenLayoutSize
!= "") {
768 if (s
.length() > 0) {
771 s
+= screenLayoutSize
;
773 if (this->screenLayoutLong
!= "") {
774 if (s
.length() > 0) {
777 s
+= screenLayoutLong
;
779 if (this->orientation
!= "") {
780 if (s
.length() > 0) {
785 if (this->uiModeType
!= "") {
786 if (s
.length() > 0) {
791 if (this->uiModeNight
!= "") {
792 if (s
.length() > 0) {
797 if (this->density
!= "") {
798 if (s
.length() > 0) {
803 if (this->touchscreen
!= "") {
804 if (s
.length() > 0) {
809 if (this->keysHidden
!= "") {
810 if (s
.length() > 0) {
815 if (this->keyboard
!= "") {
816 if (s
.length() > 0) {
821 if (this->navHidden
!= "") {
822 if (s
.length() > 0) {
827 if (this->navigation
!= "") {
828 if (s
.length() > 0) {
833 if (this->screenSize
!= "") {
834 if (s
.length() > 0) {
839 if (this->version
!= "") {
840 if (s
.length() > 0) {
849 bool AaptGroupEntry::getMccName(const char* name
,
850 ResTable_config
* out
)
852 if (strcmp(name
, kWildcardName
) == 0) {
853 if (out
) out
->mcc
= 0;
856 const char* c
= name
;
857 if (tolower(*c
) != 'm') return false;
859 if (tolower(*c
) != 'c') return false;
861 if (tolower(*c
) != 'c') return false;
866 while (*c
>= '0' && *c
<= '9') {
869 if (*c
!= 0) return false;
870 if (c
-val
!= 3) return false;
874 if (out
) out
->mcc
= d
;
881 bool AaptGroupEntry::getMncName(const char* name
,
882 ResTable_config
* out
)
884 if (strcmp(name
, kWildcardName
) == 0) {
885 if (out
) out
->mcc
= 0;
888 const char* c
= name
;
889 if (tolower(*c
) != 'm') return false;
891 if (tolower(*c
) != 'n') return false;
893 if (tolower(*c
) != 'c') return false;
898 while (*c
>= '0' && *c
<= '9') {
901 if (*c
!= 0) return false;
902 if (c
-val
== 0 || c
-val
> 3) return false;
905 out
->mnc
= atoi(val
);
912 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
915 * TODO: Should insist that the first two letters are lower case, and the
916 * second two are upper.
918 bool AaptGroupEntry::getLocaleName(const char* fileName
,
919 ResTable_config
* out
)
921 if (strcmp(fileName
, kWildcardName
) == 0
922 || strcmp(fileName
, kDefaultLocale
) == 0) {
924 out
->language
[0] = 0;
925 out
->language
[1] = 0;
932 if (strlen(fileName
) == 2 && isalpha(fileName
[0]) && isalpha(fileName
[1])) {
934 out
->language
[0] = fileName
[0];
935 out
->language
[1] = fileName
[1];
942 if (strlen(fileName
) == 5 &&
943 isalpha(fileName
[0]) &&
944 isalpha(fileName
[1]) &&
945 fileName
[2] == '-' &&
946 isalpha(fileName
[3]) &&
947 isalpha(fileName
[4])) {
949 out
->language
[0] = fileName
[0];
950 out
->language
[1] = fileName
[1];
951 out
->country
[0] = fileName
[3];
952 out
->country
[1] = fileName
[4];
960 bool AaptGroupEntry::getScreenLayoutSizeName(const char* name
,
961 ResTable_config
* out
)
963 if (strcmp(name
, kWildcardName
) == 0) {
964 if (out
) out
->screenLayout
=
965 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
966 | ResTable_config::SCREENSIZE_ANY
;
968 } else if (strcmp(name
, "small") == 0) {
969 if (out
) out
->screenLayout
=
970 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
971 | ResTable_config::SCREENSIZE_SMALL
;
973 } else if (strcmp(name
, "normal") == 0) {
974 if (out
) out
->screenLayout
=
975 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
976 | ResTable_config::SCREENSIZE_NORMAL
;
978 } else if (strcmp(name
, "large") == 0) {
979 if (out
) out
->screenLayout
=
980 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
981 | ResTable_config::SCREENSIZE_LARGE
;
983 } else if (strcmp(name
, "xlarge") == 0) {
984 if (out
) out
->screenLayout
=
985 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
986 | ResTable_config::SCREENSIZE_XLARGE
;
993 bool AaptGroupEntry::getScreenLayoutLongName(const char* name
,
994 ResTable_config
* out
)
996 if (strcmp(name
, kWildcardName
) == 0) {
997 if (out
) out
->screenLayout
=
998 (out
->screenLayout
&~ResTable_config::MASK_SCREENLONG
)
999 | ResTable_config::SCREENLONG_ANY
;
1001 } else if (strcmp(name
, "long") == 0) {
1002 if (out
) out
->screenLayout
=
1003 (out
->screenLayout
&~ResTable_config::MASK_SCREENLONG
)
1004 | ResTable_config::SCREENLONG_YES
;
1006 } else if (strcmp(name
, "notlong") == 0) {
1007 if (out
) out
->screenLayout
=
1008 (out
->screenLayout
&~ResTable_config::MASK_SCREENLONG
)
1009 | ResTable_config::SCREENLONG_NO
;
1016 bool AaptGroupEntry::getOrientationName(const char* name
,
1017 ResTable_config
* out
)
1019 if (strcmp(name
, kWildcardName
) == 0) {
1020 if (out
) out
->orientation
= out
->ORIENTATION_ANY
;
1022 } else if (strcmp(name
, "port") == 0) {
1023 if (out
) out
->orientation
= out
->ORIENTATION_PORT
;
1025 } else if (strcmp(name
, "land") == 0) {
1026 if (out
) out
->orientation
= out
->ORIENTATION_LAND
;
1028 } else if (strcmp(name
, "square") == 0) {
1029 if (out
) out
->orientation
= out
->ORIENTATION_SQUARE
;
1036 bool AaptGroupEntry::getUiModeTypeName(const char* name
,
1037 ResTable_config
* out
)
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
;
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
;
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
;
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
;
1064 bool AaptGroupEntry::getUiModeNightName(const char* name
,
1065 ResTable_config
* out
)
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
;
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
;
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
;
1087 bool AaptGroupEntry::getDensityName(const char* name
,
1088 ResTable_config
* out
)
1090 if (strcmp(name
, kWildcardName
) == 0) {
1091 if (out
) out
->density
= ResTable_config::DENSITY_DEFAULT
;
1095 if (strcmp(name
, "nodpi") == 0) {
1096 if (out
) out
->density
= ResTable_config::DENSITY_NONE
;
1100 if (strcmp(name
, "ldpi") == 0) {
1101 if (out
) out
->density
= ResTable_config::DENSITY_LOW
;
1105 if (strcmp(name
, "mdpi") == 0) {
1106 if (out
) out
->density
= ResTable_config::DENSITY_MEDIUM
;
1110 if (strcmp(name
, "tvdpi") == 0) {
1111 if (out
) out
->density
= ResTable_config::DENSITY_TV
;
1115 if (strcmp(name
, "hdpi") == 0) {
1116 if (out
) out
->density
= ResTable_config::DENSITY_HIGH
;
1120 if (strcmp(name
, "xhdpi") == 0) {
1121 if (out
) out
->density
= ResTable_config::DENSITY_MEDIUM
*2;
1125 char* c
= (char*)name
;
1126 while (*c
>= '0' && *c
<= '9') {
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' ||
1138 // temporarily replace the first letter with \0 to
1147 if (out
) out
->density
= d
;
1154 bool AaptGroupEntry::getTouchscreenName(const char* name
,
1155 ResTable_config
* out
)
1157 if (strcmp(name
, kWildcardName
) == 0) {
1158 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_ANY
;
1160 } else if (strcmp(name
, "notouch") == 0) {
1161 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_NOTOUCH
;
1163 } else if (strcmp(name
, "stylus") == 0) {
1164 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_STYLUS
;
1166 } else if (strcmp(name
, "finger") == 0) {
1167 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_FINGER
;
1174 bool AaptGroupEntry::getKeysHiddenName(const char* name
,
1175 ResTable_config
* out
)
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
;
1194 if (out
) out
->inputFlags
= (out
->inputFlags
&~mask
) | value
;
1201 bool AaptGroupEntry::getKeyboardName(const char* name
,
1202 ResTable_config
* out
)
1204 if (strcmp(name
, kWildcardName
) == 0) {
1205 if (out
) out
->keyboard
= out
->KEYBOARD_ANY
;
1207 } else if (strcmp(name
, "nokeys") == 0) {
1208 if (out
) out
->keyboard
= out
->KEYBOARD_NOKEYS
;
1210 } else if (strcmp(name
, "qwerty") == 0) {
1211 if (out
) out
->keyboard
= out
->KEYBOARD_QWERTY
;
1213 } else if (strcmp(name
, "12key") == 0) {
1214 if (out
) out
->keyboard
= out
->KEYBOARD_12KEY
;
1221 bool AaptGroupEntry::getNavHiddenName(const char* name
,
1222 ResTable_config
* out
)
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
;
1238 if (out
) out
->inputFlags
= (out
->inputFlags
&~mask
) | value
;
1245 bool AaptGroupEntry::getNavigationName(const char* name
,
1246 ResTable_config
* out
)
1248 if (strcmp(name
, kWildcardName
) == 0) {
1249 if (out
) out
->navigation
= out
->NAVIGATION_ANY
;
1251 } else if (strcmp(name
, "nonav") == 0) {
1252 if (out
) out
->navigation
= out
->NAVIGATION_NONAV
;
1254 } else if (strcmp(name
, "dpad") == 0) {
1255 if (out
) out
->navigation
= out
->NAVIGATION_DPAD
;
1257 } else if (strcmp(name
, "trackball") == 0) {
1258 if (out
) out
->navigation
= out
->NAVIGATION_TRACKBALL
;
1260 } else if (strcmp(name
, "wheel") == 0) {
1261 if (out
) out
->navigation
= out
->NAVIGATION_WHEEL
;
1268 bool AaptGroupEntry::getScreenSizeName(const char* name
, ResTable_config
* out
)
1270 if (strcmp(name
, kWildcardName
) == 0) {
1272 out
->screenWidth
= out
->SCREENWIDTH_ANY
;
1273 out
->screenHeight
= out
->SCREENHEIGHT_ANY
;
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
);
1285 while (*y
>= '0' && *y
<= '9') y
++;
1286 if (y
== name
|| *y
!= 0) return false;
1287 String8
yName(x
, y
-x
);
1289 uint16_t w
= (uint16_t)atoi(xName
.string());
1290 uint16_t h
= (uint16_t)atoi(yName
.string());
1296 out
->screenWidth
= w
;
1297 out
->screenHeight
= h
;
1303 bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name
, ResTable_config
* out
)
1305 if (strcmp(name
, kWildcardName
) == 0) {
1307 out
->smallestScreenWidthDp
= out
->SCREENWIDTH_ANY
;
1312 if (*name
!= 's') return false;
1314 if (*name
!= 'w') return false;
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
);
1322 out
->smallestScreenWidthDp
= (uint16_t)atoi(xName
.string());
1328 bool AaptGroupEntry::getScreenWidthDpName(const char* name
, ResTable_config
* out
)
1330 if (strcmp(name
, kWildcardName
) == 0) {
1332 out
->screenWidthDp
= out
->SCREENWIDTH_ANY
;
1337 if (*name
!= 'w') return false;
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
);
1345 out
->screenWidthDp
= (uint16_t)atoi(xName
.string());
1351 bool AaptGroupEntry::getScreenHeightDpName(const char* name
, ResTable_config
* out
)
1353 if (strcmp(name
, kWildcardName
) == 0) {
1355 out
->screenHeightDp
= out
->SCREENWIDTH_ANY
;
1360 if (*name
!= 'h') return false;
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
);
1368 out
->screenHeightDp
= (uint16_t)atoi(xName
.string());
1374 bool AaptGroupEntry::getVersionName(const char* name
, ResTable_config
* out
)
1376 if (strcmp(name
, kWildcardName
) == 0) {
1378 out
->sdkVersion
= out
->SDKVERSION_ANY
;
1379 out
->minorVersion
= out
->MINORVERSION_ANY
;
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
);
1395 out
->sdkVersion
= (uint16_t)atoi(sdkName
.string());
1396 out
->minorVersion
= 0;
1402 int AaptGroupEntry::compare(const AaptGroupEntry
& o
) const
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
);
1427 const ResTable_config
& AaptGroupEntry::toParams() const
1429 if (!mParamsChanged
) {
1433 mParamsChanged
= false;
1434 ResTable_config
& params(mParams
);
1435 memset(¶ms
, 0, sizeof(params
));
1436 getMccName(mcc
.string(), ¶ms
);
1437 getMncName(mnc
.string(), ¶ms
);
1438 getLocaleName(locale
.string(), ¶ms
);
1439 getSmallestScreenWidthDpName(smallestScreenWidthDp
.string(), ¶ms
);
1440 getScreenWidthDpName(screenWidthDp
.string(), ¶ms
);
1441 getScreenHeightDpName(screenHeightDp
.string(), ¶ms
);
1442 getScreenLayoutSizeName(screenLayoutSize
.string(), ¶ms
);
1443 getScreenLayoutLongName(screenLayoutLong
.string(), ¶ms
);
1444 getOrientationName(orientation
.string(), ¶ms
);
1445 getUiModeTypeName(uiModeType
.string(), ¶ms
);
1446 getUiModeNightName(uiModeNight
.string(), ¶ms
);
1447 getDensityName(density
.string(), ¶ms
);
1448 getTouchscreenName(touchscreen
.string(), ¶ms
);
1449 getKeysHiddenName(keysHidden
.string(), ¶ms
);
1450 getKeyboardName(keyboard
.string(), ¶ms
);
1451 getNavHiddenName(navHidden
.string(), ¶ms
);
1452 getNavigationName(navigation
.string(), ¶ms
);
1453 getScreenSizeName(screenSize
.string(), ¶ms
);
1454 getVersionName(version
.string(), ¶ms
);
1456 // Fix up version number based on specified parameters.
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
) {
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
) {
1475 if (minSdk
> params
.sdkVersion
) {
1476 params
.sdkVersion
= minSdk
;
1482 // =========================================================================
1483 // =========================================================================
1484 // =========================================================================
1486 void* AaptFile::editData(size_t size
)
1488 if (size
<= mBufferSize
) {
1492 size_t allocSize
= (size
*3)/2;
1493 void* buf
= realloc(mData
, allocSize
);
1499 mBufferSize
= allocSize
;
1503 void* AaptFile::editData(size_t* outSize
)
1506 *outSize
= mDataSize
;
1511 void* AaptFile::padData(size_t wordSize
)
1513 const size_t extra
= mDataSize%wordSize
;
1518 size_t initial
= mDataSize
;
1519 void* data
= editData(initial
+(wordSize
-extra
));
1521 memset(((uint8_t*)data
) + initial
, 0, wordSize
-extra
);
1526 status_t
AaptFile::writeData(const void* data
, size_t size
)
1528 size_t end
= mDataSize
;
1529 size_t total
= size
+ end
;
1530 void* buf
= editData(total
);
1532 return UNKNOWN_ERROR
;
1534 memcpy(((char*)buf
)+end
, data
, size
);
1538 void AaptFile::clearData()
1540 if (mData
!= NULL
) free(mData
);
1546 String8
AaptFile::getPrintableSource() const
1549 String8
name(mGroupEntry
.toDirName(String8()));
1550 name
.appendPath(mPath
);
1551 name
.append(" #generated");
1557 // =========================================================================
1558 // =========================================================================
1559 // =========================================================================
1561 status_t
AaptGroup::addFile(const sp
<AaptFile
>& file
)
1563 if (mFiles
.indexOfKey(file
->getGroupEntry()) < 0) {
1564 file
->mPath
= mPath
;
1565 mFiles
.add(file
->getGroupEntry(), file
);
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());
1576 SourcePos(file
->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1577 getPrintableSource().string());
1578 return UNKNOWN_ERROR
;
1581 void AaptGroup::removeFile(size_t index
)
1583 mFiles
.removeItemsAt(index
);
1586 void AaptGroup::print(const String8
& prefix
) const
1588 printf("%s%s\n", prefix
.string(), getPath().string());
1589 const size_t N
=mFiles
.size();
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());
1598 printf("%s Src: (%s) %s\n", prefix
.string(), e
.toDirName(String8()).string(),
1599 file
->getPrintableSource().string());
1601 //printf("%s File Group Entry: %s\n", prefix.string(),
1602 // file->getGroupEntry().toDirName(String8()).string());
1606 String8
AaptGroup::getPrintableSource() const
1608 if (mFiles
.size() > 0) {
1609 // Arbitrarily pull the first source file out of the list.
1610 return mFiles
.valueAt(0)->getPrintableSource();
1613 // Should never hit this case, but to be safe...
1618 // =========================================================================
1619 // =========================================================================
1620 // =========================================================================
1622 status_t
AaptDir::addFile(const String8
& name
, const sp
<AaptGroup
>& file
)
1624 if (mFiles
.indexOfKey(name
) >= 0) {
1625 return ALREADY_EXISTS
;
1627 mFiles
.add(name
, file
);
1631 status_t
AaptDir::addDir(const String8
& name
, const sp
<AaptDir
>& dir
)
1633 if (mDirs
.indexOfKey(name
) >= 0) {
1634 return ALREADY_EXISTS
;
1636 mDirs
.add(name
, dir
);
1640 sp
<AaptDir
> AaptDir::makeDir(const String8
& path
)
1643 String8 remain
= path
;
1645 sp
<AaptDir
> subdir
= this;
1646 while (name
= remain
.walkPath(&remain
), remain
!= "") {
1647 subdir
= subdir
->makeDir(name
);
1650 ssize_t i
= subdir
->mDirs
.indexOfKey(name
);
1652 return subdir
->mDirs
.valueAt(i
);
1654 sp
<AaptDir
> dir
= new AaptDir(name
, subdir
->mPath
.appendPathCopy(name
));
1655 subdir
->mDirs
.add(name
, dir
);
1659 void AaptDir::removeFile(const String8
& name
)
1661 mFiles
.removeItem(name
);
1664 void AaptDir::removeDir(const String8
& name
)
1666 mDirs
.removeItem(name
);
1669 status_t
AaptDir::addLeafFile(const String8
& leafName
, const sp
<AaptFile
>& file
)
1671 sp
<AaptGroup
> group
;
1672 if (mFiles
.indexOfKey(leafName
) >= 0) {
1673 group
= mFiles
.valueFor(leafName
);
1675 group
= new AaptGroup(leafName
, mPath
.appendPathCopy(leafName
));
1676 mFiles
.add(leafName
, group
);
1679 return group
->addFile(file
);
1682 ssize_t
AaptDir::slurpFullTree(Bundle
* bundle
, const String8
& srcDir
,
1683 const AaptGroupEntry
& kind
, const String8
& resType
,
1684 sp
<FilePathStore
>& fullResPaths
)
1686 Vector
<String8
> fileNames
;
1690 dir
= opendir(srcDir
.string());
1692 fprintf(stderr
, "ERROR: opendir(%s): %s\n", srcDir
.string(), strerror(errno
));
1693 return UNKNOWN_ERROR
;
1697 * Slurp the filenames out of the directory.
1700 struct dirent
* entry
;
1702 entry
= readdir(dir
);
1706 if (isHidden(srcDir
.string(), entry
->d_name
))
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
));
1723 * Stash away the files and recursively descend into subdirectories.
1725 const size_t N
= fileNames
.size();
1727 for (i
= 0; i
< N
; i
++) {
1728 String8
pathName(srcDir
);
1731 pathName
.appendPath(fileNames
[i
].string());
1732 type
= getFileType(pathName
.string());
1733 if (type
== kFileTypeDirectory
) {
1735 bool notAdded
= false;
1736 if (mDirs
.indexOfKey(fileNames
[i
]) >= 0) {
1737 subdir
= mDirs
.valueFor(fileNames
[i
]);
1739 subdir
= new AaptDir(fileNames
[i
], mPath
.appendPathCopy(fileNames
[i
]));
1742 ssize_t res
= subdir
->slurpFullTree(bundle
, pathName
, kind
,
1743 resType
, fullResPaths
);
1744 if (res
< NO_ERROR
) {
1747 if (res
> 0 && notAdded
) {
1748 mDirs
.add(fileNames
[i
], subdir
);
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
) {
1761 if (bundle
->getVerbose())
1762 printf(" (ignoring non-file/dir '%s')\n", pathName
.string());
1769 status_t
AaptDir::validate() const
1771 const size_t NF
= mFiles
.size();
1772 const size_t ND
= mDirs
.size();
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
;
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
;
1791 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1792 // (this is mostly caught by the "marked" stuff, below)
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
;
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
;
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
;
1824 status_t err
= mDirs
.valueAt(i
)->validate();
1825 if (err
!= NO_ERROR
) {
1833 void AaptDir::print(const String8
& prefix
) const
1835 const size_t ND
=getDirs().size();
1837 for (i
=0; i
<ND
; i
++) {
1838 getDirs().valueAt(i
)->print(prefix
);
1841 const size_t NF
=getFiles().size();
1842 for (i
=0; i
<NF
; i
++) {
1843 getFiles().valueAt(i
)->print(prefix
);
1847 String8
AaptDir::getPrintableSource() const
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();
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();
1858 // Should never hit this case, but to be safe...
1863 // =========================================================================
1864 // =========================================================================
1865 // =========================================================================
1867 AaptAssets::AaptAssets()
1868 : AaptDir(String8(), String8()),
1869 mChanged(false), mHaveIncludedAssets(false), mRes(NULL
)
1873 const SortedVector
<AaptGroupEntry
>& AaptAssets::getGroupEntries() const {
1876 return mGroupEntries
;
1879 status_t
AaptAssets::addFile(const String8
& name
, const sp
<AaptGroup
>& file
)
1882 return AaptDir::addFile(name
, file
);
1885 sp
<AaptFile
> AaptAssets::addFile(
1886 const String8
& filePath
, const AaptGroupEntry
& entry
,
1887 const String8
& srcDir
, sp
<AaptGroup
>* outGroup
,
1888 const String8
& resType
)
1890 sp
<AaptDir
> dir
= this;
1891 sp
<AaptGroup
> group
;
1893 String8 root
, remain(filePath
), partialPath
;
1894 while (remain
.length() > 0) {
1895 root
= remain
.walkPath(&remain
);
1896 partialPath
.appendPath(root
);
1898 const String8
rootStr(root
);
1900 if (remain
.length() == 0) {
1901 ssize_t i
= dir
->getFiles().indexOfKey(rootStr
);
1903 group
= dir
->getFiles().valueAt(i
);
1905 group
= new AaptGroup(rootStr
, filePath
);
1906 status_t res
= dir
->addFile(rootStr
, group
);
1907 if (res
!= NO_ERROR
) {
1911 file
= new AaptFile(srcDir
.appendPathCopy(filePath
), entry
, resType
);
1912 status_t res
= group
->addFile(file
);
1913 if (res
!= NO_ERROR
) {
1919 ssize_t i
= dir
->getDirs().indexOfKey(rootStr
);
1921 dir
= dir
->getDirs().valueAt(i
);
1923 sp
<AaptDir
> subdir
= new AaptDir(rootStr
, partialPath
);
1924 status_t res
= dir
->addDir(rootStr
, subdir
);
1925 if (res
!= NO_ERROR
) {
1933 mGroupEntries
.add(entry
);
1934 if (outGroup
) *outGroup
= group
;
1938 void AaptAssets::addResource(const String8
& leafName
, const String8
& path
,
1939 const sp
<AaptFile
>& file
, const String8
& resType
)
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
);
1947 subdir
->addFile(leafName
, grr
);
1951 ssize_t
AaptAssets::slurpFromArgs(Bundle
* bundle
)
1956 const Vector
<const char *>& resDirs
= bundle
->getResourceSourceDirs();
1957 const size_t dirCount
=resDirs
.size();
1958 sp
<AaptAssets
> current
= this;
1960 const int N
= bundle
->getFileSpecCount();
1963 * If a package manifest was specified, include that first.
1965 if (bundle
->getAndroidManifestFile() != NULL
) {
1966 // place at root of zip.
1967 String8
srcFile(bundle
->getAndroidManifestFile());
1968 addFile(srcFile
.getPathLeaf(), AaptGroupEntry(), srcFile
.getPathDir(),
1974 * If a directory of custom assets was supplied, slurp 'em up.
1976 if (bundle
->getAssetSourceDir()) {
1977 const char* assetDir
= bundle
->getAssetSourceDir();
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
;
1984 if (type
!= kFileTypeDirectory
) {
1985 fprintf(stderr
, "ERROR: '%s' is not a directory\n", assetDir
);
1986 return UNKNOWN_ERROR
;
1989 String8
assetRoot(assetDir
);
1990 sp
<AaptDir
> assetAaptDir
= makeDir(String8(kAssetDir
));
1991 AaptGroupEntry group
;
1992 count
= assetAaptDir
->slurpFullTree(bundle
, assetRoot
, group
,
1993 String8(), mFullAssetPaths
);
1999 mGroupEntries
.add(group
);
2001 totalCount
+= count
;
2003 if (bundle
->getVerbose())
2004 printf("Found %d custom asset file%s in %s\n",
2005 count
, (count
==1) ? "" : "s", assetDir
);
2009 * If a directory of resource-specific assets was supplied, slurp 'em up.
2011 for (size_t i
=0; i
<dirCount
; i
++) {
2012 const char *res
= resDirs
[i
];
2014 type
= getFileType(res
);
2015 if (type
== kFileTypeNonexistent
) {
2016 fprintf(stderr
, "ERROR: resource directory '%s' does not exist\n", res
);
2017 return UNKNOWN_ERROR
;
2019 if (type
== kFileTypeDirectory
) {
2021 sp
<AaptAssets
> nextOverlay
= new AaptAssets();
2022 current
->setOverlay(nextOverlay
);
2023 current
= nextOverlay
;
2024 current
->setFullResPaths(mFullResPaths
);
2026 count
= current
->slurpResourceTree(bundle
, String8(res
));
2032 totalCount
+= count
;
2035 fprintf(stderr
, "ERROR: '%s' is not a directory\n", res
);
2036 return UNKNOWN_ERROR
;
2042 * Now do any additional raw files.
2044 for (int arg
=0; arg
<N
; arg
++) {
2045 const char* assetDir
= bundle
->getFileSpecEntry(arg
);
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
;
2052 if (type
!= kFileTypeDirectory
) {
2053 fprintf(stderr
, "ERROR: '%s' is not a directory\n", assetDir
);
2054 return UNKNOWN_ERROR
;
2057 String8
assetRoot(assetDir
);
2059 if (bundle
->getVerbose())
2060 printf("Processing raw dir '%s'\n", (const char*) assetDir
);
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.
2067 count
= slurpFullTree(bundle
, assetRoot
, AaptGroupEntry(), String8(), mFullAssetPaths
);
2069 /* failure; report error and remove archive */
2073 totalCount
+= count
;
2075 if (bundle
->getVerbose())
2076 printf("Found %d asset file%s in %s\n",
2077 count
, (count
==1) ? "" : "s", assetDir
);
2081 if (count
!= NO_ERROR
) {
2086 count
= filter(bundle
);
2087 if (count
!= NO_ERROR
) {
2096 ssize_t
AaptAssets::slurpFullTree(Bundle
* bundle
, const String8
& srcDir
,
2097 const AaptGroupEntry
& kind
,
2098 const String8
& resType
,
2099 sp
<FilePathStore
>& fullResPaths
)
2101 ssize_t res
= AaptDir::slurpFullTree(bundle
, srcDir
, kind
, resType
, fullResPaths
);
2103 mGroupEntries
.add(kind
);
2109 ssize_t
AaptAssets::slurpResourceTree(Bundle
* bundle
, const String8
& srcDir
)
2113 DIR* dir
= opendir(srcDir
.string());
2115 fprintf(stderr
, "ERROR: opendir(%s): %s\n", srcDir
.string(), strerror(errno
));
2116 return UNKNOWN_ERROR
;
2122 * Run through the directory, looking for dirs that match the
2126 struct dirent
* entry
= readdir(dir
);
2127 if (entry
== NULL
) {
2131 if (isHidden(srcDir
.string(), entry
->d_name
)) {
2135 String8
subdirName(srcDir
);
2136 subdirName
.appendPath(entry
->d_name
);
2138 AaptGroupEntry group
;
2140 bool b
= group
.initFromDirName(entry
->d_name
, &resType
);
2142 fprintf(stderr
, "invalid resource directory name: %s/%s\n", srcDir
.string(),
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
);
2158 FileType type
= getFileType(subdirName
.string());
2160 if (type
== kFileTypeDirectory
) {
2161 sp
<AaptDir
> dir
= makeDir(resType
);
2162 ssize_t res
= dir
->slurpFullTree(bundle
, subdirName
, group
,
2163 resType
, mFullResPaths
);
2169 mGroupEntries
.add(group
);
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
2176 sp
<AaptDir
> rdir
= resDir(resType
);
2181 if (bundle
->getVerbose()) {
2182 fprintf(stderr
, " (ignoring file '%s')\n", subdirName
.string());
2198 AaptAssets::slurpResourceZip(Bundle
* bundle
, const char* filename
)
2201 SortedVector
<AaptGroupEntry
> entries
;
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
);
2212 const int N
= zip
->getNumEntries();
2213 for (int i
=0; i
<N
; i
++) {
2214 ZipEntry
* entry
= zip
->getEntryByIndex(i
);
2215 if (entry
->getDeleted()) {
2219 String8
entryName(entry
->getFileName());
2221 String8 dirName
= entryName
.getPathDir();
2222 sp
<AaptDir
> dir
= dirName
== "" ? this : makeDir(dirName
);
2225 AaptGroupEntry kind
;
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
);
2232 // these are untyped and don't have an AaptGroupEntry
2234 if (entries
.indexOf(kind
) < 0) {
2236 mGroupEntries
.add(kind
);
2239 // use the one from the zip file if they both exist.
2240 dir
->removeFile(entryName
.getPathLeaf());
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());
2249 file
->setCompressionMethod(entry
->getCompressionMethod());
2252 if (entryName
== "AndroidManifest.xml") {
2253 printf("AndroidManifest.xml\n");
2255 printf("\n\nfile: %s\n", entryName
.string());
2258 size_t len
= entry
->getUncompressedLen();
2259 void* data
= zip
->uncompress(entry
);
2260 void* buf
= file
->editData(len
);
2261 memcpy(buf
, data
, len
);
2265 const unsigned char* p
= (unsigned char*)data
;
2266 const unsigned char* end
= p
+len
;
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
);
2288 status_t
AaptAssets::filter(Bundle
* bundle
)
2290 ResourceFilter reqFilter
;
2291 status_t err
= reqFilter
.parse(bundle
->getConfigurations());
2292 if (err
!= NO_ERROR
) {
2296 ResourceFilter prefFilter
;
2297 err
= prefFilter
.parse(bundle
->getPreferredConfigurations());
2298 if (err
!= NO_ERROR
) {
2302 if (reqFilter
.isEmpty() && prefFilter
.isEmpty()) {
2306 if (bundle
->getVerbose()) {
2307 if (!reqFilter
.isEmpty()) {
2308 printf("Applying required filter: %s\n",
2309 bundle
->getConfigurations());
2311 if (!prefFilter
.isEmpty()) {
2312 printf("Applying preferred filter: %s\n",
2313 bundle
->getPreferredConfigurations());
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.
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?
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
);
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.
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.
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());
2369 // Quick check: no preferred filters, nothing more to do.
2370 if (prefFilter
.isEmpty()) {
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.
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.
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());
2424 sp
<AaptSymbols
> AaptAssets::getSymbolsFor(const String8
& name
)
2426 sp
<AaptSymbols
> sym
= mSymbols
.valueFor(name
);
2428 sym
= new AaptSymbols();
2429 mSymbols
.add(name
, sym
);
2434 status_t
AaptAssets::buildIncludedResources(Bundle
* bundle
)
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",
2446 return UNKNOWN_ERROR
;
2449 mHaveIncludedAssets
= true;
2455 status_t
AaptAssets::addIncludedResources(const sp
<AaptFile
>& file
)
2457 const ResTable
& res
= getIncludedResources();
2459 return const_cast<ResTable
&>(res
).add(file
->getData(), file
->getSize(), NULL
);
2462 const ResTable
& AaptAssets::getIncludedResources() const
2464 return mIncludedAssets
.getResources(false);
2467 void AaptAssets::print(const String8
& prefix
) const
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)");
2481 printf("\n%sFiles:\n", prefix
.string());
2482 AaptDir::print(innerPrefix
);
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
);
2494 sp
<AaptDir
> AaptAssets::resDir(const String8
& name
) const
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
) {
2508 valid_symbol_name(const String8
& symbol
)
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",
2522 const char*const* k
= KEYWORDS
;
2523 const char*const s
= symbol
.string();
2525 if (0 == strcmp(s
, *k
)) {