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:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
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] == '*') {
127 ignore
= strncasecmp(token
, path
+ plen
- n
, n
) == 0;
129 } else if (n
> 1 && token
[n
- 1] == '*') {
131 ignore
= strncasecmp(token
, path
, n
- 1) == 0;
133 ignore
= strcasecmp(token
, path
) == 0;
137 if (ignore
&& chatty
) {
138 fprintf(stderr
, " (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
139 type
== kFileTypeDirectory
? "dir" : "file",
141 matchedPattern
? matchedPattern
: "");
148 // =========================================================================
149 // =========================================================================
150 // =========================================================================
153 AaptGroupEntry::parseNamePart(const String8
& part
, int* axis
, uint32_t* value
)
155 ResTable_config config
;
158 if (getMccName(part
.string(), &config
)) {
165 if (getMncName(part
.string(), &config
)) {
172 if (part
.length() == 2 && isalpha(part
[0]) && isalpha(part
[1])) {
173 *axis
= AXIS_LANGUAGE
;
174 *value
= part
[1] << 8 | part
[0];
178 // locale - language_REGION
179 if (part
.length() == 5 && isalpha(part
[0]) && isalpha(part
[1])
180 && part
[2] == '_' && isalpha(part
[3]) && isalpha(part
[4])) {
181 *axis
= AXIS_LANGUAGE
;
182 *value
= (part
[4] << 24) | (part
[3] << 16) | (part
[1] << 8) | (part
[0]);
186 // smallest screen dp width
187 if (getSmallestScreenWidthDpName(part
.string(), &config
)) {
188 *axis
= AXIS_SMALLESTSCREENWIDTHDP
;
189 *value
= config
.smallestScreenWidthDp
;
194 if (getScreenWidthDpName(part
.string(), &config
)) {
195 *axis
= AXIS_SCREENWIDTHDP
;
196 *value
= config
.screenWidthDp
;
201 if (getScreenHeightDpName(part
.string(), &config
)) {
202 *axis
= AXIS_SCREENHEIGHTDP
;
203 *value
= config
.screenHeightDp
;
207 // screen layout size
208 if (getScreenLayoutSizeName(part
.string(), &config
)) {
209 *axis
= AXIS_SCREENLAYOUTSIZE
;
210 *value
= (config
.screenLayout
&ResTable_config::MASK_SCREENSIZE
);
214 // screen layout long
215 if (getScreenLayoutLongName(part
.string(), &config
)) {
216 *axis
= AXIS_SCREENLAYOUTLONG
;
217 *value
= (config
.screenLayout
&ResTable_config::MASK_SCREENLONG
);
222 if (getOrientationName(part
.string(), &config
)) {
223 *axis
= AXIS_ORIENTATION
;
224 *value
= config
.orientation
;
229 if (getUiModeTypeName(part
.string(), &config
)) {
230 *axis
= AXIS_UIMODETYPE
;
231 *value
= (config
.uiMode
&ResTable_config::MASK_UI_MODE_TYPE
);
236 if (getUiModeNightName(part
.string(), &config
)) {
237 *axis
= AXIS_UIMODENIGHT
;
238 *value
= (config
.uiMode
&ResTable_config::MASK_UI_MODE_NIGHT
);
243 if (getDensityName(part
.string(), &config
)) {
244 *axis
= AXIS_DENSITY
;
245 *value
= config
.density
;
250 if (getTouchscreenName(part
.string(), &config
)) {
251 *axis
= AXIS_TOUCHSCREEN
;
252 *value
= config
.touchscreen
;
257 if (getKeysHiddenName(part
.string(), &config
)) {
258 *axis
= AXIS_KEYSHIDDEN
;
259 *value
= config
.inputFlags
;
264 if (getKeyboardName(part
.string(), &config
)) {
265 *axis
= AXIS_KEYBOARD
;
266 *value
= config
.keyboard
;
271 if (getNavHiddenName(part
.string(), &config
)) {
272 *axis
= AXIS_NAVHIDDEN
;
273 *value
= config
.inputFlags
;
278 if (getNavigationName(part
.string(), &config
)) {
279 *axis
= AXIS_NAVIGATION
;
280 *value
= config
.navigation
;
285 if (getScreenSizeName(part
.string(), &config
)) {
286 *axis
= AXIS_SCREENSIZE
;
287 *value
= config
.screenSize
;
292 if (getVersionName(part
.string(), &config
)) {
293 *axis
= AXIS_VERSION
;
294 *value
= config
.version
;
302 AaptGroupEntry::getConfigValueForAxis(const ResTable_config
& config
, int axis
)
310 return (((uint32_t)config
.country
[1]) << 24) | (((uint32_t)config
.country
[0]) << 16)
311 | (((uint32_t)config
.language
[1]) << 8) | (config
.language
[0]);
312 case AXIS_SCREENLAYOUTSIZE
:
313 return config
.screenLayout
&ResTable_config::MASK_SCREENSIZE
;
314 case AXIS_ORIENTATION
:
315 return config
.orientation
;
316 case AXIS_UIMODETYPE
:
317 return (config
.uiMode
&ResTable_config::MASK_UI_MODE_TYPE
);
318 case AXIS_UIMODENIGHT
:
319 return (config
.uiMode
&ResTable_config::MASK_UI_MODE_NIGHT
);
321 return config
.density
;
322 case AXIS_TOUCHSCREEN
:
323 return config
.touchscreen
;
324 case AXIS_KEYSHIDDEN
:
325 return config
.inputFlags
;
327 return config
.keyboard
;
328 case AXIS_NAVIGATION
:
329 return config
.navigation
;
330 case AXIS_SCREENSIZE
:
331 return config
.screenSize
;
332 case AXIS_SMALLESTSCREENWIDTHDP
:
333 return config
.smallestScreenWidthDp
;
334 case AXIS_SCREENWIDTHDP
:
335 return config
.screenWidthDp
;
336 case AXIS_SCREENHEIGHTDP
:
337 return config
.screenHeightDp
;
339 return config
.version
;
345 AaptGroupEntry::configSameExcept(const ResTable_config
& config
,
346 const ResTable_config
& otherConfig
, int axis
)
348 for (int i
=AXIS_START
; i
<=AXIS_END
; i
++) {
352 if (getConfigValueForAxis(config
, i
) != getConfigValueForAxis(otherConfig
, i
)) {
360 AaptGroupEntry::initFromDirName(const char* dir
, String8
* resType
)
362 mParamsChanged
= true;
364 Vector
<String8
> parts
;
366 String8 mcc
, mnc
, loc
, layoutsize
, layoutlong
, orient
, den
;
367 String8 touch
, key
, keysHidden
, nav
, navHidden
, size
, vers
;
368 String8 uiModeType
, uiModeNight
, smallestwidthdp
, widthdp
, heightdp
;
372 while (NULL
!= (q
= strchr(p
, '-'))) {
376 //printf("part: %s\n", parts[parts.size()-1].string());
382 //printf("part: %s\n", parts[parts.size()-1].string());
384 const int N
= parts
.size();
386 String8 part
= parts
[index
];
389 if (!isValidResourceType(part
)) {
401 if (getMccName(part
.string())) {
410 //printf("not mcc: %s\n", part.string());
414 if (getMncName(part
.string())) {
423 //printf("not mcc: %s\n", part.string());
427 if (part
.length() == 2 && isalpha(part
[0]) && isalpha(part
[1])) {
436 //printf("not language: %s\n", part.string());
441 && part
.length() == 3 && part
[0] == 'r' && part
[0] && part
[1]) {
444 loc
+= part
.string() + 1;
452 //printf("not region: %s\n", part.string());
455 if (getSmallestScreenWidthDpName(part
.string())) {
456 smallestwidthdp
= part
;
464 //printf("not smallest screen width dp: %s\n", part.string());
467 if (getScreenWidthDpName(part
.string())) {
476 //printf("not screen width dp: %s\n", part.string());
479 if (getScreenHeightDpName(part
.string())) {
488 //printf("not screen height dp: %s\n", part.string());
491 if (getScreenLayoutSizeName(part
.string())) {
500 //printf("not screen layout size: %s\n", part.string());
503 if (getScreenLayoutLongName(part
.string())) {
512 //printf("not screen layout long: %s\n", part.string());
516 if (getOrientationName(part
.string())) {
525 //printf("not orientation: %s\n", part.string());
529 if (getUiModeTypeName(part
.string())) {
538 //printf("not ui mode type: %s\n", part.string());
542 if (getUiModeNightName(part
.string())) {
551 //printf("not ui mode night: %s\n", part.string());
555 if (getDensityName(part
.string())) {
564 //printf("not density: %s\n", part.string());
568 if (getTouchscreenName(part
.string())) {
577 //printf("not touchscreen: %s\n", part.string());
581 if (getKeysHiddenName(part
.string())) {
590 //printf("not keysHidden: %s\n", part.string());
594 if (getKeyboardName(part
.string())) {
603 //printf("not keyboard: %s\n", part.string());
607 if (getNavHiddenName(part
.string())) {
616 //printf("not navHidden: %s\n", part.string());
619 if (getNavigationName(part
.string())) {
628 //printf("not navigation: %s\n", part.string());
631 if (getScreenSizeName(part
.string())) {
640 //printf("not screen size: %s\n", part.string());
643 if (getVersionName(part
.string())) {
652 //printf("not version: %s\n", part.string());
655 // if there are extra parts, it doesn't match
662 this->screenLayoutSize
= layoutsize
;
663 this->screenLayoutLong
= layoutlong
;
664 this->smallestScreenWidthDp
= smallestwidthdp
;
665 this->screenWidthDp
= widthdp
;
666 this->screenHeightDp
= heightdp
;
667 this->orientation
= orient
;
668 this->uiModeType
= uiModeType
;
669 this->uiModeNight
= uiModeNight
;
671 this->touchscreen
= touch
;
672 this->keysHidden
= keysHidden
;
673 this->keyboard
= key
;
674 this->navHidden
= navHidden
;
675 this->navigation
= nav
;
676 this->screenSize
= size
;
677 this->version
= vers
;
679 // what is this anyway?
686 AaptGroupEntry::toString() const
688 String8 s
= this->mcc
;
694 s
+= smallestScreenWidthDp
;
700 s
+= screenLayoutSize
;
702 s
+= screenLayoutLong
;
704 s
+= this->orientation
;
729 AaptGroupEntry::toDirName(const String8
& resType
) const
732 if (this->mcc
!= "") {
733 if (s
.length() > 0) {
738 if (this->mnc
!= "") {
739 if (s
.length() > 0) {
744 if (this->locale
!= "") {
745 if (s
.length() > 0) {
750 if (this->smallestScreenWidthDp
!= "") {
751 if (s
.length() > 0) {
754 s
+= smallestScreenWidthDp
;
756 if (this->screenWidthDp
!= "") {
757 if (s
.length() > 0) {
762 if (this->screenHeightDp
!= "") {
763 if (s
.length() > 0) {
768 if (this->screenLayoutSize
!= "") {
769 if (s
.length() > 0) {
772 s
+= screenLayoutSize
;
774 if (this->screenLayoutLong
!= "") {
775 if (s
.length() > 0) {
778 s
+= screenLayoutLong
;
780 if (this->orientation
!= "") {
781 if (s
.length() > 0) {
786 if (this->uiModeType
!= "") {
787 if (s
.length() > 0) {
792 if (this->uiModeNight
!= "") {
793 if (s
.length() > 0) {
798 if (this->density
!= "") {
799 if (s
.length() > 0) {
804 if (this->touchscreen
!= "") {
805 if (s
.length() > 0) {
810 if (this->keysHidden
!= "") {
811 if (s
.length() > 0) {
816 if (this->keyboard
!= "") {
817 if (s
.length() > 0) {
822 if (this->navHidden
!= "") {
823 if (s
.length() > 0) {
828 if (this->navigation
!= "") {
829 if (s
.length() > 0) {
834 if (this->screenSize
!= "") {
835 if (s
.length() > 0) {
840 if (this->version
!= "") {
841 if (s
.length() > 0) {
850 bool AaptGroupEntry::getMccName(const char* name
,
851 ResTable_config
* out
)
853 if (strcmp(name
, kWildcardName
) == 0) {
854 if (out
) out
->mcc
= 0;
857 const char* c
= name
;
858 if (tolower(*c
) != 'm') return false;
860 if (tolower(*c
) != 'c') return false;
862 if (tolower(*c
) != 'c') return false;
867 while (*c
>= '0' && *c
<= '9') {
870 if (*c
!= 0) return false;
871 if (c
-val
!= 3) return false;
875 if (out
) out
->mcc
= d
;
882 bool AaptGroupEntry::getMncName(const char* name
,
883 ResTable_config
* out
)
885 if (strcmp(name
, kWildcardName
) == 0) {
886 if (out
) out
->mcc
= 0;
889 const char* c
= name
;
890 if (tolower(*c
) != 'm') return false;
892 if (tolower(*c
) != 'n') return false;
894 if (tolower(*c
) != 'c') return false;
899 while (*c
>= '0' && *c
<= '9') {
902 if (*c
!= 0) return false;
903 if (c
-val
== 0 || c
-val
> 3) return false;
906 out
->mnc
= atoi(val
);
913 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
916 * TODO: Should insist that the first two letters are lower case, and the
917 * second two are upper.
919 bool AaptGroupEntry::getLocaleName(const char* fileName
,
920 ResTable_config
* out
)
922 if (strcmp(fileName
, kWildcardName
) == 0
923 || strcmp(fileName
, kDefaultLocale
) == 0) {
925 out
->language
[0] = 0;
926 out
->language
[1] = 0;
933 if (strlen(fileName
) == 2 && isalpha(fileName
[0]) && isalpha(fileName
[1])) {
935 out
->language
[0] = fileName
[0];
936 out
->language
[1] = fileName
[1];
943 if (strlen(fileName
) == 5 &&
944 isalpha(fileName
[0]) &&
945 isalpha(fileName
[1]) &&
946 fileName
[2] == '-' &&
947 isalpha(fileName
[3]) &&
948 isalpha(fileName
[4])) {
950 out
->language
[0] = fileName
[0];
951 out
->language
[1] = fileName
[1];
952 out
->country
[0] = fileName
[3];
953 out
->country
[1] = fileName
[4];
961 bool AaptGroupEntry::getScreenLayoutSizeName(const char* name
,
962 ResTable_config
* out
)
964 if (strcmp(name
, kWildcardName
) == 0) {
965 if (out
) out
->screenLayout
=
966 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
967 | ResTable_config::SCREENSIZE_ANY
;
969 } else if (strcmp(name
, "small") == 0) {
970 if (out
) out
->screenLayout
=
971 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
972 | ResTable_config::SCREENSIZE_SMALL
;
974 } else if (strcmp(name
, "normal") == 0) {
975 if (out
) out
->screenLayout
=
976 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
977 | ResTable_config::SCREENSIZE_NORMAL
;
979 } else if (strcmp(name
, "large") == 0) {
980 if (out
) out
->screenLayout
=
981 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
982 | ResTable_config::SCREENSIZE_LARGE
;
984 } else if (strcmp(name
, "xlarge") == 0) {
985 if (out
) out
->screenLayout
=
986 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
987 | ResTable_config::SCREENSIZE_XLARGE
;
994 bool AaptGroupEntry::getScreenLayoutLongName(const char* name
,
995 ResTable_config
* out
)
997 if (strcmp(name
, kWildcardName
) == 0) {
998 if (out
) out
->screenLayout
=
999 (out
->screenLayout
&~ResTable_config::MASK_SCREENLONG
)
1000 | ResTable_config::SCREENLONG_ANY
;
1002 } else if (strcmp(name
, "long") == 0) {
1003 if (out
) out
->screenLayout
=
1004 (out
->screenLayout
&~ResTable_config::MASK_SCREENLONG
)
1005 | ResTable_config::SCREENLONG_YES
;
1007 } else if (strcmp(name
, "notlong") == 0) {
1008 if (out
) out
->screenLayout
=
1009 (out
->screenLayout
&~ResTable_config::MASK_SCREENLONG
)
1010 | ResTable_config::SCREENLONG_NO
;
1017 bool AaptGroupEntry::getOrientationName(const char* name
,
1018 ResTable_config
* out
)
1020 if (strcmp(name
, kWildcardName
) == 0) {
1021 if (out
) out
->orientation
= out
->ORIENTATION_ANY
;
1023 } else if (strcmp(name
, "port") == 0) {
1024 if (out
) out
->orientation
= out
->ORIENTATION_PORT
;
1026 } else if (strcmp(name
, "land") == 0) {
1027 if (out
) out
->orientation
= out
->ORIENTATION_LAND
;
1029 } else if (strcmp(name
, "square") == 0) {
1030 if (out
) out
->orientation
= out
->ORIENTATION_SQUARE
;
1037 bool AaptGroupEntry::getUiModeTypeName(const char* name
,
1038 ResTable_config
* out
)
1040 if (strcmp(name
, kWildcardName
) == 0) {
1041 if (out
) out
->uiMode
=
1042 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_TYPE
)
1043 | ResTable_config::UI_MODE_TYPE_ANY
;
1045 } else if (strcmp(name
, "desk") == 0) {
1046 if (out
) out
->uiMode
=
1047 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_TYPE
)
1048 | ResTable_config::UI_MODE_TYPE_DESK
;
1050 } else if (strcmp(name
, "car") == 0) {
1051 if (out
) out
->uiMode
=
1052 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_TYPE
)
1053 | ResTable_config::UI_MODE_TYPE_CAR
;
1055 } else if (strcmp(name
, "television") == 0) {
1056 if (out
) out
->uiMode
=
1057 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_TYPE
)
1058 | ResTable_config::UI_MODE_TYPE_TELEVISION
;
1065 bool AaptGroupEntry::getUiModeNightName(const char* name
,
1066 ResTable_config
* out
)
1068 if (strcmp(name
, kWildcardName
) == 0) {
1069 if (out
) out
->uiMode
=
1070 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_NIGHT
)
1071 | ResTable_config::UI_MODE_NIGHT_ANY
;
1073 } else if (strcmp(name
, "night") == 0) {
1074 if (out
) out
->uiMode
=
1075 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_NIGHT
)
1076 | ResTable_config::UI_MODE_NIGHT_YES
;
1078 } else if (strcmp(name
, "notnight") == 0) {
1079 if (out
) out
->uiMode
=
1080 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_NIGHT
)
1081 | ResTable_config::UI_MODE_NIGHT_NO
;
1088 bool AaptGroupEntry::getDensityName(const char* name
,
1089 ResTable_config
* out
)
1091 if (strcmp(name
, kWildcardName
) == 0) {
1092 if (out
) out
->density
= ResTable_config::DENSITY_DEFAULT
;
1096 if (strcmp(name
, "nodpi") == 0) {
1097 if (out
) out
->density
= ResTable_config::DENSITY_NONE
;
1101 if (strcmp(name
, "ldpi") == 0) {
1102 if (out
) out
->density
= ResTable_config::DENSITY_LOW
;
1106 if (strcmp(name
, "mdpi") == 0) {
1107 if (out
) out
->density
= ResTable_config::DENSITY_MEDIUM
;
1111 if (strcmp(name
, "tvdpi") == 0) {
1112 if (out
) out
->density
= ResTable_config::DENSITY_TV
;
1116 if (strcmp(name
, "hdpi") == 0) {
1117 if (out
) out
->density
= ResTable_config::DENSITY_HIGH
;
1121 if (strcmp(name
, "xhdpi") == 0) {
1122 if (out
) out
->density
= ResTable_config::DENSITY_MEDIUM
*2;
1126 char* c
= (char*)name
;
1127 while (*c
>= '0' && *c
<= '9') {
1131 // check that we have 'dpi' after the last digit.
1132 if (toupper(c
[0]) != 'D' ||
1133 toupper(c
[1]) != 'P' ||
1134 toupper(c
[2]) != 'I' ||
1139 // temporarily replace the first letter with \0 to
1148 if (out
) out
->density
= d
;
1155 bool AaptGroupEntry::getTouchscreenName(const char* name
,
1156 ResTable_config
* out
)
1158 if (strcmp(name
, kWildcardName
) == 0) {
1159 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_ANY
;
1161 } else if (strcmp(name
, "notouch") == 0) {
1162 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_NOTOUCH
;
1164 } else if (strcmp(name
, "stylus") == 0) {
1165 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_STYLUS
;
1167 } else if (strcmp(name
, "finger") == 0) {
1168 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_FINGER
;
1175 bool AaptGroupEntry::getKeysHiddenName(const char* name
,
1176 ResTable_config
* out
)
1180 if (strcmp(name
, kWildcardName
) == 0) {
1181 mask
= ResTable_config::MASK_KEYSHIDDEN
;
1182 value
= ResTable_config::KEYSHIDDEN_ANY
;
1183 } else if (strcmp(name
, "keysexposed") == 0) {
1184 mask
= ResTable_config::MASK_KEYSHIDDEN
;
1185 value
= ResTable_config::KEYSHIDDEN_NO
;
1186 } else if (strcmp(name
, "keyshidden") == 0) {
1187 mask
= ResTable_config::MASK_KEYSHIDDEN
;
1188 value
= ResTable_config::KEYSHIDDEN_YES
;
1189 } else if (strcmp(name
, "keyssoft") == 0) {
1190 mask
= ResTable_config::MASK_KEYSHIDDEN
;
1191 value
= ResTable_config::KEYSHIDDEN_SOFT
;
1195 if (out
) out
->inputFlags
= (out
->inputFlags
&~mask
) | value
;
1202 bool AaptGroupEntry::getKeyboardName(const char* name
,
1203 ResTable_config
* out
)
1205 if (strcmp(name
, kWildcardName
) == 0) {
1206 if (out
) out
->keyboard
= out
->KEYBOARD_ANY
;
1208 } else if (strcmp(name
, "nokeys") == 0) {
1209 if (out
) out
->keyboard
= out
->KEYBOARD_NOKEYS
;
1211 } else if (strcmp(name
, "qwerty") == 0) {
1212 if (out
) out
->keyboard
= out
->KEYBOARD_QWERTY
;
1214 } else if (strcmp(name
, "12key") == 0) {
1215 if (out
) out
->keyboard
= out
->KEYBOARD_12KEY
;
1222 bool AaptGroupEntry::getNavHiddenName(const char* name
,
1223 ResTable_config
* out
)
1227 if (strcmp(name
, kWildcardName
) == 0) {
1228 mask
= ResTable_config::MASK_NAVHIDDEN
;
1229 value
= ResTable_config::NAVHIDDEN_ANY
;
1230 } else if (strcmp(name
, "navexposed") == 0) {
1231 mask
= ResTable_config::MASK_NAVHIDDEN
;
1232 value
= ResTable_config::NAVHIDDEN_NO
;
1233 } else if (strcmp(name
, "navhidden") == 0) {
1234 mask
= ResTable_config::MASK_NAVHIDDEN
;
1235 value
= ResTable_config::NAVHIDDEN_YES
;
1239 if (out
) out
->inputFlags
= (out
->inputFlags
&~mask
) | value
;
1246 bool AaptGroupEntry::getNavigationName(const char* name
,
1247 ResTable_config
* out
)
1249 if (strcmp(name
, kWildcardName
) == 0) {
1250 if (out
) out
->navigation
= out
->NAVIGATION_ANY
;
1252 } else if (strcmp(name
, "nonav") == 0) {
1253 if (out
) out
->navigation
= out
->NAVIGATION_NONAV
;
1255 } else if (strcmp(name
, "dpad") == 0) {
1256 if (out
) out
->navigation
= out
->NAVIGATION_DPAD
;
1258 } else if (strcmp(name
, "trackball") == 0) {
1259 if (out
) out
->navigation
= out
->NAVIGATION_TRACKBALL
;
1261 } else if (strcmp(name
, "wheel") == 0) {
1262 if (out
) out
->navigation
= out
->NAVIGATION_WHEEL
;
1269 bool AaptGroupEntry::getScreenSizeName(const char* name
, ResTable_config
* out
)
1271 if (strcmp(name
, kWildcardName
) == 0) {
1273 out
->screenWidth
= out
->SCREENWIDTH_ANY
;
1274 out
->screenHeight
= out
->SCREENHEIGHT_ANY
;
1279 const char* x
= name
;
1280 while (*x
>= '0' && *x
<= '9') x
++;
1281 if (x
== name
|| *x
!= 'x') return false;
1282 String8
xName(name
, x
-name
);
1286 while (*y
>= '0' && *y
<= '9') y
++;
1287 if (y
== name
|| *y
!= 0) return false;
1288 String8
yName(x
, y
-x
);
1290 uint16_t w
= (uint16_t)atoi(xName
.string());
1291 uint16_t h
= (uint16_t)atoi(yName
.string());
1297 out
->screenWidth
= w
;
1298 out
->screenHeight
= h
;
1304 bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name
, ResTable_config
* out
)
1306 if (strcmp(name
, kWildcardName
) == 0) {
1308 out
->smallestScreenWidthDp
= out
->SCREENWIDTH_ANY
;
1313 if (*name
!= 's') return false;
1315 if (*name
!= 'w') return false;
1317 const char* x
= name
;
1318 while (*x
>= '0' && *x
<= '9') x
++;
1319 if (x
== name
|| x
[0] != 'd' || x
[1] != 'p' || x
[2] != 0) return false;
1320 String8
xName(name
, x
-name
);
1323 out
->smallestScreenWidthDp
= (uint16_t)atoi(xName
.string());
1329 bool AaptGroupEntry::getScreenWidthDpName(const char* name
, ResTable_config
* out
)
1331 if (strcmp(name
, kWildcardName
) == 0) {
1333 out
->screenWidthDp
= out
->SCREENWIDTH_ANY
;
1338 if (*name
!= 'w') return false;
1340 const char* x
= name
;
1341 while (*x
>= '0' && *x
<= '9') x
++;
1342 if (x
== name
|| x
[0] != 'd' || x
[1] != 'p' || x
[2] != 0) return false;
1343 String8
xName(name
, x
-name
);
1346 out
->screenWidthDp
= (uint16_t)atoi(xName
.string());
1352 bool AaptGroupEntry::getScreenHeightDpName(const char* name
, ResTable_config
* out
)
1354 if (strcmp(name
, kWildcardName
) == 0) {
1356 out
->screenHeightDp
= out
->SCREENWIDTH_ANY
;
1361 if (*name
!= 'h') return false;
1363 const char* x
= name
;
1364 while (*x
>= '0' && *x
<= '9') x
++;
1365 if (x
== name
|| x
[0] != 'd' || x
[1] != 'p' || x
[2] != 0) return false;
1366 String8
xName(name
, x
-name
);
1369 out
->screenHeightDp
= (uint16_t)atoi(xName
.string());
1375 bool AaptGroupEntry::getVersionName(const char* name
, ResTable_config
* out
)
1377 if (strcmp(name
, kWildcardName
) == 0) {
1379 out
->sdkVersion
= out
->SDKVERSION_ANY
;
1380 out
->minorVersion
= out
->MINORVERSION_ANY
;
1390 const char* s
= name
;
1391 while (*s
>= '0' && *s
<= '9') s
++;
1392 if (s
== name
|| *s
!= 0) return false;
1393 String8
sdkName(name
, s
-name
);
1396 out
->sdkVersion
= (uint16_t)atoi(sdkName
.string());
1397 out
->minorVersion
= 0;
1403 int AaptGroupEntry::compare(const AaptGroupEntry
& o
) const
1405 int v
= mcc
.compare(o
.mcc
);
1406 if (v
== 0) v
= mnc
.compare(o
.mnc
);
1407 if (v
== 0) v
= locale
.compare(o
.locale
);
1408 if (v
== 0) v
= vendor
.compare(o
.vendor
);
1409 if (v
== 0) v
= smallestScreenWidthDp
.compare(o
.smallestScreenWidthDp
);
1410 if (v
== 0) v
= screenWidthDp
.compare(o
.screenWidthDp
);
1411 if (v
== 0) v
= screenHeightDp
.compare(o
.screenHeightDp
);
1412 if (v
== 0) v
= screenLayoutSize
.compare(o
.screenLayoutSize
);
1413 if (v
== 0) v
= screenLayoutLong
.compare(o
.screenLayoutLong
);
1414 if (v
== 0) v
= orientation
.compare(o
.orientation
);
1415 if (v
== 0) v
= uiModeType
.compare(o
.uiModeType
);
1416 if (v
== 0) v
= uiModeNight
.compare(o
.uiModeNight
);
1417 if (v
== 0) v
= density
.compare(o
.density
);
1418 if (v
== 0) v
= touchscreen
.compare(o
.touchscreen
);
1419 if (v
== 0) v
= keysHidden
.compare(o
.keysHidden
);
1420 if (v
== 0) v
= keyboard
.compare(o
.keyboard
);
1421 if (v
== 0) v
= navHidden
.compare(o
.navHidden
);
1422 if (v
== 0) v
= navigation
.compare(o
.navigation
);
1423 if (v
== 0) v
= screenSize
.compare(o
.screenSize
);
1424 if (v
== 0) v
= version
.compare(o
.version
);
1428 const ResTable_config
& AaptGroupEntry::toParams() const
1430 if (!mParamsChanged
) {
1434 mParamsChanged
= false;
1435 ResTable_config
& params(mParams
);
1436 memset(¶ms
, 0, sizeof(params
));
1437 getMccName(mcc
.string(), ¶ms
);
1438 getMncName(mnc
.string(), ¶ms
);
1439 getLocaleName(locale
.string(), ¶ms
);
1440 getSmallestScreenWidthDpName(smallestScreenWidthDp
.string(), ¶ms
);
1441 getScreenWidthDpName(screenWidthDp
.string(), ¶ms
);
1442 getScreenHeightDpName(screenHeightDp
.string(), ¶ms
);
1443 getScreenLayoutSizeName(screenLayoutSize
.string(), ¶ms
);
1444 getScreenLayoutLongName(screenLayoutLong
.string(), ¶ms
);
1445 getOrientationName(orientation
.string(), ¶ms
);
1446 getUiModeTypeName(uiModeType
.string(), ¶ms
);
1447 getUiModeNightName(uiModeNight
.string(), ¶ms
);
1448 getDensityName(density
.string(), ¶ms
);
1449 getTouchscreenName(touchscreen
.string(), ¶ms
);
1450 getKeysHiddenName(keysHidden
.string(), ¶ms
);
1451 getKeyboardName(keyboard
.string(), ¶ms
);
1452 getNavHiddenName(navHidden
.string(), ¶ms
);
1453 getNavigationName(navigation
.string(), ¶ms
);
1454 getScreenSizeName(screenSize
.string(), ¶ms
);
1455 getVersionName(version
.string(), ¶ms
);
1457 // Fix up version number based on specified parameters.
1459 if (params
.smallestScreenWidthDp
!= ResTable_config::SCREENWIDTH_ANY
1460 || params
.screenWidthDp
!= ResTable_config::SCREENWIDTH_ANY
1461 || params
.screenHeightDp
!= ResTable_config::SCREENHEIGHT_ANY
) {
1462 minSdk
= SDK_HONEYCOMB_MR2
;
1463 } else if ((params
.uiMode
&ResTable_config::MASK_UI_MODE_TYPE
)
1464 != ResTable_config::UI_MODE_TYPE_ANY
1465 || (params
.uiMode
&ResTable_config::MASK_UI_MODE_NIGHT
)
1466 != ResTable_config::UI_MODE_NIGHT_ANY
) {
1468 } else if ((params
.screenLayout
&ResTable_config::MASK_SCREENSIZE
)
1469 != ResTable_config::SCREENSIZE_ANY
1470 || (params
.screenLayout
&ResTable_config::MASK_SCREENLONG
)
1471 != ResTable_config::SCREENLONG_ANY
1472 || params
.density
!= ResTable_config::DENSITY_DEFAULT
) {
1476 if (minSdk
> params
.sdkVersion
) {
1477 params
.sdkVersion
= minSdk
;
1483 // =========================================================================
1484 // =========================================================================
1485 // =========================================================================
1487 void* AaptFile::editData(size_t size
)
1489 if (size
<= mBufferSize
) {
1493 size_t allocSize
= (size
*3)/2;
1494 void* buf
= realloc(mData
, allocSize
);
1500 mBufferSize
= allocSize
;
1504 void* AaptFile::editData(size_t* outSize
)
1507 *outSize
= mDataSize
;
1512 void* AaptFile::padData(size_t wordSize
)
1514 const size_t extra
= mDataSize%wordSize
;
1519 size_t initial
= mDataSize
;
1520 void* data
= editData(initial
+(wordSize
-extra
));
1522 memset(((uint8_t*)data
) + initial
, 0, wordSize
-extra
);
1527 status_t
AaptFile::writeData(const void* data
, size_t size
)
1529 size_t end
= mDataSize
;
1530 size_t total
= size
+ end
;
1531 void* buf
= editData(total
);
1533 return UNKNOWN_ERROR
;
1535 memcpy(((char*)buf
)+end
, data
, size
);
1539 void AaptFile::clearData()
1541 if (mData
!= NULL
) free(mData
);
1547 String8
AaptFile::getPrintableSource() const
1550 String8
name(mGroupEntry
.toDirName(String8()));
1551 name
.appendPath(mPath
);
1552 name
.append(" #generated");
1558 // =========================================================================
1559 // =========================================================================
1560 // =========================================================================
1562 status_t
AaptGroup::addFile(const sp
<AaptFile
>& file
)
1564 if (mFiles
.indexOfKey(file
->getGroupEntry()) < 0) {
1565 file
->mPath
= mPath
;
1566 mFiles
.add(file
->getGroupEntry(), file
);
1571 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1572 file
->getSourceFile().string(),
1573 file
->getGroupEntry().toDirName(String8()).string(),
1574 mLeaf
.string(), mPath
.string());
1577 SourcePos(file
->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1578 getPrintableSource().string());
1579 return UNKNOWN_ERROR
;
1582 void AaptGroup::removeFile(size_t index
)
1584 mFiles
.removeItemsAt(index
);
1587 void AaptGroup::print(const String8
& prefix
) const
1589 printf("%s%s\n", prefix
.string(), getPath().string());
1590 const size_t N
=mFiles
.size();
1592 for (i
=0; i
<N
; i
++) {
1593 sp
<AaptFile
> file
= mFiles
.valueAt(i
);
1594 const AaptGroupEntry
& e
= file
->getGroupEntry();
1595 if (file
->hasData()) {
1596 printf("%s Gen: (%s) %d bytes\n", prefix
.string(), e
.toDirName(String8()).string(),
1597 (int)file
->getSize());
1599 printf("%s Src: (%s) %s\n", prefix
.string(), e
.toDirName(String8()).string(),
1600 file
->getPrintableSource().string());
1602 //printf("%s File Group Entry: %s\n", prefix.string(),
1603 // file->getGroupEntry().toDirName(String8()).string());
1607 String8
AaptGroup::getPrintableSource() const
1609 if (mFiles
.size() > 0) {
1610 // Arbitrarily pull the first source file out of the list.
1611 return mFiles
.valueAt(0)->getPrintableSource();
1614 // Should never hit this case, but to be safe...
1619 // =========================================================================
1620 // =========================================================================
1621 // =========================================================================
1623 status_t
AaptDir::addFile(const String8
& name
, const sp
<AaptGroup
>& file
)
1625 if (mFiles
.indexOfKey(name
) >= 0) {
1626 return ALREADY_EXISTS
;
1628 mFiles
.add(name
, file
);
1632 status_t
AaptDir::addDir(const String8
& name
, const sp
<AaptDir
>& dir
)
1634 if (mDirs
.indexOfKey(name
) >= 0) {
1635 return ALREADY_EXISTS
;
1637 mDirs
.add(name
, dir
);
1641 sp
<AaptDir
> AaptDir::makeDir(const String8
& path
)
1644 String8 remain
= path
;
1646 sp
<AaptDir
> subdir
= this;
1647 while (name
= remain
.walkPath(&remain
), remain
!= "") {
1648 subdir
= subdir
->makeDir(name
);
1651 ssize_t i
= subdir
->mDirs
.indexOfKey(name
);
1653 return subdir
->mDirs
.valueAt(i
);
1655 sp
<AaptDir
> dir
= new AaptDir(name
, subdir
->mPath
.appendPathCopy(name
));
1656 subdir
->mDirs
.add(name
, dir
);
1660 void AaptDir::removeFile(const String8
& name
)
1662 mFiles
.removeItem(name
);
1665 void AaptDir::removeDir(const String8
& name
)
1667 mDirs
.removeItem(name
);
1670 status_t
AaptDir::addLeafFile(const String8
& leafName
, const sp
<AaptFile
>& file
)
1672 sp
<AaptGroup
> group
;
1673 if (mFiles
.indexOfKey(leafName
) >= 0) {
1674 group
= mFiles
.valueFor(leafName
);
1676 group
= new AaptGroup(leafName
, mPath
.appendPathCopy(leafName
));
1677 mFiles
.add(leafName
, group
);
1680 return group
->addFile(file
);
1683 ssize_t
AaptDir::slurpFullTree(Bundle
* bundle
, const String8
& srcDir
,
1684 const AaptGroupEntry
& kind
, const String8
& resType
,
1685 sp
<FilePathStore
>& fullResPaths
)
1687 Vector
<String8
> fileNames
;
1691 dir
= opendir(srcDir
.string());
1693 fprintf(stderr
, "ERROR: opendir(%s): %s\n", srcDir
.string(), strerror(errno
));
1694 return UNKNOWN_ERROR
;
1698 * Slurp the filenames out of the directory.
1701 struct dirent
* entry
;
1703 entry
= readdir(dir
);
1707 if (isHidden(srcDir
.string(), entry
->d_name
))
1710 String8
name(entry
->d_name
);
1711 fileNames
.add(name
);
1712 // Add fully qualified path for dependency purposes
1713 // if we're collecting them
1714 if (fullResPaths
!= NULL
) {
1715 fullResPaths
->add(srcDir
.appendPathCopy(name
));
1724 * Stash away the files and recursively descend into subdirectories.
1726 const size_t N
= fileNames
.size();
1728 for (i
= 0; i
< N
; i
++) {
1729 String8
pathName(srcDir
);
1732 pathName
.appendPath(fileNames
[i
].string());
1733 type
= getFileType(pathName
.string());
1734 if (type
== kFileTypeDirectory
) {
1736 bool notAdded
= false;
1737 if (mDirs
.indexOfKey(fileNames
[i
]) >= 0) {
1738 subdir
= mDirs
.valueFor(fileNames
[i
]);
1740 subdir
= new AaptDir(fileNames
[i
], mPath
.appendPathCopy(fileNames
[i
]));
1743 ssize_t res
= subdir
->slurpFullTree(bundle
, pathName
, kind
,
1744 resType
, fullResPaths
);
1745 if (res
< NO_ERROR
) {
1748 if (res
> 0 && notAdded
) {
1749 mDirs
.add(fileNames
[i
], subdir
);
1752 } else if (type
== kFileTypeRegular
) {
1753 sp
<AaptFile
> file
= new AaptFile(pathName
, kind
, resType
);
1754 status_t err
= addLeafFile(fileNames
[i
], file
);
1755 if (err
!= NO_ERROR
) {
1762 if (bundle
->getVerbose())
1763 printf(" (ignoring non-file/dir '%s')\n", pathName
.string());
1770 status_t
AaptDir::validate() const
1772 const size_t NF
= mFiles
.size();
1773 const size_t ND
= mDirs
.size();
1775 for (i
= 0; i
< NF
; i
++) {
1776 if (!validateFileName(mFiles
.valueAt(i
)->getLeaf().string())) {
1777 SourcePos(mFiles
.valueAt(i
)->getPrintableSource(), -1).error(
1778 "Invalid filename. Unable to add.");
1779 return UNKNOWN_ERROR
;
1783 for (j
= i
+1; j
< NF
; j
++) {
1784 if (strcasecmp(mFiles
.valueAt(i
)->getLeaf().string(),
1785 mFiles
.valueAt(j
)->getLeaf().string()) == 0) {
1786 SourcePos(mFiles
.valueAt(i
)->getPrintableSource(), -1).error(
1787 "File is case-insensitive equivalent to: %s",
1788 mFiles
.valueAt(j
)->getPrintableSource().string());
1789 return UNKNOWN_ERROR
;
1792 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1793 // (this is mostly caught by the "marked" stuff, below)
1796 for (j
= 0; j
< ND
; j
++) {
1797 if (strcasecmp(mFiles
.valueAt(i
)->getLeaf().string(),
1798 mDirs
.valueAt(j
)->getLeaf().string()) == 0) {
1799 SourcePos(mFiles
.valueAt(i
)->getPrintableSource(), -1).error(
1800 "File conflicts with dir from: %s",
1801 mDirs
.valueAt(j
)->getPrintableSource().string());
1802 return UNKNOWN_ERROR
;
1807 for (i
= 0; i
< ND
; i
++) {
1808 if (!validateFileName(mDirs
.valueAt(i
)->getLeaf().string())) {
1809 SourcePos(mDirs
.valueAt(i
)->getPrintableSource(), -1).error(
1810 "Invalid directory name, unable to add.");
1811 return UNKNOWN_ERROR
;
1815 for (j
= i
+1; j
< ND
; j
++) {
1816 if (strcasecmp(mDirs
.valueAt(i
)->getLeaf().string(),
1817 mDirs
.valueAt(j
)->getLeaf().string()) == 0) {
1818 SourcePos(mDirs
.valueAt(i
)->getPrintableSource(), -1).error(
1819 "Directory is case-insensitive equivalent to: %s",
1820 mDirs
.valueAt(j
)->getPrintableSource().string());
1821 return UNKNOWN_ERROR
;
1825 status_t err
= mDirs
.valueAt(i
)->validate();
1826 if (err
!= NO_ERROR
) {
1834 void AaptDir::print(const String8
& prefix
) const
1836 const size_t ND
=getDirs().size();
1838 for (i
=0; i
<ND
; i
++) {
1839 getDirs().valueAt(i
)->print(prefix
);
1842 const size_t NF
=getFiles().size();
1843 for (i
=0; i
<NF
; i
++) {
1844 getFiles().valueAt(i
)->print(prefix
);
1848 String8
AaptDir::getPrintableSource() const
1850 if (mFiles
.size() > 0) {
1851 // Arbitrarily pull the first file out of the list as the source dir.
1852 return mFiles
.valueAt(0)->getPrintableSource().getPathDir();
1854 if (mDirs
.size() > 0) {
1855 // Or arbitrarily pull the first dir out of the list as the source dir.
1856 return mDirs
.valueAt(0)->getPrintableSource().getPathDir();
1859 // Should never hit this case, but to be safe...
1864 // =========================================================================
1865 // =========================================================================
1866 // =========================================================================
1868 AaptAssets::AaptAssets()
1869 : AaptDir(String8(), String8()),
1870 mChanged(false), mHaveIncludedAssets(false), mRes(NULL
)
1874 const SortedVector
<AaptGroupEntry
>& AaptAssets::getGroupEntries() const {
1877 return mGroupEntries
;
1880 status_t
AaptAssets::addFile(const String8
& name
, const sp
<AaptGroup
>& file
)
1883 return AaptDir::addFile(name
, file
);
1886 sp
<AaptFile
> AaptAssets::addFile(
1887 const String8
& filePath
, const AaptGroupEntry
& entry
,
1888 const String8
& srcDir
, sp
<AaptGroup
>* outGroup
,
1889 const String8
& resType
)
1891 sp
<AaptDir
> dir
= this;
1892 sp
<AaptGroup
> group
;
1894 String8 root
, remain(filePath
), partialPath
;
1895 while (remain
.length() > 0) {
1896 root
= remain
.walkPath(&remain
);
1897 partialPath
.appendPath(root
);
1899 const String8
rootStr(root
);
1901 if (remain
.length() == 0) {
1902 ssize_t i
= dir
->getFiles().indexOfKey(rootStr
);
1904 group
= dir
->getFiles().valueAt(i
);
1906 group
= new AaptGroup(rootStr
, filePath
);
1907 status_t res
= dir
->addFile(rootStr
, group
);
1908 if (res
!= NO_ERROR
) {
1912 file
= new AaptFile(srcDir
.appendPathCopy(filePath
), entry
, resType
);
1913 status_t res
= group
->addFile(file
);
1914 if (res
!= NO_ERROR
) {
1920 ssize_t i
= dir
->getDirs().indexOfKey(rootStr
);
1922 dir
= dir
->getDirs().valueAt(i
);
1924 sp
<AaptDir
> subdir
= new AaptDir(rootStr
, partialPath
);
1925 status_t res
= dir
->addDir(rootStr
, subdir
);
1926 if (res
!= NO_ERROR
) {
1934 mGroupEntries
.add(entry
);
1935 if (outGroup
) *outGroup
= group
;
1939 void AaptAssets::addResource(const String8
& leafName
, const String8
& path
,
1940 const sp
<AaptFile
>& file
, const String8
& resType
)
1942 sp
<AaptDir
> res
= AaptDir::makeDir(kResString
);
1943 String8 dirname
= file
->getGroupEntry().toDirName(resType
);
1944 sp
<AaptDir
> subdir
= res
->makeDir(dirname
);
1945 sp
<AaptGroup
> grr
= new AaptGroup(leafName
, path
);
1948 subdir
->addFile(leafName
, grr
);
1952 ssize_t
AaptAssets::slurpFromArgs(Bundle
* bundle
)
1957 const Vector
<const char *>& resDirs
= bundle
->getResourceSourceDirs();
1958 const size_t dirCount
=resDirs
.size();
1959 sp
<AaptAssets
> current
= this;
1961 const int N
= bundle
->getFileSpecCount();
1964 * If a package manifest was specified, include that first.
1966 if (bundle
->getAndroidManifestFile() != NULL
) {
1967 // place at root of zip.
1968 String8
srcFile(bundle
->getAndroidManifestFile());
1969 addFile(srcFile
.getPathLeaf(), AaptGroupEntry(), srcFile
.getPathDir(),
1975 * If a directory of custom assets was supplied, slurp 'em up.
1977 if (bundle
->getAssetSourceDir()) {
1978 const char* assetDir
= bundle
->getAssetSourceDir();
1980 FileType type
= getFileType(assetDir
);
1981 if (type
== kFileTypeNonexistent
) {
1982 fprintf(stderr
, "ERROR: asset directory '%s' does not exist\n", assetDir
);
1983 return UNKNOWN_ERROR
;
1985 if (type
!= kFileTypeDirectory
) {
1986 fprintf(stderr
, "ERROR: '%s' is not a directory\n", assetDir
);
1987 return UNKNOWN_ERROR
;
1990 String8
assetRoot(assetDir
);
1991 sp
<AaptDir
> assetAaptDir
= makeDir(String8(kAssetDir
));
1992 AaptGroupEntry group
;
1993 count
= assetAaptDir
->slurpFullTree(bundle
, assetRoot
, group
,
1994 String8(), mFullAssetPaths
);
2000 mGroupEntries
.add(group
);
2002 totalCount
+= count
;
2004 if (bundle
->getVerbose())
2005 printf("Found %d custom asset file%s in %s\n",
2006 count
, (count
==1) ? "" : "s", assetDir
);
2010 * If a directory of resource-specific assets was supplied, slurp 'em up.
2012 for (size_t i
=0; i
<dirCount
; i
++) {
2013 const char *res
= resDirs
[i
];
2015 type
= getFileType(res
);
2016 if (type
== kFileTypeNonexistent
) {
2017 fprintf(stderr
, "ERROR: resource directory '%s' does not exist\n", res
);
2018 return UNKNOWN_ERROR
;
2020 if (type
== kFileTypeDirectory
) {
2022 sp
<AaptAssets
> nextOverlay
= new AaptAssets();
2023 current
->setOverlay(nextOverlay
);
2024 current
= nextOverlay
;
2025 current
->setFullResPaths(mFullResPaths
);
2027 count
= current
->slurpResourceTree(bundle
, String8(res
));
2033 totalCount
+= count
;
2036 fprintf(stderr
, "ERROR: '%s' is not a directory\n", res
);
2037 return UNKNOWN_ERROR
;
2043 * Now do any additional raw files.
2045 for (int arg
=0; arg
<N
; arg
++) {
2046 const char* assetDir
= bundle
->getFileSpecEntry(arg
);
2048 FileType type
= getFileType(assetDir
);
2049 if (type
== kFileTypeNonexistent
) {
2050 fprintf(stderr
, "ERROR: input directory '%s' does not exist\n", assetDir
);
2051 return UNKNOWN_ERROR
;
2053 if (type
!= kFileTypeDirectory
) {
2054 fprintf(stderr
, "ERROR: '%s' is not a directory\n", assetDir
);
2055 return UNKNOWN_ERROR
;
2058 String8
assetRoot(assetDir
);
2060 if (bundle
->getVerbose())
2061 printf("Processing raw dir '%s'\n", (const char*) assetDir
);
2064 * Do a recursive traversal of subdir tree. We don't make any
2065 * guarantees about ordering, so we're okay with an inorder search
2066 * using whatever order the OS happens to hand back to us.
2068 count
= slurpFullTree(bundle
, assetRoot
, AaptGroupEntry(), String8(), mFullAssetPaths
);
2070 /* failure; report error and remove archive */
2074 totalCount
+= count
;
2076 if (bundle
->getVerbose())
2077 printf("Found %d asset file%s in %s\n",
2078 count
, (count
==1) ? "" : "s", assetDir
);
2082 if (count
!= NO_ERROR
) {
2087 count
= filter(bundle
);
2088 if (count
!= NO_ERROR
) {
2097 ssize_t
AaptAssets::slurpFullTree(Bundle
* bundle
, const String8
& srcDir
,
2098 const AaptGroupEntry
& kind
,
2099 const String8
& resType
,
2100 sp
<FilePathStore
>& fullResPaths
)
2102 ssize_t res
= AaptDir::slurpFullTree(bundle
, srcDir
, kind
, resType
, fullResPaths
);
2104 mGroupEntries
.add(kind
);
2110 ssize_t
AaptAssets::slurpResourceTree(Bundle
* bundle
, const String8
& srcDir
)
2114 DIR* dir
= opendir(srcDir
.string());
2116 fprintf(stderr
, "ERROR: opendir(%s): %s\n", srcDir
.string(), strerror(errno
));
2117 return UNKNOWN_ERROR
;
2123 * Run through the directory, looking for dirs that match the
2127 struct dirent
* entry
= readdir(dir
);
2128 if (entry
== NULL
) {
2132 if (isHidden(srcDir
.string(), entry
->d_name
)) {
2136 String8
subdirName(srcDir
);
2137 subdirName
.appendPath(entry
->d_name
);
2139 AaptGroupEntry group
;
2141 bool b
= group
.initFromDirName(entry
->d_name
, &resType
);
2143 fprintf(stderr
, "invalid resource directory name: %s/%s\n", srcDir
.string(),
2149 if (bundle
->getMaxResVersion() != NULL
&& group
.getVersionString().length() != 0) {
2150 int maxResInt
= atoi(bundle
->getMaxResVersion());
2151 const char *verString
= group
.getVersionString().string();
2152 int dirVersionInt
= atoi(verString
+ 1); // skip 'v' in version name
2153 if (dirVersionInt
> maxResInt
) {
2154 fprintf(stderr
, "max res %d, skipping %s\n", maxResInt
, entry
->d_name
);
2159 FileType type
= getFileType(subdirName
.string());
2161 if (type
== kFileTypeDirectory
) {
2162 sp
<AaptDir
> dir
= makeDir(resType
);
2163 ssize_t res
= dir
->slurpFullTree(bundle
, subdirName
, group
,
2164 resType
, mFullResPaths
);
2170 mGroupEntries
.add(group
);
2174 // Only add this directory if we don't already have a resource dir
2175 // for the current type. This ensures that we only add the dir once
2177 sp
<AaptDir
> rdir
= resDir(resType
);
2182 if (bundle
->getVerbose()) {
2183 fprintf(stderr
, " (ignoring file '%s')\n", subdirName
.string());
2199 AaptAssets::slurpResourceZip(Bundle
* bundle
, const char* filename
)
2202 SortedVector
<AaptGroupEntry
> entries
;
2204 ZipFile
* zip
= new ZipFile
;
2205 status_t err
= zip
->open(filename
, ZipFile::kOpenReadOnly
);
2206 if (err
!= NO_ERROR
) {
2207 fprintf(stderr
, "error opening zip file %s\n", filename
);
2213 const int N
= zip
->getNumEntries();
2214 for (int i
=0; i
<N
; i
++) {
2215 ZipEntry
* entry
= zip
->getEntryByIndex(i
);
2216 if (entry
->getDeleted()) {
2220 String8
entryName(entry
->getFileName());
2222 String8 dirName
= entryName
.getPathDir();
2223 sp
<AaptDir
> dir
= dirName
== "" ? this : makeDir(dirName
);
2226 AaptGroupEntry kind
;
2229 if (entryName
.walkPath(&remain
) == kResourceDir
) {
2230 // these are the resources, pull their type out of the directory name
2231 kind
.initFromDirName(remain
.walkPath().string(), &resType
);
2233 // these are untyped and don't have an AaptGroupEntry
2235 if (entries
.indexOf(kind
) < 0) {
2237 mGroupEntries
.add(kind
);
2240 // use the one from the zip file if they both exist.
2241 dir
->removeFile(entryName
.getPathLeaf());
2243 sp
<AaptFile
> file
= new AaptFile(entryName
, kind
, resType
);
2244 status_t err
= dir
->addLeafFile(entryName
.getPathLeaf(), file
);
2245 if (err
!= NO_ERROR
) {
2246 fprintf(stderr
, "err=%s entryName=%s\n", strerror(err
), entryName
.string());
2250 file
->setCompressionMethod(entry
->getCompressionMethod());
2253 if (entryName
== "AndroidManifest.xml") {
2254 printf("AndroidManifest.xml\n");
2256 printf("\n\nfile: %s\n", entryName
.string());
2259 size_t len
= entry
->getUncompressedLen();
2260 void* data
= zip
->uncompress(entry
);
2261 void* buf
= file
->editData(len
);
2262 memcpy(buf
, data
, len
);
2266 const unsigned char* p
= (unsigned char*)data
;
2267 const unsigned char* end
= p
+len
;
2269 for (int i
=0; i
<32 && p
< end
; i
++) {
2270 printf("0x%03x ", i
*0x10 + OFF
);
2271 for (int j
=0; j
<0x10 && p
< end
; j
++) {
2272 printf(" %02x", *p
);
2289 status_t
AaptAssets::filter(Bundle
* bundle
)
2291 ResourceFilter reqFilter
;
2292 status_t err
= reqFilter
.parse(bundle
->getConfigurations());
2293 if (err
!= NO_ERROR
) {
2297 ResourceFilter prefFilter
;
2298 err
= prefFilter
.parse(bundle
->getPreferredConfigurations());
2299 if (err
!= NO_ERROR
) {
2303 if (reqFilter
.isEmpty() && prefFilter
.isEmpty()) {
2307 if (bundle
->getVerbose()) {
2308 if (!reqFilter
.isEmpty()) {
2309 printf("Applying required filter: %s\n",
2310 bundle
->getConfigurations());
2312 if (!prefFilter
.isEmpty()) {
2313 printf("Applying preferred filter: %s\n",
2314 bundle
->getPreferredConfigurations());
2318 const Vector
<sp
<AaptDir
> >& resdirs
= mResDirs
;
2319 const size_t ND
= resdirs
.size();
2320 for (size_t i
=0; i
<ND
; i
++) {
2321 const sp
<AaptDir
>& dir
= resdirs
.itemAt(i
);
2322 if (dir
->getLeaf() == kValuesDir
) {
2323 // The "value" dir is special since a single file defines
2324 // multiple resources, so we can not do filtering on the
2325 // files themselves.
2328 if (dir
->getLeaf() == kMipmapDir
) {
2329 // We also skip the "mipmap" directory, since the point of this
2330 // is to include all densities without stripping. If you put
2331 // other configurations in here as well they won't be stripped
2332 // either... So don't do that. Seriously. What is wrong with you?
2336 const size_t NG
= dir
->getFiles().size();
2337 for (size_t j
=0; j
<NG
; j
++) {
2338 sp
<AaptGroup
> grp
= dir
->getFiles().valueAt(j
);
2340 // First remove any configurations we know we don't need.
2341 for (size_t k
=0; k
<grp
->getFiles().size(); k
++) {
2342 sp
<AaptFile
> file
= grp
->getFiles().valueAt(k
);
2343 if (k
== 0 && grp
->getFiles().size() == 1) {
2344 // If this is the only file left, we need to keep it.
2345 // Otherwise the resource IDs we are using will be inconsistent
2346 // with what we get when not stripping. Sucky, but at least
2347 // for now we can rely on the back-end doing another filtering
2348 // pass to take this out and leave us with this resource name
2349 // containing no entries.
2352 if (file
->getPath().getPathExtension() == ".xml") {
2353 // We can't remove .xml files at this point, because when
2354 // we parse them they may add identifier resources, so
2355 // removing them can cause our resource identifiers to
2356 // become inconsistent.
2359 const ResTable_config
& config(file
->getGroupEntry().toParams());
2360 if (!reqFilter
.match(config
)) {
2361 if (bundle
->getVerbose()) {
2362 printf("Pruning unneeded resource: %s\n",
2363 file
->getPrintableSource().string());
2370 // Quick check: no preferred filters, nothing more to do.
2371 if (prefFilter
.isEmpty()) {
2375 // Now deal with preferred configurations.
2376 for (int axis
=AXIS_START
; axis
<=AXIS_END
; axis
++) {
2377 for (size_t k
=0; k
<grp
->getFiles().size(); k
++) {
2378 sp
<AaptFile
> file
= grp
->getFiles().valueAt(k
);
2379 if (k
== 0 && grp
->getFiles().size() == 1) {
2380 // If this is the only file left, we need to keep it.
2381 // Otherwise the resource IDs we are using will be inconsistent
2382 // with what we get when not stripping. Sucky, but at least
2383 // for now we can rely on the back-end doing another filtering
2384 // pass to take this out and leave us with this resource name
2385 // containing no entries.
2388 if (file
->getPath().getPathExtension() == ".xml") {
2389 // We can't remove .xml files at this point, because when
2390 // we parse them they may add identifier resources, so
2391 // removing them can cause our resource identifiers to
2392 // become inconsistent.
2395 const ResTable_config
& config(file
->getGroupEntry().toParams());
2396 if (!prefFilter
.match(axis
, config
)) {
2397 // This is a resource we would prefer not to have. Check
2398 // to see if have a similar variation that we would like
2399 // to have and, if so, we can drop it.
2400 for (size_t m
=0; m
<grp
->getFiles().size(); m
++) {
2401 if (m
== k
) continue;
2402 sp
<AaptFile
> mfile
= grp
->getFiles().valueAt(m
);
2403 const ResTable_config
& mconfig(mfile
->getGroupEntry().toParams());
2404 if (AaptGroupEntry::configSameExcept(config
, mconfig
, axis
)) {
2405 if (prefFilter
.match(axis
, mconfig
)) {
2406 if (bundle
->getVerbose()) {
2407 printf("Pruning unneeded resource: %s\n",
2408 file
->getPrintableSource().string());
2425 sp
<AaptSymbols
> AaptAssets::getSymbolsFor(const String8
& name
)
2427 sp
<AaptSymbols
> sym
= mSymbols
.valueFor(name
);
2429 sym
= new AaptSymbols();
2430 mSymbols
.add(name
, sym
);
2435 status_t
AaptAssets::buildIncludedResources(Bundle
* bundle
)
2437 if (!mHaveIncludedAssets
) {
2438 // Add in all includes.
2439 const Vector
<const char*>& incl
= bundle
->getPackageIncludes();
2440 const size_t N
=incl
.size();
2441 for (size_t i
=0; i
<N
; i
++) {
2442 if (bundle
->getVerbose())
2443 printf("Including resources from package: %s\n", incl
[i
]);
2444 if (!mIncludedAssets
.addAssetPath(String8(incl
[i
]), NULL
)) {
2445 fprintf(stderr
, "ERROR: Asset package include '%s' not found.\n",
2447 return UNKNOWN_ERROR
;
2450 mHaveIncludedAssets
= true;
2456 status_t
AaptAssets::addIncludedResources(const sp
<AaptFile
>& file
)
2458 const ResTable
& res
= getIncludedResources();
2460 return const_cast<ResTable
&>(res
).add(file
->getData(), file
->getSize(), NULL
);
2463 const ResTable
& AaptAssets::getIncludedResources() const
2465 return mIncludedAssets
.getResources(false);
2468 void AaptAssets::print(const String8
& prefix
) const
2470 String8
innerPrefix(prefix
);
2471 innerPrefix
.append(" ");
2472 String8
innerInnerPrefix(innerPrefix
);
2473 innerInnerPrefix
.append(" ");
2474 printf("%sConfigurations:\n", prefix
.string());
2475 const size_t N
=mGroupEntries
.size();
2476 for (size_t i
=0; i
<N
; i
++) {
2477 String8 cname
= mGroupEntries
.itemAt(i
).toDirName(String8());
2478 printf("%s %s\n", prefix
.string(),
2479 cname
!= "" ? cname
.string() : "(default)");
2482 printf("\n%sFiles:\n", prefix
.string());
2483 AaptDir::print(innerPrefix
);
2485 printf("\n%sResource Dirs:\n", prefix
.string());
2486 const Vector
<sp
<AaptDir
> >& resdirs
= mResDirs
;
2487 const size_t NR
= resdirs
.size();
2488 for (size_t i
=0; i
<NR
; i
++) {
2489 const sp
<AaptDir
>& d
= resdirs
.itemAt(i
);
2490 printf("%s Type %s\n", prefix
.string(), d
->getLeaf().string());
2491 d
->print(innerInnerPrefix
);
2495 sp
<AaptDir
> AaptAssets::resDir(const String8
& name
) const
2497 const Vector
<sp
<AaptDir
> >& resdirs
= mResDirs
;
2498 const size_t N
= resdirs
.size();
2499 for (size_t i
=0; i
<N
; i
++) {
2500 const sp
<AaptDir
>& d
= resdirs
.itemAt(i
);
2501 if (d
->getLeaf() == name
) {
2509 valid_symbol_name(const String8
& symbol
)
2511 static char const * const KEYWORDS
[] = {
2512 "abstract", "assert", "boolean", "break",
2513 "byte", "case", "catch", "char", "class", "const", "continue",
2514 "default", "do", "double", "else", "enum", "extends", "final",
2515 "finally", "float", "for", "goto", "if", "implements", "import",
2516 "instanceof", "int", "interface", "long", "native", "new", "package",
2517 "private", "protected", "public", "return", "short", "static",
2518 "strictfp", "super", "switch", "synchronized", "this", "throw",
2519 "throws", "transient", "try", "void", "volatile", "while",
2520 "true", "false", "null",
2523 const char*const* k
= KEYWORDS
;
2524 const char*const s
= symbol
.string();
2526 if (0 == strcmp(s
, *k
)) {