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