blob: 82bcfc2c1a8bfb800f28df48522437d3f3962a45 [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4
5#include "AaptAssets.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07006#include "AaptConfig.h"
7#include "AaptUtil.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08008#include "Main.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07009#include "ResourceFilter.h"
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +000010#include "Utils.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080011
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +000012#include <androidfw/PathUtils.h>
Adam Lesinski282e1812014-01-23 18:17:42 -080013#include <utils/misc.h>
14#include <utils/SortedVector.h>
15
16#include <ctype.h>
17#include <dirent.h>
18#include <errno.h>
19
Adam Lesinski282e1812014-01-23 18:17:42 -080020static const char* kAssetDir = "assets";
21static const char* kResourceDir = "res";
22static const char* kValuesDir = "values";
23static const char* kMipmapDir = "mipmap";
24static const char* kInvalidChars = "/\\:";
25static const size_t kMaxAssetFileName = 100;
26
27static const String8 kResString(kResourceDir);
28
29/*
30 * Names of asset files must meet the following criteria:
31 *
32 * - the filename length must be less than kMaxAssetFileName bytes long
33 * (and can't be empty)
34 * - all characters must be 7-bit printable ASCII
35 * - none of { '/' '\\' ':' }
36 *
37 * Pass in just the filename, not the full path.
38 */
39static bool validateFileName(const char* fileName)
40{
41 const char* cp = fileName;
42 size_t len = 0;
43
44 while (*cp != '\0') {
45 if ((*cp & 0x80) != 0)
46 return false; // reject high ASCII
47 if (*cp < 0x20 || *cp >= 0x7f)
48 return false; // reject control chars and 0x7f
49 if (strchr(kInvalidChars, *cp) != NULL)
50 return false; // reject path sep chars
51 cp++;
52 len++;
53 }
54
55 if (len < 1 || len > kMaxAssetFileName)
56 return false; // reject empty or too long
57
58 return true;
59}
60
61// The default to use if no other ignore pattern is defined.
62const char * const gDefaultIgnoreAssets =
63 "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
64// The ignore pattern that can be passed via --ignore-assets in Main.cpp
65const char * gUserIgnoreAssets = NULL;
66
67static bool isHidden(const char *root, const char *path)
68{
69 // Patterns syntax:
70 // - Delimiter is :
71 // - Entry can start with the flag ! to avoid printing a warning
72 // about the file being ignored.
73 // - Entry can have the flag "<dir>" to match only directories
74 // or <file> to match only files. Default is to match both.
75 // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
76 // where prefix/suffix must have at least 1 character (so that
77 // we don't match a '*' catch-all pattern.)
78 // - The special filenames "." and ".." are always ignored.
79 // - Otherwise the full string is matched.
80 // - match is not case-sensitive.
81
82 if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
83 return true;
84 }
85
86 const char *delim = ":";
87 const char *p = gUserIgnoreAssets;
88 if (!p || !p[0]) {
89 p = getenv("ANDROID_AAPT_IGNORE");
90 }
91 if (!p || !p[0]) {
92 p = gDefaultIgnoreAssets;
93 }
94 char *patterns = strdup(p);
95
96 bool ignore = false;
97 bool chatty = true;
98 char *matchedPattern = NULL;
99
100 String8 fullPath(root);
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000101 appendPath(fullPath, String8(path));
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000102 FileType type = getFileType(fullPath.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800103
104 int plen = strlen(path);
105
106 // Note: we don't have strtok_r under mingw.
107 for(char *token = strtok(patterns, delim);
108 !ignore && token != NULL;
109 token = strtok(NULL, delim)) {
110 chatty = token[0] != '!';
111 if (!chatty) token++; // skip !
112 if (strncasecmp(token, "<dir>" , 5) == 0) {
113 if (type != kFileTypeDirectory) continue;
114 token += 5;
115 }
116 if (strncasecmp(token, "<file>", 6) == 0) {
117 if (type != kFileTypeRegular) continue;
118 token += 6;
119 }
120
121 matchedPattern = token;
122 int n = strlen(token);
123
124 if (token[0] == '*') {
125 // Match *suffix
126 token++;
127 n--;
128 if (n <= plen) {
129 ignore = strncasecmp(token, path + plen - n, n) == 0;
130 }
131 } else if (n > 1 && token[n - 1] == '*') {
132 // Match prefix*
133 ignore = strncasecmp(token, path, n - 1) == 0;
134 } else {
135 ignore = strcasecmp(token, path) == 0;
136 }
137 }
138
139 if (ignore && chatty) {
140 fprintf(stderr, " (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
141 type == kFileTypeDirectory ? "dir" : "file",
142 path,
143 matchedPattern ? matchedPattern : "");
144 }
145
146 free(patterns);
147 return ignore;
148}
149
150// =========================================================================
151// =========================================================================
152// =========================================================================
153
Narayan Kamath91447d82014-01-21 15:32:36 +0000154/* static */
155inline bool isAlpha(const String8& string) {
156 const size_t length = string.length();
157 for (size_t i = 0; i < length; ++i) {
158 if (!isalpha(string[i])) {
159 return false;
160 }
161 }
162
163 return true;
164}
165
166/* static */
167inline bool isNumber(const String8& string) {
168 const size_t length = string.length();
169 for (size_t i = 0; i < length; ++i) {
170 if (!isdigit(string[i])) {
171 return false;
172 }
173 }
174
175 return true;
176}
177
178void AaptLocaleValue::setLanguage(const char* languageChars) {
179 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700180 while ((*languageChars) != '\0' && i < sizeof(language)/sizeof(language[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000181 language[i++] = tolower(*languageChars);
182 languageChars++;
183 }
184}
185
186void AaptLocaleValue::setRegion(const char* regionChars) {
187 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700188 while ((*regionChars) != '\0' && i < sizeof(region)/sizeof(region[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000189 region[i++] = toupper(*regionChars);
190 regionChars++;
191 }
192}
193
194void AaptLocaleValue::setScript(const char* scriptChars) {
195 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700196 while ((*scriptChars) != '\0' && i < sizeof(script)/sizeof(script[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000197 if (i == 0) {
198 script[i++] = toupper(*scriptChars);
199 } else {
200 script[i++] = tolower(*scriptChars);
201 }
202 scriptChars++;
203 }
204}
205
206void AaptLocaleValue::setVariant(const char* variantChars) {
207 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700208 while ((*variantChars) != '\0' && i < sizeof(variant)/sizeof(variant[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000209 variant[i++] = *variantChars;
210 variantChars++;
211 }
212}
213
214bool AaptLocaleValue::initFromFilterString(const String8& str) {
215 // A locale (as specified in the filter) is an underscore separated name such
216 // as "en_US", "en_Latn_US", or "en_US_POSIX".
Adam Lesinskifab50872014-04-16 14:40:42 -0700217 Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_');
Narayan Kamath91447d82014-01-21 15:32:36 +0000218
219 const int numTags = parts.size();
220 bool valid = false;
221 if (numTags >= 1) {
222 const String8& lang = parts[0];
223 if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000224 setLanguage(lang.c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000225 valid = true;
226 }
227 }
228
229 if (!valid || numTags == 1) {
230 return valid;
231 }
232
233 // At this point, valid == true && numTags > 1.
234 const String8& part2 = parts[1];
235 if ((part2.length() == 2 && isAlpha(part2)) ||
236 (part2.length() == 3 && isNumber(part2))) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000237 setRegion(part2.c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000238 } else if (part2.length() == 4 && isAlpha(part2)) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000239 setScript(part2.c_str());
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800240 } else if (part2.length() >= 4 && part2.length() <= 8) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000241 setVariant(part2.c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000242 } else {
243 valid = false;
244 }
245
246 if (!valid || numTags == 2) {
247 return valid;
248 }
249
250 // At this point, valid == true && numTags > 1.
251 const String8& part3 = parts[2];
252 if (((part3.length() == 2 && isAlpha(part3)) ||
253 (part3.length() == 3 && isNumber(part3))) && script[0]) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000254 setRegion(part3.c_str());
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800255 } else if (part3.length() >= 4 && part3.length() <= 8) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000256 setVariant(part3.c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000257 } else {
258 valid = false;
259 }
260
261 if (!valid || numTags == 3) {
262 return valid;
263 }
264
265 const String8& part4 = parts[3];
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800266 if (part4.length() >= 4 && part4.length() <= 8) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000267 setVariant(part4.c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000268 } else {
269 valid = false;
270 }
271
272 if (!valid || numTags > 4) {
273 return false;
274 }
275
276 return true;
277}
278
279int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
280 const int size = parts.size();
281 int currentIndex = startIndex;
282
283 String8 part = parts[currentIndex];
284 if (part[0] == 'b' && part[1] == '+') {
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800285 // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
Narayan Kamath91447d82014-01-21 15:32:36 +0000286 // except that the separator is "+" and not "-".
Adam Lesinskifab50872014-04-16 14:40:42 -0700287 Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+');
Narayan Kamath91447d82014-01-21 15:32:36 +0000288 subtags.removeItemsAt(0);
289 if (subtags.size() == 1) {
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000290 setLanguage(subtags[0].c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000291 } else if (subtags.size() == 2) {
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000292 setLanguage(subtags[0].c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000293
294 // The second tag can either be a region, a variant or a script.
295 switch (subtags[1].size()) {
296 case 2:
297 case 3:
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000298 setRegion(subtags[1].c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000299 break;
300 case 4:
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800301 if (isAlpha(subtags[1])) {
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000302 setScript(subtags[1].c_str());
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800303 break;
304 }
305 // This is not alphabetical, so we fall through to variant
Stephen Hinesd17f06d2019-05-08 16:37:20 -0700306 [[fallthrough]];
Narayan Kamath91447d82014-01-21 15:32:36 +0000307 case 5:
308 case 6:
309 case 7:
310 case 8:
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000311 setVariant(subtags[1].c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000312 break;
313 default:
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800314 fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000315 part.c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000316 return -1;
317 }
318 } else if (subtags.size() == 3) {
319 // The language is always the first subtag.
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000320 setLanguage(subtags[0].c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000321
322 // The second subtag can either be a script or a region code.
323 // If its size is 4, it's a script code, else it's a region code.
Narayan Kamath91447d82014-01-21 15:32:36 +0000324 if (subtags[1].size() == 4) {
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000325 setScript(subtags[1].c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000326 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000327 setRegion(subtags[1].c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000328 } else {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000329 fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n", part.c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000330 return -1;
331 }
332
333 // The third tag can either be a region code (if the second tag was
334 // a script), else a variant code.
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800335 if (subtags[2].size() >= 4) {
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000336 setVariant(subtags[2].c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000337 } else {
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000338 setRegion(subtags[2].c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000339 }
340 } else if (subtags.size() == 4) {
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000341 setLanguage(subtags[0].c_str());
342 setScript(subtags[1].c_str());
343 setRegion(subtags[2].c_str());
344 setVariant(subtags[3].c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000345 } else {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000346 fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name: %s\n", part.c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000347 return -1;
348 }
349
350 return ++currentIndex;
351 } else {
Narayan Kamath7f1a8952015-02-10 16:11:55 +0000352 if ((part.length() == 2 || part.length() == 3)
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000353 && isAlpha(part) && strcmp("car", part.c_str())) {
Tomasz Wasilczyk835dfe52023-08-17 16:27:22 +0000354 setLanguage(part.c_str());
Narayan Kamath91447d82014-01-21 15:32:36 +0000355 if (++currentIndex == size) {
356 return size;
357 }
358 } else {
359 return currentIndex;
360 }
361
362 part = parts[currentIndex];
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000363 if (part.c_str()[0] == 'r' && part.length() == 3) {
364 setRegion(part.c_str() + 1);
Narayan Kamath91447d82014-01-21 15:32:36 +0000365 if (++currentIndex == size) {
366 return size;
367 }
368 }
369 }
370
371 return currentIndex;
372}
373
Narayan Kamath91447d82014-01-21 15:32:36 +0000374void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
375 config.unpackLanguage(language);
376 config.unpackRegion(region);
Roozbeh Pournader79608982016-03-03 15:06:46 -0800377 if (config.localeScript[0] && !config.localeScriptWasComputed) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000378 memcpy(script, config.localeScript, sizeof(config.localeScript));
379 }
380
381 if (config.localeVariant[0]) {
382 memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
383 }
384}
385
386void AaptLocaleValue::writeTo(ResTable_config* out) const {
387 out->packLanguage(language);
388 out->packRegion(region);
389
390 if (script[0]) {
391 memcpy(out->localeScript, script, sizeof(out->localeScript));
392 }
393
394 if (variant[0]) {
395 memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
396 }
397}
398
Adam Lesinski282e1812014-01-23 18:17:42 -0800399bool
400AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
401{
Adam Lesinskifab50872014-04-16 14:40:42 -0700402 const char* q = strchr(dir, '-');
403 size_t typeLen;
404 if (q != NULL) {
405 typeLen = q - dir;
406 } else {
407 typeLen = strlen(dir);
408 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800409
Adam Lesinskifab50872014-04-16 14:40:42 -0700410 String8 type(dir, typeLen);
411 if (!isValidResourceType(type)) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800412 return false;
413 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800414
Adam Lesinskifab50872014-04-16 14:40:42 -0700415 if (q != NULL) {
416 if (!AaptConfig::parse(String8(q + 1), &mParams)) {
417 return false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800418 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800419 }
420
Adam Lesinskifab50872014-04-16 14:40:42 -0700421 *resType = type;
Adam Lesinski282e1812014-01-23 18:17:42 -0800422 return true;
423}
424
425String8
Adam Lesinski282e1812014-01-23 18:17:42 -0800426AaptGroupEntry::toDirName(const String8& resType) const
427{
428 String8 s = resType;
Adam Lesinskifab50872014-04-16 14:40:42 -0700429 String8 params = mParams.toString();
430 if (params.length() > 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800431 if (s.length() > 0) {
432 s += "-";
433 }
Adam Lesinskifab50872014-04-16 14:40:42 -0700434 s += params;
Adam Lesinski282e1812014-01-23 18:17:42 -0800435 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800436 return s;
437}
438
Adam Lesinski282e1812014-01-23 18:17:42 -0800439
440// =========================================================================
441// =========================================================================
442// =========================================================================
443
444void* AaptFile::editData(size_t size)
445{
446 if (size <= mBufferSize) {
447 mDataSize = size;
448 return mData;
449 }
450 size_t allocSize = (size*3)/2;
451 void* buf = realloc(mData, allocSize);
452 if (buf == NULL) {
453 return NULL;
454 }
455 mData = buf;
456 mDataSize = size;
457 mBufferSize = allocSize;
458 return buf;
459}
460
Adam Lesinskide898ff2014-01-29 18:20:45 -0800461void* AaptFile::editDataInRange(size_t offset, size_t size)
462{
463 return (void*)(((uint8_t*) editData(offset + size)) + offset);
464}
465
Adam Lesinski282e1812014-01-23 18:17:42 -0800466void* AaptFile::editData(size_t* outSize)
467{
468 if (outSize) {
469 *outSize = mDataSize;
470 }
471 return mData;
472}
473
474void* AaptFile::padData(size_t wordSize)
475{
476 const size_t extra = mDataSize%wordSize;
477 if (extra == 0) {
478 return mData;
479 }
480
481 size_t initial = mDataSize;
482 void* data = editData(initial+(wordSize-extra));
483 if (data != NULL) {
484 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
485 }
486 return data;
487}
488
489status_t AaptFile::writeData(const void* data, size_t size)
490{
491 size_t end = mDataSize;
492 size_t total = size + end;
493 void* buf = editData(total);
494 if (buf == NULL) {
495 return UNKNOWN_ERROR;
496 }
497 memcpy(((char*)buf)+end, data, size);
498 return NO_ERROR;
499}
500
501void AaptFile::clearData()
502{
503 if (mData != NULL) free(mData);
504 mData = NULL;
505 mDataSize = 0;
506 mBufferSize = 0;
507}
508
509String8 AaptFile::getPrintableSource() const
510{
511 if (hasData()) {
512 String8 name(mGroupEntry.toDirName(String8()));
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000513 appendPath(name, mPath);
Adam Lesinski282e1812014-01-23 18:17:42 -0800514 name.append(" #generated");
515 return name;
516 }
517 return mSourceFile;
518}
519
520// =========================================================================
521// =========================================================================
522// =========================================================================
523
Adam Lesinski09384302014-01-22 16:07:42 -0800524status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
Adam Lesinski282e1812014-01-23 18:17:42 -0800525{
Adam Lesinski09384302014-01-22 16:07:42 -0800526 ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
527 if (index >= 0 && overwriteDuplicate) {
528 fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000529 mFiles[index]->getSourceFile().c_str(),
530 file->getSourceFile().c_str());
Adam Lesinski09384302014-01-22 16:07:42 -0800531 removeFile(index);
532 index = -1;
533 }
534
535 if (index < 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800536 file->mPath = mPath;
537 mFiles.add(file->getGroupEntry(), file);
538 return NO_ERROR;
539 }
540
Adam Lesinski48f05d22014-05-12 22:13:02 -0700541 // Check if the version is automatically applied. This is a common source of
542 // error.
543 ConfigDescription withoutVersion = file->getGroupEntry().toParams();
544 withoutVersion.version = 0;
545 AaptConfig::applyVersionForCompatibility(&withoutVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -0800546
Adam Lesinski48f05d22014-05-12 22:13:02 -0700547 const sp<AaptFile>& originalFile = mFiles.valueAt(index);
548 SourcePos(file->getSourceFile(), -1)
549 .error("Duplicate file.\n%s: Original is here. %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000550 originalFile->getPrintableSource().c_str(),
Adam Lesinski48f05d22014-05-12 22:13:02 -0700551 (withoutVersion.version != 0) ? "The version qualifier may be implied." : "");
Adam Lesinski282e1812014-01-23 18:17:42 -0800552 return UNKNOWN_ERROR;
553}
554
555void AaptGroup::removeFile(size_t index)
556{
557 mFiles.removeItemsAt(index);
558}
559
560void AaptGroup::print(const String8& prefix) const
561{
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000562 printf("%s%s\n", prefix.c_str(), getPath().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800563 const size_t N=mFiles.size();
564 size_t i;
565 for (i=0; i<N; i++) {
566 sp<AaptFile> file = mFiles.valueAt(i);
567 const AaptGroupEntry& e = file->getGroupEntry();
568 if (file->hasData()) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000569 printf("%s Gen: (%s) %d bytes\n", prefix.c_str(), e.toDirName(String8()).c_str(),
Adam Lesinski282e1812014-01-23 18:17:42 -0800570 (int)file->getSize());
571 } else {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000572 printf("%s Src: (%s) %s\n", prefix.c_str(), e.toDirName(String8()).c_str(),
573 file->getPrintableSource().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800574 }
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000575 //printf("%s File Group Entry: %s\n", prefix.c_str(),
576 // file->getGroupEntry().toDirName(String8()).c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800577 }
578}
579
580String8 AaptGroup::getPrintableSource() const
581{
582 if (mFiles.size() > 0) {
583 // Arbitrarily pull the first source file out of the list.
584 return mFiles.valueAt(0)->getPrintableSource();
585 }
586
587 // Should never hit this case, but to be safe...
588 return getPath();
589
590}
591
592// =========================================================================
593// =========================================================================
594// =========================================================================
595
596status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
597{
598 if (mFiles.indexOfKey(name) >= 0) {
599 return ALREADY_EXISTS;
600 }
601 mFiles.add(name, file);
602 return NO_ERROR;
603}
604
605status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
606{
607 if (mDirs.indexOfKey(name) >= 0) {
608 return ALREADY_EXISTS;
609 }
610 mDirs.add(name, dir);
611 return NO_ERROR;
612}
613
614sp<AaptDir> AaptDir::makeDir(const String8& path)
615{
616 String8 name;
617 String8 remain = path;
618
619 sp<AaptDir> subdir = this;
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000620 while (name = walkPath(remain, &remain), remain != "") {
Adam Lesinski282e1812014-01-23 18:17:42 -0800621 subdir = subdir->makeDir(name);
622 }
623
624 ssize_t i = subdir->mDirs.indexOfKey(name);
625 if (i >= 0) {
626 return subdir->mDirs.valueAt(i);
627 }
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000628 sp<AaptDir> dir = new AaptDir(name, appendPathCopy(subdir->mPath, name));
Adam Lesinski282e1812014-01-23 18:17:42 -0800629 subdir->mDirs.add(name, dir);
630 return dir;
631}
632
633void AaptDir::removeFile(const String8& name)
634{
635 mFiles.removeItem(name);
636}
637
638void AaptDir::removeDir(const String8& name)
639{
640 mDirs.removeItem(name);
641}
642
Adam Lesinski09384302014-01-22 16:07:42 -0800643status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
644 const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -0800645{
646 sp<AaptGroup> group;
647 if (mFiles.indexOfKey(leafName) >= 0) {
648 group = mFiles.valueFor(leafName);
649 } else {
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000650 group = new AaptGroup(leafName, appendPathCopy(mPath, leafName));
Adam Lesinski282e1812014-01-23 18:17:42 -0800651 mFiles.add(leafName, group);
652 }
653
Adam Lesinski09384302014-01-22 16:07:42 -0800654 return group->addFile(file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800655}
656
657ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
658 const AaptGroupEntry& kind, const String8& resType,
Adam Lesinski09384302014-01-22 16:07:42 -0800659 sp<FilePathStore>& fullResPaths, const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -0800660{
661 Vector<String8> fileNames;
662 {
663 DIR* dir = NULL;
664
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000665 dir = opendir(srcDir.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800666 if (dir == NULL) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000667 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.c_str(), strerror(errno));
Adam Lesinski282e1812014-01-23 18:17:42 -0800668 return UNKNOWN_ERROR;
669 }
670
671 /*
672 * Slurp the filenames out of the directory.
673 */
674 while (1) {
675 struct dirent* entry;
676
677 entry = readdir(dir);
678 if (entry == NULL)
679 break;
680
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000681 if (isHidden(srcDir.c_str(), entry->d_name))
Adam Lesinski282e1812014-01-23 18:17:42 -0800682 continue;
683
684 String8 name(entry->d_name);
685 fileNames.add(name);
686 // Add fully qualified path for dependency purposes
687 // if we're collecting them
688 if (fullResPaths != NULL) {
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000689 fullResPaths->add(appendPathCopy(srcDir, name));
Adam Lesinski282e1812014-01-23 18:17:42 -0800690 }
691 }
692 closedir(dir);
693 }
694
695 ssize_t count = 0;
696
697 /*
698 * Stash away the files and recursively descend into subdirectories.
699 */
700 const size_t N = fileNames.size();
701 size_t i;
702 for (i = 0; i < N; i++) {
703 String8 pathName(srcDir);
704 FileType type;
705
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000706 appendPath(pathName, fileNames[i]);
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000707 type = getFileType(pathName.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800708 if (type == kFileTypeDirectory) {
709 sp<AaptDir> subdir;
710 bool notAdded = false;
711 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
712 subdir = mDirs.valueFor(fileNames[i]);
713 } else {
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000714 subdir = new AaptDir(fileNames[i], appendPathCopy(mPath, fileNames[i]));
Adam Lesinski282e1812014-01-23 18:17:42 -0800715 notAdded = true;
716 }
717 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Adam Lesinski09384302014-01-22 16:07:42 -0800718 resType, fullResPaths, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800719 if (res < NO_ERROR) {
720 return res;
721 }
722 if (res > 0 && notAdded) {
723 mDirs.add(fileNames[i], subdir);
724 }
725 count += res;
726 } else if (type == kFileTypeRegular) {
727 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
Adam Lesinski09384302014-01-22 16:07:42 -0800728 status_t err = addLeafFile(fileNames[i], file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800729 if (err != NO_ERROR) {
730 return err;
731 }
732
733 count++;
734
735 } else {
736 if (bundle->getVerbose())
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000737 printf(" (ignoring non-file/dir '%s')\n", pathName.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800738 }
739 }
740
741 return count;
742}
743
744status_t AaptDir::validate() const
745{
746 const size_t NF = mFiles.size();
747 const size_t ND = mDirs.size();
748 size_t i;
749 for (i = 0; i < NF; i++) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000750 if (!validateFileName(mFiles.valueAt(i)->getLeaf().c_str())) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800751 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
752 "Invalid filename. Unable to add.");
753 return UNKNOWN_ERROR;
754 }
755
756 size_t j;
757 for (j = i+1; j < NF; j++) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000758 if (strcasecmp(mFiles.valueAt(i)->getLeaf().c_str(),
759 mFiles.valueAt(j)->getLeaf().c_str()) == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800760 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
761 "File is case-insensitive equivalent to: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000762 mFiles.valueAt(j)->getPrintableSource().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800763 return UNKNOWN_ERROR;
764 }
765
766 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
767 // (this is mostly caught by the "marked" stuff, below)
768 }
769
770 for (j = 0; j < ND; j++) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000771 if (strcasecmp(mFiles.valueAt(i)->getLeaf().c_str(),
772 mDirs.valueAt(j)->getLeaf().c_str()) == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800773 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
774 "File conflicts with dir from: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000775 mDirs.valueAt(j)->getPrintableSource().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800776 return UNKNOWN_ERROR;
777 }
778 }
779 }
780
781 for (i = 0; i < ND; i++) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000782 if (!validateFileName(mDirs.valueAt(i)->getLeaf().c_str())) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800783 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
784 "Invalid directory name, unable to add.");
785 return UNKNOWN_ERROR;
786 }
787
788 size_t j;
789 for (j = i+1; j < ND; j++) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000790 if (strcasecmp(mDirs.valueAt(i)->getLeaf().c_str(),
791 mDirs.valueAt(j)->getLeaf().c_str()) == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800792 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
793 "Directory is case-insensitive equivalent to: %s",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000794 mDirs.valueAt(j)->getPrintableSource().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800795 return UNKNOWN_ERROR;
796 }
797 }
798
799 status_t err = mDirs.valueAt(i)->validate();
800 if (err != NO_ERROR) {
801 return err;
802 }
803 }
804
805 return NO_ERROR;
806}
807
808void AaptDir::print(const String8& prefix) const
809{
810 const size_t ND=getDirs().size();
811 size_t i;
812 for (i=0; i<ND; i++) {
813 getDirs().valueAt(i)->print(prefix);
814 }
815
816 const size_t NF=getFiles().size();
817 for (i=0; i<NF; i++) {
818 getFiles().valueAt(i)->print(prefix);
819 }
820}
821
822String8 AaptDir::getPrintableSource() const
823{
824 if (mFiles.size() > 0) {
825 // Arbitrarily pull the first file out of the list as the source dir.
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000826 return getPathDir(mFiles.valueAt(0)->getPrintableSource());
Adam Lesinski282e1812014-01-23 18:17:42 -0800827 }
828 if (mDirs.size() > 0) {
829 // Or arbitrarily pull the first dir out of the list as the source dir.
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000830 return getPathDir(mDirs.valueAt(0)->getPrintableSource());
Adam Lesinski282e1812014-01-23 18:17:42 -0800831 }
832
833 // Should never hit this case, but to be safe...
834 return mPath;
835
836}
837
838// =========================================================================
839// =========================================================================
840// =========================================================================
841
842status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
843{
844 status_t err = NO_ERROR;
845 size_t N = javaSymbols->mSymbols.size();
846 for (size_t i=0; i<N; i++) {
847 const String8& name = javaSymbols->mSymbols.keyAt(i);
848 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
849 ssize_t pos = mSymbols.indexOfKey(name);
850 if (pos < 0) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000851 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800852 err = UNKNOWN_ERROR;
853 continue;
854 }
855 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000856 // i, N, name.c_str(), entry.isJavaSymbol ? 1 : 0);
Adam Lesinski282e1812014-01-23 18:17:42 -0800857 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
858 }
859
860 N = javaSymbols->mNestedSymbols.size();
861 for (size_t i=0; i<N; i++) {
862 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
863 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
864 ssize_t pos = mNestedSymbols.indexOfKey(name);
865 if (pos < 0) {
866 SourcePos pos;
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000867 pos.error("Java symbol dir %s not defined\n", name.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800868 err = UNKNOWN_ERROR;
869 continue;
870 }
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +0000871 //printf("**** applying java symbols in dir %s\n", name.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -0800872 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
873 if (myerr != NO_ERROR) {
874 err = myerr;
875 }
876 }
877
878 return err;
879}
880
881// =========================================================================
882// =========================================================================
883// =========================================================================
884
885AaptAssets::AaptAssets()
886 : AaptDir(String8(), String8()),
Narayan Kamath91447d82014-01-21 15:32:36 +0000887 mHavePrivateSymbols(false),
888 mChanged(false), mHaveIncludedAssets(false),
Adam Lesinskifab50872014-04-16 14:40:42 -0700889 mRes(NULL) {}
Adam Lesinski282e1812014-01-23 18:17:42 -0800890
891const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
892 if (mChanged) {
893 }
894 return mGroupEntries;
895}
896
897status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
898{
899 mChanged = true;
900 return AaptDir::addFile(name, file);
901}
902
903sp<AaptFile> AaptAssets::addFile(
904 const String8& filePath, const AaptGroupEntry& entry,
905 const String8& srcDir, sp<AaptGroup>* outGroup,
906 const String8& resType)
907{
908 sp<AaptDir> dir = this;
909 sp<AaptGroup> group;
910 sp<AaptFile> file;
911 String8 root, remain(filePath), partialPath;
912 while (remain.length() > 0) {
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000913 root = walkPath(remain, &remain);
914 appendPath(partialPath, root);
Adam Lesinski282e1812014-01-23 18:17:42 -0800915
916 const String8 rootStr(root);
917
918 if (remain.length() == 0) {
919 ssize_t i = dir->getFiles().indexOfKey(rootStr);
920 if (i >= 0) {
921 group = dir->getFiles().valueAt(i);
922 } else {
923 group = new AaptGroup(rootStr, filePath);
924 status_t res = dir->addFile(rootStr, group);
925 if (res != NO_ERROR) {
926 return NULL;
927 }
928 }
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000929 file = new AaptFile(appendPathCopy(srcDir, filePath), entry, resType);
Adam Lesinski282e1812014-01-23 18:17:42 -0800930 status_t res = group->addFile(file);
931 if (res != NO_ERROR) {
932 return NULL;
933 }
934 break;
935
936 } else {
937 ssize_t i = dir->getDirs().indexOfKey(rootStr);
938 if (i >= 0) {
939 dir = dir->getDirs().valueAt(i);
940 } else {
941 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
942 status_t res = dir->addDir(rootStr, subdir);
943 if (res != NO_ERROR) {
944 return NULL;
945 }
946 dir = subdir;
947 }
948 }
949 }
950
951 mGroupEntries.add(entry);
952 if (outGroup) *outGroup = group;
953 return file;
954}
955
956void AaptAssets::addResource(const String8& leafName, const String8& path,
957 const sp<AaptFile>& file, const String8& resType)
958{
959 sp<AaptDir> res = AaptDir::makeDir(kResString);
960 String8 dirname = file->getGroupEntry().toDirName(resType);
961 sp<AaptDir> subdir = res->makeDir(dirname);
962 sp<AaptGroup> grr = new AaptGroup(leafName, path);
963 grr->addFile(file);
964
965 subdir->addFile(leafName, grr);
966}
967
Guang Zhu8c2df712017-03-21 03:53:43 +0000968
Adam Lesinski282e1812014-01-23 18:17:42 -0800969ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
970{
971 int count;
972 int totalCount = 0;
973 FileType type;
974 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
975 const size_t dirCount =resDirs.size();
976 sp<AaptAssets> current = this;
977
978 const int N = bundle->getFileSpecCount();
979
980 /*
981 * If a package manifest was specified, include that first.
982 */
983 if (bundle->getAndroidManifestFile() != NULL) {
984 // place at root of zip.
985 String8 srcFile(bundle->getAndroidManifestFile());
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +0000986 addFile(getPathLeaf(srcFile), AaptGroupEntry(), getPathDir(srcFile),
Adam Lesinski282e1812014-01-23 18:17:42 -0800987 NULL, String8());
988 totalCount++;
989 }
990
991 /*
992 * If a directory of custom assets was supplied, slurp 'em up.
993 */
Adam Lesinski09384302014-01-22 16:07:42 -0800994 const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
995 const int AN = assetDirs.size();
996 for (int i = 0; i < AN; i++) {
997 FileType type = getFileType(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -0800998 if (type == kFileTypeNonexistent) {
Adam Lesinski09384302014-01-22 16:07:42 -0800999 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001000 return UNKNOWN_ERROR;
1001 }
1002 if (type != kFileTypeDirectory) {
Adam Lesinski09384302014-01-22 16:07:42 -08001003 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001004 return UNKNOWN_ERROR;
1005 }
1006
Adam Lesinski09384302014-01-22 16:07:42 -08001007 String8 assetRoot(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001008 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1009 AaptGroupEntry group;
1010 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Adam Lesinski09384302014-01-22 16:07:42 -08001011 String8(), mFullAssetPaths, true);
Adam Lesinski282e1812014-01-23 18:17:42 -08001012 if (count < 0) {
1013 totalCount = count;
1014 goto bail;
1015 }
1016 if (count > 0) {
1017 mGroupEntries.add(group);
1018 }
1019 totalCount += count;
1020
Adam Lesinski09384302014-01-22 16:07:42 -08001021 if (bundle->getVerbose()) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001022 printf("Found %d custom asset file%s in %s\n",
Adam Lesinski09384302014-01-22 16:07:42 -08001023 count, (count==1) ? "" : "s", assetDirs[i]);
1024 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001025 }
1026
1027 /*
1028 * If a directory of resource-specific assets was supplied, slurp 'em up.
1029 */
1030 for (size_t i=0; i<dirCount; i++) {
1031 const char *res = resDirs[i];
1032 if (res) {
1033 type = getFileType(res);
1034 if (type == kFileTypeNonexistent) {
1035 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1036 return UNKNOWN_ERROR;
1037 }
1038 if (type == kFileTypeDirectory) {
1039 if (i>0) {
1040 sp<AaptAssets> nextOverlay = new AaptAssets();
1041 current->setOverlay(nextOverlay);
1042 current = nextOverlay;
1043 current->setFullResPaths(mFullResPaths);
1044 }
1045 count = current->slurpResourceTree(bundle, String8(res));
Bryan Mawhinney9ab9b932014-01-24 16:18:13 +00001046 if (i > 0 && count > 0) {
1047 count = current->filter(bundle);
1048 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001049
1050 if (count < 0) {
1051 totalCount = count;
1052 goto bail;
1053 }
1054 totalCount += count;
1055 }
1056 else {
1057 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1058 return UNKNOWN_ERROR;
1059 }
1060 }
1061
1062 }
1063 /*
1064 * Now do any additional raw files.
1065 */
1066 for (int arg=0; arg<N; arg++) {
1067 const char* assetDir = bundle->getFileSpecEntry(arg);
1068
1069 FileType type = getFileType(assetDir);
1070 if (type == kFileTypeNonexistent) {
1071 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1072 return UNKNOWN_ERROR;
1073 }
1074 if (type != kFileTypeDirectory) {
1075 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1076 return UNKNOWN_ERROR;
1077 }
1078
1079 String8 assetRoot(assetDir);
1080
1081 if (bundle->getVerbose())
1082 printf("Processing raw dir '%s'\n", (const char*) assetDir);
1083
1084 /*
1085 * Do a recursive traversal of subdir tree. We don't make any
1086 * guarantees about ordering, so we're okay with an inorder search
1087 * using whatever order the OS happens to hand back to us.
1088 */
1089 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
1090 if (count < 0) {
1091 /* failure; report error and remove archive */
1092 totalCount = count;
1093 goto bail;
1094 }
1095 totalCount += count;
1096
1097 if (bundle->getVerbose())
1098 printf("Found %d asset file%s in %s\n",
1099 count, (count==1) ? "" : "s", assetDir);
1100 }
1101
1102 count = validate();
1103 if (count != NO_ERROR) {
1104 totalCount = count;
1105 goto bail;
1106 }
1107
1108 count = filter(bundle);
1109 if (count != NO_ERROR) {
1110 totalCount = count;
1111 goto bail;
1112 }
1113
1114bail:
1115 return totalCount;
1116}
1117
1118ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1119 const AaptGroupEntry& kind,
1120 const String8& resType,
Adam Lesinski40e8eef2014-09-16 14:43:29 -07001121 sp<FilePathStore>& fullResPaths,
1122 const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -08001123{
Adam Lesinski40e8eef2014-09-16 14:43:29 -07001124 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -08001125 if (res > 0) {
1126 mGroupEntries.add(kind);
1127 }
1128
1129 return res;
1130}
1131
1132ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1133{
1134 ssize_t err = 0;
1135
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001136 DIR* dir = opendir(srcDir.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001137 if (dir == NULL) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001138 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.c_str(), strerror(errno));
Adam Lesinski282e1812014-01-23 18:17:42 -08001139 return UNKNOWN_ERROR;
1140 }
1141
1142 status_t count = 0;
1143
1144 /*
1145 * Run through the directory, looking for dirs that match the
1146 * expected pattern.
1147 */
1148 while (1) {
1149 struct dirent* entry = readdir(dir);
1150 if (entry == NULL) {
1151 break;
1152 }
1153
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001154 if (isHidden(srcDir.c_str(), entry->d_name)) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001155 continue;
1156 }
1157
1158 String8 subdirName(srcDir);
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00001159 appendPath(subdirName, entry->d_name);
Adam Lesinski282e1812014-01-23 18:17:42 -08001160
1161 AaptGroupEntry group;
1162 String8 resType;
1163 bool b = group.initFromDirName(entry->d_name, &resType);
1164 if (!b) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001165 fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.c_str(),
Adam Lesinski282e1812014-01-23 18:17:42 -08001166 entry->d_name);
1167 err = -1;
1168 continue;
1169 }
1170
1171 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
1172 int maxResInt = atoi(bundle->getMaxResVersion());
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001173 const char *verString = group.getVersionString().c_str();
Adam Lesinski282e1812014-01-23 18:17:42 -08001174 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
1175 if (dirVersionInt > maxResInt) {
1176 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
1177 continue;
1178 }
1179 }
1180
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001181 FileType type = getFileType(subdirName.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001182
1183 if (type == kFileTypeDirectory) {
1184 sp<AaptDir> dir = makeDir(resType);
1185 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1186 resType, mFullResPaths);
1187 if (res < 0) {
1188 count = res;
1189 goto bail;
1190 }
1191 if (res > 0) {
1192 mGroupEntries.add(group);
1193 count += res;
1194 }
1195
1196 // Only add this directory if we don't already have a resource dir
1197 // for the current type. This ensures that we only add the dir once
1198 // for all configs.
1199 sp<AaptDir> rdir = resDir(resType);
1200 if (rdir == NULL) {
1201 mResDirs.add(dir);
1202 }
1203 } else {
1204 if (bundle->getVerbose()) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001205 fprintf(stderr, " (ignoring file '%s')\n", subdirName.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001206 }
1207 }
1208 }
1209
1210bail:
1211 closedir(dir);
1212 dir = NULL;
1213
1214 if (err != 0) {
1215 return err;
1216 }
1217 return count;
1218}
1219
1220ssize_t
Andreas Gampe2412f842014-09-30 20:55:57 -07001221AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename)
Adam Lesinski282e1812014-01-23 18:17:42 -08001222{
1223 int count = 0;
1224 SortedVector<AaptGroupEntry> entries;
1225
1226 ZipFile* zip = new ZipFile;
1227 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1228 if (err != NO_ERROR) {
1229 fprintf(stderr, "error opening zip file %s\n", filename);
1230 count = err;
1231 delete zip;
1232 return -1;
1233 }
1234
1235 const int N = zip->getNumEntries();
1236 for (int i=0; i<N; i++) {
1237 ZipEntry* entry = zip->getEntryByIndex(i);
1238 if (entry->getDeleted()) {
1239 continue;
1240 }
1241
1242 String8 entryName(entry->getFileName());
1243
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00001244 String8 dirName = getPathDir(entryName);
Adam Lesinski282e1812014-01-23 18:17:42 -08001245 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1246
1247 String8 resType;
1248 AaptGroupEntry kind;
1249
1250 String8 remain;
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00001251 if (walkPath(entryName, &remain) == kResourceDir) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001252 // these are the resources, pull their type out of the directory name
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00001253 kind.initFromDirName(walkPath(remain).c_str(), &resType);
Adam Lesinski282e1812014-01-23 18:17:42 -08001254 } else {
1255 // these are untyped and don't have an AaptGroupEntry
1256 }
1257 if (entries.indexOf(kind) < 0) {
1258 entries.add(kind);
1259 mGroupEntries.add(kind);
1260 }
1261
1262 // use the one from the zip file if they both exist.
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00001263 dir->removeFile(getPathLeaf(entryName));
Adam Lesinski282e1812014-01-23 18:17:42 -08001264
1265 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00001266 status_t err = dir->addLeafFile(getPathLeaf(entryName), file);
Adam Lesinski282e1812014-01-23 18:17:42 -08001267 if (err != NO_ERROR) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001268 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001269 count = err;
1270 goto bail;
1271 }
1272 file->setCompressionMethod(entry->getCompressionMethod());
1273
1274#if 0
1275 if (entryName == "AndroidManifest.xml") {
1276 printf("AndroidManifest.xml\n");
1277 }
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001278 printf("\n\nfile: %s\n", entryName.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001279#endif
1280
1281 size_t len = entry->getUncompressedLen();
1282 void* data = zip->uncompress(entry);
1283 void* buf = file->editData(len);
1284 memcpy(buf, data, len);
1285
1286#if 0
1287 const int OFF = 0;
1288 const unsigned char* p = (unsigned char*)data;
1289 const unsigned char* end = p+len;
1290 p += OFF;
1291 for (int i=0; i<32 && p < end; i++) {
1292 printf("0x%03x ", i*0x10 + OFF);
1293 for (int j=0; j<0x10 && p < end; j++) {
1294 printf(" %02x", *p);
1295 p++;
1296 }
1297 printf("\n");
1298 }
1299#endif
1300
1301 free(data);
1302
1303 count++;
1304 }
1305
1306bail:
1307 delete zip;
1308 return count;
1309}
1310
1311status_t AaptAssets::filter(Bundle* bundle)
1312{
Hans Boehm25dfa752016-08-10 19:56:50 -07001313 sp<WeakResourceFilter> reqFilter(new WeakResourceFilter());
1314 status_t err = reqFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08001315 if (err != NO_ERROR) {
1316 return err;
1317 }
1318
Adam Lesinskifab50872014-04-16 14:40:42 -07001319 uint32_t preferredDensity = 0;
1320 if (bundle->getPreferredDensity().size() > 0) {
1321 ResTable_config preferredConfig;
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001322 if (!AaptConfig::parseDensity(bundle->getPreferredDensity().c_str(), &preferredConfig)) {
Adam Lesinskifab50872014-04-16 14:40:42 -07001323 fprintf(stderr, "Error parsing preferred density: %s\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001324 bundle->getPreferredDensity().c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07001325 return UNKNOWN_ERROR;
1326 }
1327 preferredDensity = preferredConfig.density;
Adam Lesinski282e1812014-01-23 18:17:42 -08001328 }
1329
Hans Boehm25dfa752016-08-10 19:56:50 -07001330 if (reqFilter->isEmpty() && preferredDensity == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001331 return NO_ERROR;
1332 }
1333
1334 if (bundle->getVerbose()) {
Hans Boehm25dfa752016-08-10 19:56:50 -07001335 if (!reqFilter->isEmpty()) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001336 printf("Applying required filter: %s\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001337 bundle->getConfigurations().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001338 }
Adam Lesinskifab50872014-04-16 14:40:42 -07001339 if (preferredDensity > 0) {
1340 printf("Applying preferred density filter: %s\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001341 bundle->getPreferredDensity().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001342 }
1343 }
1344
1345 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1346 const size_t ND = resdirs.size();
1347 for (size_t i=0; i<ND; i++) {
1348 const sp<AaptDir>& dir = resdirs.itemAt(i);
1349 if (dir->getLeaf() == kValuesDir) {
1350 // The "value" dir is special since a single file defines
1351 // multiple resources, so we can not do filtering on the
1352 // files themselves.
1353 continue;
1354 }
1355 if (dir->getLeaf() == kMipmapDir) {
1356 // We also skip the "mipmap" directory, since the point of this
1357 // is to include all densities without stripping. If you put
1358 // other configurations in here as well they won't be stripped
1359 // either... So don't do that. Seriously. What is wrong with you?
1360 continue;
1361 }
1362
1363 const size_t NG = dir->getFiles().size();
1364 for (size_t j=0; j<NG; j++) {
1365 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
1366
1367 // First remove any configurations we know we don't need.
1368 for (size_t k=0; k<grp->getFiles().size(); k++) {
1369 sp<AaptFile> file = grp->getFiles().valueAt(k);
1370 if (k == 0 && grp->getFiles().size() == 1) {
1371 // If this is the only file left, we need to keep it.
1372 // Otherwise the resource IDs we are using will be inconsistent
1373 // with what we get when not stripping. Sucky, but at least
1374 // for now we can rely on the back-end doing another filtering
1375 // pass to take this out and leave us with this resource name
1376 // containing no entries.
1377 continue;
1378 }
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00001379 if (getPathExtension(file->getPath()) == ".xml") {
Adam Lesinski282e1812014-01-23 18:17:42 -08001380 // We can't remove .xml files at this point, because when
1381 // we parse them they may add identifier resources, so
1382 // removing them can cause our resource identifiers to
1383 // become inconsistent.
1384 continue;
1385 }
1386 const ResTable_config& config(file->getGroupEntry().toParams());
Hans Boehm25dfa752016-08-10 19:56:50 -07001387 if (!reqFilter->match(config)) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001388 if (bundle->getVerbose()) {
1389 printf("Pruning unneeded resource: %s\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001390 file->getPrintableSource().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001391 }
1392 grp->removeFile(k);
1393 k--;
1394 }
1395 }
1396
1397 // Quick check: no preferred filters, nothing more to do.
Adam Lesinskifab50872014-04-16 14:40:42 -07001398 if (preferredDensity == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001399 continue;
1400 }
1401
Adam Lesinski8cf61842013-10-18 13:42:09 -07001402 // Get the preferred density if there is one. We do not match exactly for density.
1403 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
1404 // pick xhdpi.
Adam Lesinskifab50872014-04-16 14:40:42 -07001405 for (size_t k=0; k<grp->getFiles().size(); k++) {
1406 sp<AaptFile> file = grp->getFiles().valueAt(k);
1407 if (k == 0 && grp->getFiles().size() == 1) {
1408 // If this is the only file left, we need to keep it.
1409 // Otherwise the resource IDs we are using will be inconsistent
1410 // with what we get when not stripping. Sucky, but at least
1411 // for now we can rely on the back-end doing another filtering
1412 // pass to take this out and leave us with this resource name
1413 // containing no entries.
1414 continue;
1415 }
Tomasz Wasilczyk804e8192023-08-23 02:22:53 +00001416 if (getPathExtension(file->getPath()) == ".xml") {
Adam Lesinskifab50872014-04-16 14:40:42 -07001417 // We can't remove .xml files at this point, because when
1418 // we parse them they may add identifier resources, so
1419 // removing them can cause our resource identifiers to
1420 // become inconsistent.
1421 continue;
1422 }
1423 const ResTable_config& config(file->getGroupEntry().toParams());
1424 if (config.density != 0 && config.density != preferredDensity) {
1425 // This is a resource we would prefer not to have. Check
1426 // to see if have a similar variation that we would like
1427 // to have and, if so, we can drop it.
1428 uint32_t bestDensity = config.density;
Adam Lesinski8cf61842013-10-18 13:42:09 -07001429
Adam Lesinskifab50872014-04-16 14:40:42 -07001430 for (size_t m=0; m<grp->getFiles().size(); m++) {
1431 if (m == k) {
1432 continue;
Adam Lesinski282e1812014-01-23 18:17:42 -08001433 }
Adam Lesinski8cf61842013-10-18 13:42:09 -07001434
Adam Lesinskifab50872014-04-16 14:40:42 -07001435 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
1436 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
1437 if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
1438 // See if there is a better density resource
1439 if (mconfig.density < bestDensity &&
Bryan Mawhinneyb0db8de2014-06-06 13:27:11 +01001440 mconfig.density >= preferredDensity &&
Adam Lesinskifab50872014-04-16 14:40:42 -07001441 bestDensity > preferredDensity) {
Bryan Mawhinneyb0db8de2014-06-06 13:27:11 +01001442 // This density is our preferred density, or between our best density and
Adam Lesinskifab50872014-04-16 14:40:42 -07001443 // the preferred density, therefore it is better.
1444 bestDensity = mconfig.density;
1445 } else if (mconfig.density > bestDensity &&
1446 bestDensity < preferredDensity) {
1447 // This density is better than our best density and
1448 // our best density was smaller than our preferred
1449 // density, so it is better.
1450 bestDensity = mconfig.density;
Adam Lesinski8cf61842013-10-18 13:42:09 -07001451 }
Adam Lesinski8cf61842013-10-18 13:42:09 -07001452 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001453 }
Adam Lesinskifab50872014-04-16 14:40:42 -07001454
1455 if (bestDensity != config.density) {
1456 if (bundle->getVerbose()) {
1457 printf("Pruning unneeded resource: %s\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001458 file->getPrintableSource().c_str());
Adam Lesinskifab50872014-04-16 14:40:42 -07001459 }
1460 grp->removeFile(k);
1461 k--;
1462 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001463 }
1464 }
1465 }
1466 }
1467
1468 return NO_ERROR;
1469}
1470
1471sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1472{
1473 sp<AaptSymbols> sym = mSymbols.valueFor(name);
1474 if (sym == NULL) {
1475 sym = new AaptSymbols();
1476 mSymbols.add(name, sym);
1477 }
1478 return sym;
1479}
1480
1481sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
1482{
1483 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
1484 if (sym == NULL) {
1485 sym = new AaptSymbols();
1486 mJavaSymbols.add(name, sym);
1487 }
1488 return sym;
1489}
1490
1491status_t AaptAssets::applyJavaSymbols()
1492{
1493 size_t N = mJavaSymbols.size();
1494 for (size_t i=0; i<N; i++) {
1495 const String8& name = mJavaSymbols.keyAt(i);
1496 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
1497 ssize_t pos = mSymbols.indexOfKey(name);
1498 if (pos < 0) {
1499 SourcePos pos;
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001500 pos.error("Java symbol dir %s not defined\n", name.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001501 return UNKNOWN_ERROR;
1502 }
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001503 //printf("**** applying java symbols in dir %s\n", name.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001504 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1505 if (err != NO_ERROR) {
1506 return err;
1507 }
1508 }
1509
1510 return NO_ERROR;
1511}
1512
1513bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
1514 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001515 // sym.name.c_str(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
Adam Lesinski282e1812014-01-23 18:17:42 -08001516 // sym.isJavaSymbol ? 1 : 0);
1517 if (!mHavePrivateSymbols) return true;
1518 if (sym.isPublic) return true;
1519 if (includePrivate && sym.isJavaSymbol) return true;
1520 return false;
1521}
1522
1523status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1524{
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001525 if (mHaveIncludedAssets) {
1526 return NO_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -08001527 }
1528
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001529 // Add in all includes.
1530 const Vector<String8>& includes = bundle->getPackageIncludes();
1531 const size_t packageIncludeCount = includes.size();
1532 for (size_t i = 0; i < packageIncludeCount; i++) {
1533 if (bundle->getVerbose()) {
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001534 printf("Including resources from package: %s\n", includes[i].c_str());
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001535 }
1536
1537 if (!mIncludedAssets.addAssetPath(includes[i], NULL)) {
1538 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001539 includes[i].c_str());
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001540 return UNKNOWN_ERROR;
1541 }
1542 }
1543
1544 const String8& featureOfBase = bundle->getFeatureOfPackage();
Tomasz Wasilczyk7e22cab2023-08-24 19:02:33 +00001545 if (!featureOfBase.empty()) {
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001546 if (bundle->getVerbose()) {
1547 printf("Including base feature resources from package: %s\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001548 featureOfBase.c_str());
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001549 }
1550
1551 if (!mIncludedAssets.addAssetPath(featureOfBase, NULL)) {
1552 fprintf(stderr, "ERROR: base feature package '%s' not found.\n",
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001553 featureOfBase.c_str());
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001554 return UNKNOWN_ERROR;
1555 }
1556 }
1557
1558 mHaveIncludedAssets = true;
1559
Adam Lesinski282e1812014-01-23 18:17:42 -08001560 return NO_ERROR;
1561}
1562
1563status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
1564{
1565 const ResTable& res = getIncludedResources();
1566 // XXX dirty!
Narayan Kamath00b31442014-01-27 17:32:37 +00001567 return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
Adam Lesinski282e1812014-01-23 18:17:42 -08001568}
1569
1570const ResTable& AaptAssets::getIncludedResources() const
1571{
1572 return mIncludedAssets.getResources(false);
1573}
1574
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001575AssetManager& AaptAssets::getAssetManager()
1576{
1577 return mIncludedAssets;
1578}
1579
Adam Lesinski282e1812014-01-23 18:17:42 -08001580void AaptAssets::print(const String8& prefix) const
1581{
1582 String8 innerPrefix(prefix);
1583 innerPrefix.append(" ");
1584 String8 innerInnerPrefix(innerPrefix);
1585 innerInnerPrefix.append(" ");
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001586 printf("%sConfigurations:\n", prefix.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001587 const size_t N=mGroupEntries.size();
1588 for (size_t i=0; i<N; i++) {
1589 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001590 printf("%s %s\n", prefix.c_str(),
1591 cname != "" ? cname.c_str() : "(default)");
Adam Lesinski282e1812014-01-23 18:17:42 -08001592 }
1593
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001594 printf("\n%sFiles:\n", prefix.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001595 AaptDir::print(innerPrefix);
1596
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001597 printf("\n%sResource Dirs:\n", prefix.c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001598 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1599 const size_t NR = resdirs.size();
1600 for (size_t i=0; i<NR; i++) {
1601 const sp<AaptDir>& d = resdirs.itemAt(i);
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001602 printf("%s Type %s\n", prefix.c_str(), d->getLeaf().c_str());
Adam Lesinski282e1812014-01-23 18:17:42 -08001603 d->print(innerInnerPrefix);
1604 }
1605}
1606
1607sp<AaptDir> AaptAssets::resDir(const String8& name) const
1608{
1609 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1610 const size_t N = resdirs.size();
1611 for (size_t i=0; i<N; i++) {
1612 const sp<AaptDir>& d = resdirs.itemAt(i);
1613 if (d->getLeaf() == name) {
1614 return d;
1615 }
1616 }
1617 return NULL;
1618}
1619
1620bool
1621valid_symbol_name(const String8& symbol)
1622{
1623 static char const * const KEYWORDS[] = {
1624 "abstract", "assert", "boolean", "break",
1625 "byte", "case", "catch", "char", "class", "const", "continue",
1626 "default", "do", "double", "else", "enum", "extends", "final",
1627 "finally", "float", "for", "goto", "if", "implements", "import",
1628 "instanceof", "int", "interface", "long", "native", "new", "package",
1629 "private", "protected", "public", "return", "short", "static",
1630 "strictfp", "super", "switch", "synchronized", "this", "throw",
1631 "throws", "transient", "try", "void", "volatile", "while",
1632 "true", "false", "null",
1633 NULL
1634 };
1635 const char*const* k = KEYWORDS;
Tomasz Wasilczykd2a69832023-08-10 23:54:44 +00001636 const char*const s = symbol.c_str();
Adam Lesinski282e1812014-01-23 18:17:42 -08001637 while (*k) {
1638 if (0 == strcmp(s, *k)) {
1639 return false;
1640 }
1641 k++;
1642 }
1643 return true;
1644}