blob: 3b01827e13d8d2bbe8b61bc71ccc9bcf8cc2e2f5 [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"
Adam Lesinski282e1812014-01-23 18:17:42 -080010
11#include <utils/misc.h>
12#include <utils/SortedVector.h>
13
14#include <ctype.h>
15#include <dirent.h>
16#include <errno.h>
17
Adam Lesinski282e1812014-01-23 18:17:42 -080018static const char* kAssetDir = "assets";
19static const char* kResourceDir = "res";
20static const char* kValuesDir = "values";
21static const char* kMipmapDir = "mipmap";
22static const char* kInvalidChars = "/\\:";
23static const size_t kMaxAssetFileName = 100;
24
25static const String8 kResString(kResourceDir);
26
27/*
28 * Names of asset files must meet the following criteria:
29 *
30 * - the filename length must be less than kMaxAssetFileName bytes long
31 * (and can't be empty)
32 * - all characters must be 7-bit printable ASCII
33 * - none of { '/' '\\' ':' }
34 *
35 * Pass in just the filename, not the full path.
36 */
37static bool validateFileName(const char* fileName)
38{
39 const char* cp = fileName;
40 size_t len = 0;
41
42 while (*cp != '\0') {
43 if ((*cp & 0x80) != 0)
44 return false; // reject high ASCII
45 if (*cp < 0x20 || *cp >= 0x7f)
46 return false; // reject control chars and 0x7f
47 if (strchr(kInvalidChars, *cp) != NULL)
48 return false; // reject path sep chars
49 cp++;
50 len++;
51 }
52
53 if (len < 1 || len > kMaxAssetFileName)
54 return false; // reject empty or too long
55
56 return true;
57}
58
59// The default to use if no other ignore pattern is defined.
60const 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
63const char * gUserIgnoreAssets = NULL;
64
65static bool isHidden(const char *root, const char *path)
66{
67 // Patterns syntax:
68 // - Delimiter is :
69 // - Entry can start with the flag ! to avoid printing a warning
70 // about the file being ignored.
71 // - Entry can have the flag "<dir>" to match only directories
72 // or <file> to match only files. Default is to match both.
73 // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
74 // where prefix/suffix must have at least 1 character (so that
75 // we don't match a '*' catch-all pattern.)
76 // - The special filenames "." and ".." are always ignored.
77 // - Otherwise the full string is matched.
78 // - match is not case-sensitive.
79
80 if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
81 return true;
82 }
83
84 const char *delim = ":";
85 const char *p = gUserIgnoreAssets;
86 if (!p || !p[0]) {
87 p = getenv("ANDROID_AAPT_IGNORE");
88 }
89 if (!p || !p[0]) {
90 p = gDefaultIgnoreAssets;
91 }
92 char *patterns = strdup(p);
93
94 bool ignore = false;
95 bool chatty = true;
96 char *matchedPattern = NULL;
97
98 String8 fullPath(root);
99 fullPath.appendPath(path);
100 FileType type = getFileType(fullPath);
101
102 int plen = strlen(path);
103
104 // Note: we don't have strtok_r under mingw.
105 for(char *token = strtok(patterns, delim);
106 !ignore && token != NULL;
107 token = strtok(NULL, delim)) {
108 chatty = token[0] != '!';
109 if (!chatty) token++; // skip !
110 if (strncasecmp(token, "<dir>" , 5) == 0) {
111 if (type != kFileTypeDirectory) continue;
112 token += 5;
113 }
114 if (strncasecmp(token, "<file>", 6) == 0) {
115 if (type != kFileTypeRegular) continue;
116 token += 6;
117 }
118
119 matchedPattern = token;
120 int n = strlen(token);
121
122 if (token[0] == '*') {
123 // Match *suffix
124 token++;
125 n--;
126 if (n <= plen) {
127 ignore = strncasecmp(token, path + plen - n, n) == 0;
128 }
129 } else if (n > 1 && token[n - 1] == '*') {
130 // Match prefix*
131 ignore = strncasecmp(token, path, n - 1) == 0;
132 } else {
133 ignore = strcasecmp(token, path) == 0;
134 }
135 }
136
137 if (ignore && chatty) {
138 fprintf(stderr, " (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
139 type == kFileTypeDirectory ? "dir" : "file",
140 path,
141 matchedPattern ? matchedPattern : "");
142 }
143
144 free(patterns);
145 return ignore;
146}
147
148// =========================================================================
149// =========================================================================
150// =========================================================================
151
Narayan Kamath91447d82014-01-21 15:32:36 +0000152/* static */
153inline bool isAlpha(const String8& string) {
154 const size_t length = string.length();
155 for (size_t i = 0; i < length; ++i) {
156 if (!isalpha(string[i])) {
157 return false;
158 }
159 }
160
161 return true;
162}
163
164/* static */
165inline bool isNumber(const String8& string) {
166 const size_t length = string.length();
167 for (size_t i = 0; i < length; ++i) {
168 if (!isdigit(string[i])) {
169 return false;
170 }
171 }
172
173 return true;
174}
175
176void AaptLocaleValue::setLanguage(const char* languageChars) {
177 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700178 while ((*languageChars) != '\0' && i < sizeof(language)/sizeof(language[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000179 language[i++] = tolower(*languageChars);
180 languageChars++;
181 }
182}
183
184void AaptLocaleValue::setRegion(const char* regionChars) {
185 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700186 while ((*regionChars) != '\0' && i < sizeof(region)/sizeof(region[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000187 region[i++] = toupper(*regionChars);
188 regionChars++;
189 }
190}
191
192void AaptLocaleValue::setScript(const char* scriptChars) {
193 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700194 while ((*scriptChars) != '\0' && i < sizeof(script)/sizeof(script[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000195 if (i == 0) {
196 script[i++] = toupper(*scriptChars);
197 } else {
198 script[i++] = tolower(*scriptChars);
199 }
200 scriptChars++;
201 }
202}
203
204void AaptLocaleValue::setVariant(const char* variantChars) {
205 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700206 while ((*variantChars) != '\0' && i < sizeof(variant)/sizeof(variant[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000207 variant[i++] = *variantChars;
208 variantChars++;
209 }
210}
211
212bool AaptLocaleValue::initFromFilterString(const String8& str) {
213 // A locale (as specified in the filter) is an underscore separated name such
214 // as "en_US", "en_Latn_US", or "en_US_POSIX".
Adam Lesinskifab50872014-04-16 14:40:42 -0700215 Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_');
Narayan Kamath91447d82014-01-21 15:32:36 +0000216
217 const int numTags = parts.size();
218 bool valid = false;
219 if (numTags >= 1) {
220 const String8& lang = parts[0];
221 if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
222 setLanguage(lang.string());
223 valid = true;
224 }
225 }
226
227 if (!valid || numTags == 1) {
228 return valid;
229 }
230
231 // At this point, valid == true && numTags > 1.
232 const String8& part2 = parts[1];
233 if ((part2.length() == 2 && isAlpha(part2)) ||
234 (part2.length() == 3 && isNumber(part2))) {
235 setRegion(part2.string());
236 } else if (part2.length() == 4 && isAlpha(part2)) {
237 setScript(part2.string());
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800238 } else if (part2.length() >= 4 && part2.length() <= 8) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000239 setVariant(part2.string());
240 } else {
241 valid = false;
242 }
243
244 if (!valid || numTags == 2) {
245 return valid;
246 }
247
248 // At this point, valid == true && numTags > 1.
249 const String8& part3 = parts[2];
250 if (((part3.length() == 2 && isAlpha(part3)) ||
251 (part3.length() == 3 && isNumber(part3))) && script[0]) {
252 setRegion(part3.string());
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800253 } else if (part3.length() >= 4 && part3.length() <= 8) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000254 setVariant(part3.string());
255 } else {
256 valid = false;
257 }
258
259 if (!valid || numTags == 3) {
260 return valid;
261 }
262
263 const String8& part4 = parts[3];
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800264 if (part4.length() >= 4 && part4.length() <= 8) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000265 setVariant(part4.string());
266 } else {
267 valid = false;
268 }
269
270 if (!valid || numTags > 4) {
271 return false;
272 }
273
274 return true;
275}
276
277int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
278 const int size = parts.size();
279 int currentIndex = startIndex;
280
281 String8 part = parts[currentIndex];
282 if (part[0] == 'b' && part[1] == '+') {
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800283 // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
Narayan Kamath91447d82014-01-21 15:32:36 +0000284 // except that the separator is "+" and not "-".
Adam Lesinskifab50872014-04-16 14:40:42 -0700285 Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+');
Narayan Kamath91447d82014-01-21 15:32:36 +0000286 subtags.removeItemsAt(0);
287 if (subtags.size() == 1) {
288 setLanguage(subtags[0]);
289 } else if (subtags.size() == 2) {
290 setLanguage(subtags[0]);
291
292 // The second tag can either be a region, a variant or a script.
293 switch (subtags[1].size()) {
294 case 2:
295 case 3:
296 setRegion(subtags[1]);
297 break;
298 case 4:
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800299 if (isAlpha(subtags[1])) {
300 setScript(subtags[1]);
301 break;
302 }
303 // This is not alphabetical, so we fall through to variant
Narayan Kamath91447d82014-01-21 15:32:36 +0000304 case 5:
305 case 6:
306 case 7:
307 case 8:
308 setVariant(subtags[1]);
309 break;
310 default:
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800311 fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n",
Narayan Kamath91447d82014-01-21 15:32:36 +0000312 part.string());
313 return -1;
314 }
315 } else if (subtags.size() == 3) {
316 // The language is always the first subtag.
317 setLanguage(subtags[0]);
318
319 // The second subtag can either be a script or a region code.
320 // If its size is 4, it's a script code, else it's a region code.
321 bool hasRegion = false;
322 if (subtags[1].size() == 4) {
323 setScript(subtags[1]);
324 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
325 setRegion(subtags[1]);
326 hasRegion = true;
327 } else {
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800328 fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n", part.string());
Narayan Kamath91447d82014-01-21 15:32:36 +0000329 return -1;
330 }
331
332 // The third tag can either be a region code (if the second tag was
333 // a script), else a variant code.
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800334 if (subtags[2].size() >= 4) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000335 setVariant(subtags[2]);
336 } else {
337 setRegion(subtags[2]);
338 }
339 } else if (subtags.size() == 4) {
340 setLanguage(subtags[0]);
341 setScript(subtags[1]);
342 setRegion(subtags[2]);
343 setVariant(subtags[3]);
344 } else {
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800345 fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name: %s\n", part.string());
Narayan Kamath91447d82014-01-21 15:32:36 +0000346 return -1;
347 }
348
349 return ++currentIndex;
350 } else {
Narayan Kamath7f1a8952015-02-10 16:11:55 +0000351 if ((part.length() == 2 || part.length() == 3)
352 && isAlpha(part) && strcmp("car", part.string())) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000353 setLanguage(part);
354 if (++currentIndex == size) {
355 return size;
356 }
357 } else {
358 return currentIndex;
359 }
360
361 part = parts[currentIndex];
362 if (part.string()[0] == 'r' && part.length() == 3) {
363 setRegion(part.string() + 1);
364 if (++currentIndex == size) {
365 return size;
366 }
367 }
368 }
369
370 return currentIndex;
371}
372
Narayan Kamath91447d82014-01-21 15:32:36 +0000373void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
374 config.unpackLanguage(language);
375 config.unpackRegion(region);
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800376 if (config.localeScriptWasProvided) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000377 memcpy(script, config.localeScript, sizeof(config.localeScript));
378 }
379
380 if (config.localeVariant[0]) {
381 memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
382 }
383}
384
385void AaptLocaleValue::writeTo(ResTable_config* out) const {
386 out->packLanguage(language);
387 out->packRegion(region);
388
389 if (script[0]) {
390 memcpy(out->localeScript, script, sizeof(out->localeScript));
Roozbeh Pournaderb927c552016-01-15 11:23:42 -0800391 out->localeScriptWasProvided = true;
392 } else {
393 out->computeScript();
394 out->localeScriptWasProvided = false;
Narayan Kamath91447d82014-01-21 15:32:36 +0000395 }
396
397 if (variant[0]) {
398 memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
399 }
400}
401
Adam Lesinski282e1812014-01-23 18:17:42 -0800402bool
403AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
404{
Adam Lesinskifab50872014-04-16 14:40:42 -0700405 const char* q = strchr(dir, '-');
406 size_t typeLen;
407 if (q != NULL) {
408 typeLen = q - dir;
409 } else {
410 typeLen = strlen(dir);
411 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800412
Adam Lesinskifab50872014-04-16 14:40:42 -0700413 String8 type(dir, typeLen);
414 if (!isValidResourceType(type)) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800415 return false;
416 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800417
Adam Lesinskifab50872014-04-16 14:40:42 -0700418 if (q != NULL) {
419 if (!AaptConfig::parse(String8(q + 1), &mParams)) {
420 return false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800421 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800422 }
423
Adam Lesinskifab50872014-04-16 14:40:42 -0700424 *resType = type;
Adam Lesinski282e1812014-01-23 18:17:42 -0800425 return true;
426}
427
428String8
Adam Lesinski282e1812014-01-23 18:17:42 -0800429AaptGroupEntry::toDirName(const String8& resType) const
430{
431 String8 s = resType;
Adam Lesinskifab50872014-04-16 14:40:42 -0700432 String8 params = mParams.toString();
433 if (params.length() > 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800434 if (s.length() > 0) {
435 s += "-";
436 }
Adam Lesinskifab50872014-04-16 14:40:42 -0700437 s += params;
Adam Lesinski282e1812014-01-23 18:17:42 -0800438 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800439 return s;
440}
441
Adam Lesinski282e1812014-01-23 18:17:42 -0800442
443// =========================================================================
444// =========================================================================
445// =========================================================================
446
447void* AaptFile::editData(size_t size)
448{
449 if (size <= mBufferSize) {
450 mDataSize = size;
451 return mData;
452 }
453 size_t allocSize = (size*3)/2;
454 void* buf = realloc(mData, allocSize);
455 if (buf == NULL) {
456 return NULL;
457 }
458 mData = buf;
459 mDataSize = size;
460 mBufferSize = allocSize;
461 return buf;
462}
463
Adam Lesinskide898ff2014-01-29 18:20:45 -0800464void* AaptFile::editDataInRange(size_t offset, size_t size)
465{
466 return (void*)(((uint8_t*) editData(offset + size)) + offset);
467}
468
Adam Lesinski282e1812014-01-23 18:17:42 -0800469void* AaptFile::editData(size_t* outSize)
470{
471 if (outSize) {
472 *outSize = mDataSize;
473 }
474 return mData;
475}
476
477void* AaptFile::padData(size_t wordSize)
478{
479 const size_t extra = mDataSize%wordSize;
480 if (extra == 0) {
481 return mData;
482 }
483
484 size_t initial = mDataSize;
485 void* data = editData(initial+(wordSize-extra));
486 if (data != NULL) {
487 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
488 }
489 return data;
490}
491
492status_t AaptFile::writeData(const void* data, size_t size)
493{
494 size_t end = mDataSize;
495 size_t total = size + end;
496 void* buf = editData(total);
497 if (buf == NULL) {
498 return UNKNOWN_ERROR;
499 }
500 memcpy(((char*)buf)+end, data, size);
501 return NO_ERROR;
502}
503
504void AaptFile::clearData()
505{
506 if (mData != NULL) free(mData);
507 mData = NULL;
508 mDataSize = 0;
509 mBufferSize = 0;
510}
511
512String8 AaptFile::getPrintableSource() const
513{
514 if (hasData()) {
515 String8 name(mGroupEntry.toDirName(String8()));
516 name.appendPath(mPath);
517 name.append(" #generated");
518 return name;
519 }
520 return mSourceFile;
521}
522
523// =========================================================================
524// =========================================================================
525// =========================================================================
526
Adam Lesinski09384302014-01-22 16:07:42 -0800527status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
Adam Lesinski282e1812014-01-23 18:17:42 -0800528{
Adam Lesinski09384302014-01-22 16:07:42 -0800529 ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
530 if (index >= 0 && overwriteDuplicate) {
531 fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
532 mFiles[index]->getSourceFile().string(),
533 file->getSourceFile().string());
534 removeFile(index);
535 index = -1;
536 }
537
538 if (index < 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800539 file->mPath = mPath;
540 mFiles.add(file->getGroupEntry(), file);
541 return NO_ERROR;
542 }
543
Adam Lesinski48f05d22014-05-12 22:13:02 -0700544 // Check if the version is automatically applied. This is a common source of
545 // error.
546 ConfigDescription withoutVersion = file->getGroupEntry().toParams();
547 withoutVersion.version = 0;
548 AaptConfig::applyVersionForCompatibility(&withoutVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -0800549
Adam Lesinski48f05d22014-05-12 22:13:02 -0700550 const sp<AaptFile>& originalFile = mFiles.valueAt(index);
551 SourcePos(file->getSourceFile(), -1)
552 .error("Duplicate file.\n%s: Original is here. %s",
553 originalFile->getPrintableSource().string(),
554 (withoutVersion.version != 0) ? "The version qualifier may be implied." : "");
Adam Lesinski282e1812014-01-23 18:17:42 -0800555 return UNKNOWN_ERROR;
556}
557
558void AaptGroup::removeFile(size_t index)
559{
560 mFiles.removeItemsAt(index);
561}
562
563void AaptGroup::print(const String8& prefix) const
564{
565 printf("%s%s\n", prefix.string(), getPath().string());
566 const size_t N=mFiles.size();
567 size_t i;
568 for (i=0; i<N; i++) {
569 sp<AaptFile> file = mFiles.valueAt(i);
570 const AaptGroupEntry& e = file->getGroupEntry();
571 if (file->hasData()) {
572 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
573 (int)file->getSize());
574 } else {
575 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
576 file->getPrintableSource().string());
577 }
578 //printf("%s File Group Entry: %s\n", prefix.string(),
579 // file->getGroupEntry().toDirName(String8()).string());
580 }
581}
582
583String8 AaptGroup::getPrintableSource() const
584{
585 if (mFiles.size() > 0) {
586 // Arbitrarily pull the first source file out of the list.
587 return mFiles.valueAt(0)->getPrintableSource();
588 }
589
590 // Should never hit this case, but to be safe...
591 return getPath();
592
593}
594
595// =========================================================================
596// =========================================================================
597// =========================================================================
598
599status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
600{
601 if (mFiles.indexOfKey(name) >= 0) {
602 return ALREADY_EXISTS;
603 }
604 mFiles.add(name, file);
605 return NO_ERROR;
606}
607
608status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
609{
610 if (mDirs.indexOfKey(name) >= 0) {
611 return ALREADY_EXISTS;
612 }
613 mDirs.add(name, dir);
614 return NO_ERROR;
615}
616
617sp<AaptDir> AaptDir::makeDir(const String8& path)
618{
619 String8 name;
620 String8 remain = path;
621
622 sp<AaptDir> subdir = this;
623 while (name = remain.walkPath(&remain), remain != "") {
624 subdir = subdir->makeDir(name);
625 }
626
627 ssize_t i = subdir->mDirs.indexOfKey(name);
628 if (i >= 0) {
629 return subdir->mDirs.valueAt(i);
630 }
631 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
632 subdir->mDirs.add(name, dir);
633 return dir;
634}
635
636void AaptDir::removeFile(const String8& name)
637{
638 mFiles.removeItem(name);
639}
640
641void AaptDir::removeDir(const String8& name)
642{
643 mDirs.removeItem(name);
644}
645
Adam Lesinski09384302014-01-22 16:07:42 -0800646status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
647 const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -0800648{
649 sp<AaptGroup> group;
650 if (mFiles.indexOfKey(leafName) >= 0) {
651 group = mFiles.valueFor(leafName);
652 } else {
653 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
654 mFiles.add(leafName, group);
655 }
656
Adam Lesinski09384302014-01-22 16:07:42 -0800657 return group->addFile(file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800658}
659
660ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
661 const AaptGroupEntry& kind, const String8& resType,
Adam Lesinski09384302014-01-22 16:07:42 -0800662 sp<FilePathStore>& fullResPaths, const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -0800663{
664 Vector<String8> fileNames;
665 {
666 DIR* dir = NULL;
667
668 dir = opendir(srcDir.string());
669 if (dir == NULL) {
670 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
671 return UNKNOWN_ERROR;
672 }
673
674 /*
675 * Slurp the filenames out of the directory.
676 */
677 while (1) {
678 struct dirent* entry;
679
680 entry = readdir(dir);
681 if (entry == NULL)
682 break;
683
684 if (isHidden(srcDir.string(), entry->d_name))
685 continue;
686
687 String8 name(entry->d_name);
688 fileNames.add(name);
689 // Add fully qualified path for dependency purposes
690 // if we're collecting them
691 if (fullResPaths != NULL) {
692 fullResPaths->add(srcDir.appendPathCopy(name));
693 }
694 }
695 closedir(dir);
696 }
697
698 ssize_t count = 0;
699
700 /*
701 * Stash away the files and recursively descend into subdirectories.
702 */
703 const size_t N = fileNames.size();
704 size_t i;
705 for (i = 0; i < N; i++) {
706 String8 pathName(srcDir);
707 FileType type;
708
709 pathName.appendPath(fileNames[i].string());
710 type = getFileType(pathName.string());
711 if (type == kFileTypeDirectory) {
712 sp<AaptDir> subdir;
713 bool notAdded = false;
714 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
715 subdir = mDirs.valueFor(fileNames[i]);
716 } else {
717 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
718 notAdded = true;
719 }
720 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Adam Lesinski09384302014-01-22 16:07:42 -0800721 resType, fullResPaths, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800722 if (res < NO_ERROR) {
723 return res;
724 }
725 if (res > 0 && notAdded) {
726 mDirs.add(fileNames[i], subdir);
727 }
728 count += res;
729 } else if (type == kFileTypeRegular) {
730 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
Adam Lesinski09384302014-01-22 16:07:42 -0800731 status_t err = addLeafFile(fileNames[i], file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800732 if (err != NO_ERROR) {
733 return err;
734 }
735
736 count++;
737
738 } else {
739 if (bundle->getVerbose())
740 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
741 }
742 }
743
744 return count;
745}
746
747status_t AaptDir::validate() const
748{
749 const size_t NF = mFiles.size();
750 const size_t ND = mDirs.size();
751 size_t i;
752 for (i = 0; i < NF; i++) {
753 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
754 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
755 "Invalid filename. Unable to add.");
756 return UNKNOWN_ERROR;
757 }
758
759 size_t j;
760 for (j = i+1; j < NF; j++) {
761 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
762 mFiles.valueAt(j)->getLeaf().string()) == 0) {
763 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
764 "File is case-insensitive equivalent to: %s",
765 mFiles.valueAt(j)->getPrintableSource().string());
766 return UNKNOWN_ERROR;
767 }
768
769 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
770 // (this is mostly caught by the "marked" stuff, below)
771 }
772
773 for (j = 0; j < ND; j++) {
774 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
775 mDirs.valueAt(j)->getLeaf().string()) == 0) {
776 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
777 "File conflicts with dir from: %s",
778 mDirs.valueAt(j)->getPrintableSource().string());
779 return UNKNOWN_ERROR;
780 }
781 }
782 }
783
784 for (i = 0; i < ND; i++) {
785 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
786 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
787 "Invalid directory name, unable to add.");
788 return UNKNOWN_ERROR;
789 }
790
791 size_t j;
792 for (j = i+1; j < ND; j++) {
793 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
794 mDirs.valueAt(j)->getLeaf().string()) == 0) {
795 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
796 "Directory is case-insensitive equivalent to: %s",
797 mDirs.valueAt(j)->getPrintableSource().string());
798 return UNKNOWN_ERROR;
799 }
800 }
801
802 status_t err = mDirs.valueAt(i)->validate();
803 if (err != NO_ERROR) {
804 return err;
805 }
806 }
807
808 return NO_ERROR;
809}
810
811void AaptDir::print(const String8& prefix) const
812{
813 const size_t ND=getDirs().size();
814 size_t i;
815 for (i=0; i<ND; i++) {
816 getDirs().valueAt(i)->print(prefix);
817 }
818
819 const size_t NF=getFiles().size();
820 for (i=0; i<NF; i++) {
821 getFiles().valueAt(i)->print(prefix);
822 }
823}
824
825String8 AaptDir::getPrintableSource() const
826{
827 if (mFiles.size() > 0) {
828 // Arbitrarily pull the first file out of the list as the source dir.
829 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
830 }
831 if (mDirs.size() > 0) {
832 // Or arbitrarily pull the first dir out of the list as the source dir.
833 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
834 }
835
836 // Should never hit this case, but to be safe...
837 return mPath;
838
839}
840
841// =========================================================================
842// =========================================================================
843// =========================================================================
844
845status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
846{
847 status_t err = NO_ERROR;
848 size_t N = javaSymbols->mSymbols.size();
849 for (size_t i=0; i<N; i++) {
850 const String8& name = javaSymbols->mSymbols.keyAt(i);
851 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
852 ssize_t pos = mSymbols.indexOfKey(name);
853 if (pos < 0) {
854 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
855 err = UNKNOWN_ERROR;
856 continue;
857 }
858 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
859 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
860 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
861 }
862
863 N = javaSymbols->mNestedSymbols.size();
864 for (size_t i=0; i<N; i++) {
865 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
866 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
867 ssize_t pos = mNestedSymbols.indexOfKey(name);
868 if (pos < 0) {
869 SourcePos pos;
870 pos.error("Java symbol dir %s not defined\n", name.string());
871 err = UNKNOWN_ERROR;
872 continue;
873 }
874 //printf("**** applying java symbols in dir %s\n", name.string());
875 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
876 if (myerr != NO_ERROR) {
877 err = myerr;
878 }
879 }
880
881 return err;
882}
883
884// =========================================================================
885// =========================================================================
886// =========================================================================
887
888AaptAssets::AaptAssets()
889 : AaptDir(String8(), String8()),
Narayan Kamath91447d82014-01-21 15:32:36 +0000890 mHavePrivateSymbols(false),
891 mChanged(false), mHaveIncludedAssets(false),
Adam Lesinskifab50872014-04-16 14:40:42 -0700892 mRes(NULL) {}
Adam Lesinski282e1812014-01-23 18:17:42 -0800893
894const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
895 if (mChanged) {
896 }
897 return mGroupEntries;
898}
899
900status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
901{
902 mChanged = true;
903 return AaptDir::addFile(name, file);
904}
905
906sp<AaptFile> AaptAssets::addFile(
907 const String8& filePath, const AaptGroupEntry& entry,
908 const String8& srcDir, sp<AaptGroup>* outGroup,
909 const String8& resType)
910{
911 sp<AaptDir> dir = this;
912 sp<AaptGroup> group;
913 sp<AaptFile> file;
914 String8 root, remain(filePath), partialPath;
915 while (remain.length() > 0) {
916 root = remain.walkPath(&remain);
917 partialPath.appendPath(root);
918
919 const String8 rootStr(root);
920
921 if (remain.length() == 0) {
922 ssize_t i = dir->getFiles().indexOfKey(rootStr);
923 if (i >= 0) {
924 group = dir->getFiles().valueAt(i);
925 } else {
926 group = new AaptGroup(rootStr, filePath);
927 status_t res = dir->addFile(rootStr, group);
928 if (res != NO_ERROR) {
929 return NULL;
930 }
931 }
932 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
933 status_t res = group->addFile(file);
934 if (res != NO_ERROR) {
935 return NULL;
936 }
937 break;
938
939 } else {
940 ssize_t i = dir->getDirs().indexOfKey(rootStr);
941 if (i >= 0) {
942 dir = dir->getDirs().valueAt(i);
943 } else {
944 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
945 status_t res = dir->addDir(rootStr, subdir);
946 if (res != NO_ERROR) {
947 return NULL;
948 }
949 dir = subdir;
950 }
951 }
952 }
953
954 mGroupEntries.add(entry);
955 if (outGroup) *outGroup = group;
956 return file;
957}
958
959void AaptAssets::addResource(const String8& leafName, const String8& path,
960 const sp<AaptFile>& file, const String8& resType)
961{
962 sp<AaptDir> res = AaptDir::makeDir(kResString);
963 String8 dirname = file->getGroupEntry().toDirName(resType);
964 sp<AaptDir> subdir = res->makeDir(dirname);
965 sp<AaptGroup> grr = new AaptGroup(leafName, path);
966 grr->addFile(file);
967
968 subdir->addFile(leafName, grr);
969}
970
971
972ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
973{
974 int count;
975 int totalCount = 0;
976 FileType type;
977 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
978 const size_t dirCount =resDirs.size();
979 sp<AaptAssets> current = this;
980
981 const int N = bundle->getFileSpecCount();
982
983 /*
984 * If a package manifest was specified, include that first.
985 */
986 if (bundle->getAndroidManifestFile() != NULL) {
987 // place at root of zip.
988 String8 srcFile(bundle->getAndroidManifestFile());
989 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
990 NULL, String8());
991 totalCount++;
992 }
993
994 /*
995 * If a directory of custom assets was supplied, slurp 'em up.
996 */
Adam Lesinski09384302014-01-22 16:07:42 -0800997 const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
998 const int AN = assetDirs.size();
999 for (int i = 0; i < AN; i++) {
1000 FileType type = getFileType(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001001 if (type == kFileTypeNonexistent) {
Adam Lesinski09384302014-01-22 16:07:42 -08001002 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001003 return UNKNOWN_ERROR;
1004 }
1005 if (type != kFileTypeDirectory) {
Adam Lesinski09384302014-01-22 16:07:42 -08001006 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001007 return UNKNOWN_ERROR;
1008 }
1009
Adam Lesinski09384302014-01-22 16:07:42 -08001010 String8 assetRoot(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001011 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1012 AaptGroupEntry group;
1013 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Adam Lesinski09384302014-01-22 16:07:42 -08001014 String8(), mFullAssetPaths, true);
Adam Lesinski282e1812014-01-23 18:17:42 -08001015 if (count < 0) {
1016 totalCount = count;
1017 goto bail;
1018 }
1019 if (count > 0) {
1020 mGroupEntries.add(group);
1021 }
1022 totalCount += count;
1023
Adam Lesinski09384302014-01-22 16:07:42 -08001024 if (bundle->getVerbose()) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001025 printf("Found %d custom asset file%s in %s\n",
Adam Lesinski09384302014-01-22 16:07:42 -08001026 count, (count==1) ? "" : "s", assetDirs[i]);
1027 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001028 }
1029
1030 /*
1031 * If a directory of resource-specific assets was supplied, slurp 'em up.
1032 */
1033 for (size_t i=0; i<dirCount; i++) {
1034 const char *res = resDirs[i];
1035 if (res) {
1036 type = getFileType(res);
1037 if (type == kFileTypeNonexistent) {
1038 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1039 return UNKNOWN_ERROR;
1040 }
1041 if (type == kFileTypeDirectory) {
1042 if (i>0) {
1043 sp<AaptAssets> nextOverlay = new AaptAssets();
1044 current->setOverlay(nextOverlay);
1045 current = nextOverlay;
1046 current->setFullResPaths(mFullResPaths);
1047 }
1048 count = current->slurpResourceTree(bundle, String8(res));
Bryan Mawhinney9ab9b932014-01-24 16:18:13 +00001049 if (i > 0 && count > 0) {
1050 count = current->filter(bundle);
1051 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001052
1053 if (count < 0) {
1054 totalCount = count;
1055 goto bail;
1056 }
1057 totalCount += count;
1058 }
1059 else {
1060 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1061 return UNKNOWN_ERROR;
1062 }
1063 }
1064
1065 }
1066 /*
1067 * Now do any additional raw files.
1068 */
1069 for (int arg=0; arg<N; arg++) {
1070 const char* assetDir = bundle->getFileSpecEntry(arg);
1071
1072 FileType type = getFileType(assetDir);
1073 if (type == kFileTypeNonexistent) {
1074 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1075 return UNKNOWN_ERROR;
1076 }
1077 if (type != kFileTypeDirectory) {
1078 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1079 return UNKNOWN_ERROR;
1080 }
1081
1082 String8 assetRoot(assetDir);
1083
1084 if (bundle->getVerbose())
1085 printf("Processing raw dir '%s'\n", (const char*) assetDir);
1086
1087 /*
1088 * Do a recursive traversal of subdir tree. We don't make any
1089 * guarantees about ordering, so we're okay with an inorder search
1090 * using whatever order the OS happens to hand back to us.
1091 */
1092 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
1093 if (count < 0) {
1094 /* failure; report error and remove archive */
1095 totalCount = count;
1096 goto bail;
1097 }
1098 totalCount += count;
1099
1100 if (bundle->getVerbose())
1101 printf("Found %d asset file%s in %s\n",
1102 count, (count==1) ? "" : "s", assetDir);
1103 }
1104
1105 count = validate();
1106 if (count != NO_ERROR) {
1107 totalCount = count;
1108 goto bail;
1109 }
1110
1111 count = filter(bundle);
1112 if (count != NO_ERROR) {
1113 totalCount = count;
1114 goto bail;
1115 }
1116
1117bail:
1118 return totalCount;
1119}
1120
1121ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1122 const AaptGroupEntry& kind,
1123 const String8& resType,
Adam Lesinski40e8eef2014-09-16 14:43:29 -07001124 sp<FilePathStore>& fullResPaths,
1125 const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -08001126{
Adam Lesinski40e8eef2014-09-16 14:43:29 -07001127 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -08001128 if (res > 0) {
1129 mGroupEntries.add(kind);
1130 }
1131
1132 return res;
1133}
1134
1135ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1136{
1137 ssize_t err = 0;
1138
1139 DIR* dir = opendir(srcDir.string());
1140 if (dir == NULL) {
1141 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1142 return UNKNOWN_ERROR;
1143 }
1144
1145 status_t count = 0;
1146
1147 /*
1148 * Run through the directory, looking for dirs that match the
1149 * expected pattern.
1150 */
1151 while (1) {
1152 struct dirent* entry = readdir(dir);
1153 if (entry == NULL) {
1154 break;
1155 }
1156
1157 if (isHidden(srcDir.string(), entry->d_name)) {
1158 continue;
1159 }
1160
1161 String8 subdirName(srcDir);
1162 subdirName.appendPath(entry->d_name);
1163
1164 AaptGroupEntry group;
1165 String8 resType;
1166 bool b = group.initFromDirName(entry->d_name, &resType);
1167 if (!b) {
Adam Lesinskifab50872014-04-16 14:40:42 -07001168 fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
Adam Lesinski282e1812014-01-23 18:17:42 -08001169 entry->d_name);
1170 err = -1;
1171 continue;
1172 }
1173
1174 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
1175 int maxResInt = atoi(bundle->getMaxResVersion());
1176 const char *verString = group.getVersionString().string();
1177 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
1178 if (dirVersionInt > maxResInt) {
1179 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
1180 continue;
1181 }
1182 }
1183
1184 FileType type = getFileType(subdirName.string());
1185
1186 if (type == kFileTypeDirectory) {
1187 sp<AaptDir> dir = makeDir(resType);
1188 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1189 resType, mFullResPaths);
1190 if (res < 0) {
1191 count = res;
1192 goto bail;
1193 }
1194 if (res > 0) {
1195 mGroupEntries.add(group);
1196 count += res;
1197 }
1198
1199 // Only add this directory if we don't already have a resource dir
1200 // for the current type. This ensures that we only add the dir once
1201 // for all configs.
1202 sp<AaptDir> rdir = resDir(resType);
1203 if (rdir == NULL) {
1204 mResDirs.add(dir);
1205 }
1206 } else {
1207 if (bundle->getVerbose()) {
1208 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
1209 }
1210 }
1211 }
1212
1213bail:
1214 closedir(dir);
1215 dir = NULL;
1216
1217 if (err != 0) {
1218 return err;
1219 }
1220 return count;
1221}
1222
1223ssize_t
Andreas Gampe2412f842014-09-30 20:55:57 -07001224AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename)
Adam Lesinski282e1812014-01-23 18:17:42 -08001225{
1226 int count = 0;
1227 SortedVector<AaptGroupEntry> entries;
1228
1229 ZipFile* zip = new ZipFile;
1230 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1231 if (err != NO_ERROR) {
1232 fprintf(stderr, "error opening zip file %s\n", filename);
1233 count = err;
1234 delete zip;
1235 return -1;
1236 }
1237
1238 const int N = zip->getNumEntries();
1239 for (int i=0; i<N; i++) {
1240 ZipEntry* entry = zip->getEntryByIndex(i);
1241 if (entry->getDeleted()) {
1242 continue;
1243 }
1244
1245 String8 entryName(entry->getFileName());
1246
1247 String8 dirName = entryName.getPathDir();
1248 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1249
1250 String8 resType;
1251 AaptGroupEntry kind;
1252
1253 String8 remain;
1254 if (entryName.walkPath(&remain) == kResourceDir) {
1255 // these are the resources, pull their type out of the directory name
1256 kind.initFromDirName(remain.walkPath().string(), &resType);
1257 } else {
1258 // these are untyped and don't have an AaptGroupEntry
1259 }
1260 if (entries.indexOf(kind) < 0) {
1261 entries.add(kind);
1262 mGroupEntries.add(kind);
1263 }
1264
1265 // use the one from the zip file if they both exist.
1266 dir->removeFile(entryName.getPathLeaf());
1267
1268 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
1269 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
1270 if (err != NO_ERROR) {
1271 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
1272 count = err;
1273 goto bail;
1274 }
1275 file->setCompressionMethod(entry->getCompressionMethod());
1276
1277#if 0
1278 if (entryName == "AndroidManifest.xml") {
1279 printf("AndroidManifest.xml\n");
1280 }
1281 printf("\n\nfile: %s\n", entryName.string());
1282#endif
1283
1284 size_t len = entry->getUncompressedLen();
1285 void* data = zip->uncompress(entry);
1286 void* buf = file->editData(len);
1287 memcpy(buf, data, len);
1288
1289#if 0
1290 const int OFF = 0;
1291 const unsigned char* p = (unsigned char*)data;
1292 const unsigned char* end = p+len;
1293 p += OFF;
1294 for (int i=0; i<32 && p < end; i++) {
1295 printf("0x%03x ", i*0x10 + OFF);
1296 for (int j=0; j<0x10 && p < end; j++) {
1297 printf(" %02x", *p);
1298 p++;
1299 }
1300 printf("\n");
1301 }
1302#endif
1303
1304 free(data);
1305
1306 count++;
1307 }
1308
1309bail:
1310 delete zip;
1311 return count;
1312}
1313
1314status_t AaptAssets::filter(Bundle* bundle)
1315{
Adam Lesinskifab50872014-04-16 14:40:42 -07001316 WeakResourceFilter reqFilter;
Adam Lesinski282e1812014-01-23 18:17:42 -08001317 status_t err = reqFilter.parse(bundle->getConfigurations());
1318 if (err != NO_ERROR) {
1319 return err;
1320 }
1321
Adam Lesinskifab50872014-04-16 14:40:42 -07001322 uint32_t preferredDensity = 0;
1323 if (bundle->getPreferredDensity().size() > 0) {
1324 ResTable_config preferredConfig;
1325 if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
1326 fprintf(stderr, "Error parsing preferred density: %s\n",
1327 bundle->getPreferredDensity().string());
1328 return UNKNOWN_ERROR;
1329 }
1330 preferredDensity = preferredConfig.density;
Adam Lesinski282e1812014-01-23 18:17:42 -08001331 }
1332
Adam Lesinskifab50872014-04-16 14:40:42 -07001333 if (reqFilter.isEmpty() && preferredDensity == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001334 return NO_ERROR;
1335 }
1336
1337 if (bundle->getVerbose()) {
1338 if (!reqFilter.isEmpty()) {
1339 printf("Applying required filter: %s\n",
Adam Lesinskifab50872014-04-16 14:40:42 -07001340 bundle->getConfigurations().string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001341 }
Adam Lesinskifab50872014-04-16 14:40:42 -07001342 if (preferredDensity > 0) {
1343 printf("Applying preferred density filter: %s\n",
1344 bundle->getPreferredDensity().string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001345 }
1346 }
1347
1348 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1349 const size_t ND = resdirs.size();
1350 for (size_t i=0; i<ND; i++) {
1351 const sp<AaptDir>& dir = resdirs.itemAt(i);
1352 if (dir->getLeaf() == kValuesDir) {
1353 // The "value" dir is special since a single file defines
1354 // multiple resources, so we can not do filtering on the
1355 // files themselves.
1356 continue;
1357 }
1358 if (dir->getLeaf() == kMipmapDir) {
1359 // We also skip the "mipmap" directory, since the point of this
1360 // is to include all densities without stripping. If you put
1361 // other configurations in here as well they won't be stripped
1362 // either... So don't do that. Seriously. What is wrong with you?
1363 continue;
1364 }
1365
1366 const size_t NG = dir->getFiles().size();
1367 for (size_t j=0; j<NG; j++) {
1368 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
1369
1370 // First remove any configurations we know we don't need.
1371 for (size_t k=0; k<grp->getFiles().size(); k++) {
1372 sp<AaptFile> file = grp->getFiles().valueAt(k);
1373 if (k == 0 && grp->getFiles().size() == 1) {
1374 // If this is the only file left, we need to keep it.
1375 // Otherwise the resource IDs we are using will be inconsistent
1376 // with what we get when not stripping. Sucky, but at least
1377 // for now we can rely on the back-end doing another filtering
1378 // pass to take this out and leave us with this resource name
1379 // containing no entries.
1380 continue;
1381 }
1382 if (file->getPath().getPathExtension() == ".xml") {
1383 // We can't remove .xml files at this point, because when
1384 // we parse them they may add identifier resources, so
1385 // removing them can cause our resource identifiers to
1386 // become inconsistent.
1387 continue;
1388 }
1389 const ResTable_config& config(file->getGroupEntry().toParams());
1390 if (!reqFilter.match(config)) {
1391 if (bundle->getVerbose()) {
1392 printf("Pruning unneeded resource: %s\n",
1393 file->getPrintableSource().string());
1394 }
1395 grp->removeFile(k);
1396 k--;
1397 }
1398 }
1399
1400 // Quick check: no preferred filters, nothing more to do.
Adam Lesinskifab50872014-04-16 14:40:42 -07001401 if (preferredDensity == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001402 continue;
1403 }
1404
Adam Lesinski8cf61842013-10-18 13:42:09 -07001405 // Get the preferred density if there is one. We do not match exactly for density.
1406 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
1407 // pick xhdpi.
Adam Lesinskifab50872014-04-16 14:40:42 -07001408 for (size_t k=0; k<grp->getFiles().size(); k++) {
1409 sp<AaptFile> file = grp->getFiles().valueAt(k);
1410 if (k == 0 && grp->getFiles().size() == 1) {
1411 // If this is the only file left, we need to keep it.
1412 // Otherwise the resource IDs we are using will be inconsistent
1413 // with what we get when not stripping. Sucky, but at least
1414 // for now we can rely on the back-end doing another filtering
1415 // pass to take this out and leave us with this resource name
1416 // containing no entries.
1417 continue;
1418 }
1419 if (file->getPath().getPathExtension() == ".xml") {
1420 // We can't remove .xml files at this point, because when
1421 // we parse them they may add identifier resources, so
1422 // removing them can cause our resource identifiers to
1423 // become inconsistent.
1424 continue;
1425 }
1426 const ResTable_config& config(file->getGroupEntry().toParams());
1427 if (config.density != 0 && config.density != preferredDensity) {
1428 // This is a resource we would prefer not to have. Check
1429 // to see if have a similar variation that we would like
1430 // to have and, if so, we can drop it.
1431 uint32_t bestDensity = config.density;
Adam Lesinski8cf61842013-10-18 13:42:09 -07001432
Adam Lesinskifab50872014-04-16 14:40:42 -07001433 for (size_t m=0; m<grp->getFiles().size(); m++) {
1434 if (m == k) {
1435 continue;
Adam Lesinski282e1812014-01-23 18:17:42 -08001436 }
Adam Lesinski8cf61842013-10-18 13:42:09 -07001437
Adam Lesinskifab50872014-04-16 14:40:42 -07001438 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
1439 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
1440 if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
1441 // See if there is a better density resource
1442 if (mconfig.density < bestDensity &&
Bryan Mawhinneyb0db8de2014-06-06 13:27:11 +01001443 mconfig.density >= preferredDensity &&
Adam Lesinskifab50872014-04-16 14:40:42 -07001444 bestDensity > preferredDensity) {
Bryan Mawhinneyb0db8de2014-06-06 13:27:11 +01001445 // This density is our preferred density, or between our best density and
Adam Lesinskifab50872014-04-16 14:40:42 -07001446 // the preferred density, therefore it is better.
1447 bestDensity = mconfig.density;
1448 } else if (mconfig.density > bestDensity &&
1449 bestDensity < preferredDensity) {
1450 // This density is better than our best density and
1451 // our best density was smaller than our preferred
1452 // density, so it is better.
1453 bestDensity = mconfig.density;
Adam Lesinski8cf61842013-10-18 13:42:09 -07001454 }
Adam Lesinski8cf61842013-10-18 13:42:09 -07001455 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001456 }
Adam Lesinskifab50872014-04-16 14:40:42 -07001457
1458 if (bestDensity != config.density) {
1459 if (bundle->getVerbose()) {
1460 printf("Pruning unneeded resource: %s\n",
1461 file->getPrintableSource().string());
1462 }
1463 grp->removeFile(k);
1464 k--;
1465 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001466 }
1467 }
1468 }
1469 }
1470
1471 return NO_ERROR;
1472}
1473
1474sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1475{
1476 sp<AaptSymbols> sym = mSymbols.valueFor(name);
1477 if (sym == NULL) {
1478 sym = new AaptSymbols();
1479 mSymbols.add(name, sym);
1480 }
1481 return sym;
1482}
1483
1484sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
1485{
1486 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
1487 if (sym == NULL) {
1488 sym = new AaptSymbols();
1489 mJavaSymbols.add(name, sym);
1490 }
1491 return sym;
1492}
1493
1494status_t AaptAssets::applyJavaSymbols()
1495{
1496 size_t N = mJavaSymbols.size();
1497 for (size_t i=0; i<N; i++) {
1498 const String8& name = mJavaSymbols.keyAt(i);
1499 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
1500 ssize_t pos = mSymbols.indexOfKey(name);
1501 if (pos < 0) {
1502 SourcePos pos;
1503 pos.error("Java symbol dir %s not defined\n", name.string());
1504 return UNKNOWN_ERROR;
1505 }
1506 //printf("**** applying java symbols in dir %s\n", name.string());
1507 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1508 if (err != NO_ERROR) {
1509 return err;
1510 }
1511 }
1512
1513 return NO_ERROR;
1514}
1515
1516bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
1517 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
1518 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
1519 // sym.isJavaSymbol ? 1 : 0);
1520 if (!mHavePrivateSymbols) return true;
1521 if (sym.isPublic) return true;
1522 if (includePrivate && sym.isJavaSymbol) return true;
1523 return false;
1524}
1525
1526status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1527{
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001528 if (mHaveIncludedAssets) {
1529 return NO_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -08001530 }
1531
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001532 // Add in all includes.
1533 const Vector<String8>& includes = bundle->getPackageIncludes();
1534 const size_t packageIncludeCount = includes.size();
1535 for (size_t i = 0; i < packageIncludeCount; i++) {
1536 if (bundle->getVerbose()) {
1537 printf("Including resources from package: %s\n", includes[i].string());
1538 }
1539
1540 if (!mIncludedAssets.addAssetPath(includes[i], NULL)) {
1541 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
1542 includes[i].string());
1543 return UNKNOWN_ERROR;
1544 }
1545 }
1546
1547 const String8& featureOfBase = bundle->getFeatureOfPackage();
1548 if (!featureOfBase.isEmpty()) {
1549 if (bundle->getVerbose()) {
1550 printf("Including base feature resources from package: %s\n",
1551 featureOfBase.string());
1552 }
1553
1554 if (!mIncludedAssets.addAssetPath(featureOfBase, NULL)) {
1555 fprintf(stderr, "ERROR: base feature package '%s' not found.\n",
1556 featureOfBase.string());
1557 return UNKNOWN_ERROR;
1558 }
1559 }
1560
1561 mHaveIncludedAssets = true;
1562
Adam Lesinski282e1812014-01-23 18:17:42 -08001563 return NO_ERROR;
1564}
1565
1566status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
1567{
1568 const ResTable& res = getIncludedResources();
1569 // XXX dirty!
Narayan Kamath00b31442014-01-27 17:32:37 +00001570 return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
Adam Lesinski282e1812014-01-23 18:17:42 -08001571}
1572
1573const ResTable& AaptAssets::getIncludedResources() const
1574{
1575 return mIncludedAssets.getResources(false);
1576}
1577
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001578AssetManager& AaptAssets::getAssetManager()
1579{
1580 return mIncludedAssets;
1581}
1582
Adam Lesinski282e1812014-01-23 18:17:42 -08001583void AaptAssets::print(const String8& prefix) const
1584{
1585 String8 innerPrefix(prefix);
1586 innerPrefix.append(" ");
1587 String8 innerInnerPrefix(innerPrefix);
1588 innerInnerPrefix.append(" ");
1589 printf("%sConfigurations:\n", prefix.string());
1590 const size_t N=mGroupEntries.size();
1591 for (size_t i=0; i<N; i++) {
1592 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
1593 printf("%s %s\n", prefix.string(),
1594 cname != "" ? cname.string() : "(default)");
1595 }
1596
1597 printf("\n%sFiles:\n", prefix.string());
1598 AaptDir::print(innerPrefix);
1599
1600 printf("\n%sResource Dirs:\n", prefix.string());
1601 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1602 const size_t NR = resdirs.size();
1603 for (size_t i=0; i<NR; i++) {
1604 const sp<AaptDir>& d = resdirs.itemAt(i);
1605 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
1606 d->print(innerInnerPrefix);
1607 }
1608}
1609
1610sp<AaptDir> AaptAssets::resDir(const String8& name) const
1611{
1612 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1613 const size_t N = resdirs.size();
1614 for (size_t i=0; i<N; i++) {
1615 const sp<AaptDir>& d = resdirs.itemAt(i);
1616 if (d->getLeaf() == name) {
1617 return d;
1618 }
1619 }
1620 return NULL;
1621}
1622
1623bool
1624valid_symbol_name(const String8& symbol)
1625{
1626 static char const * const KEYWORDS[] = {
1627 "abstract", "assert", "boolean", "break",
1628 "byte", "case", "catch", "char", "class", "const", "continue",
1629 "default", "do", "double", "else", "enum", "extends", "final",
1630 "finally", "float", "for", "goto", "if", "implements", "import",
1631 "instanceof", "int", "interface", "long", "native", "new", "package",
1632 "private", "protected", "public", "return", "short", "static",
1633 "strictfp", "super", "switch", "synchronized", "this", "throw",
1634 "throws", "transient", "try", "void", "volatile", "while",
1635 "true", "false", "null",
1636 NULL
1637 };
1638 const char*const* k = KEYWORDS;
1639 const char*const s = symbol.string();
1640 while (*k) {
1641 if (0 == strcmp(s, *k)) {
1642 return false;
1643 }
1644 k++;
1645 }
1646 return true;
1647}