blob: 12d53898a2d0e32bfad41e5cadb70d7f6a28829a [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
564#if 0
565 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
566 file->getSourceFile().string(),
567 file->getGroupEntry().toDirName(String8()).string(),
568 mLeaf.string(), mPath.string());
569#endif
570
571 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
572 getPrintableSource().string());
573 return UNKNOWN_ERROR;
574}
575
576void AaptGroup::removeFile(size_t index)
577{
578 mFiles.removeItemsAt(index);
579}
580
581void AaptGroup::print(const String8& prefix) const
582{
583 printf("%s%s\n", prefix.string(), getPath().string());
584 const size_t N=mFiles.size();
585 size_t i;
586 for (i=0; i<N; i++) {
587 sp<AaptFile> file = mFiles.valueAt(i);
588 const AaptGroupEntry& e = file->getGroupEntry();
589 if (file->hasData()) {
590 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
591 (int)file->getSize());
592 } else {
593 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
594 file->getPrintableSource().string());
595 }
596 //printf("%s File Group Entry: %s\n", prefix.string(),
597 // file->getGroupEntry().toDirName(String8()).string());
598 }
599}
600
601String8 AaptGroup::getPrintableSource() const
602{
603 if (mFiles.size() > 0) {
604 // Arbitrarily pull the first source file out of the list.
605 return mFiles.valueAt(0)->getPrintableSource();
606 }
607
608 // Should never hit this case, but to be safe...
609 return getPath();
610
611}
612
613// =========================================================================
614// =========================================================================
615// =========================================================================
616
617status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
618{
619 if (mFiles.indexOfKey(name) >= 0) {
620 return ALREADY_EXISTS;
621 }
622 mFiles.add(name, file);
623 return NO_ERROR;
624}
625
626status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
627{
628 if (mDirs.indexOfKey(name) >= 0) {
629 return ALREADY_EXISTS;
630 }
631 mDirs.add(name, dir);
632 return NO_ERROR;
633}
634
635sp<AaptDir> AaptDir::makeDir(const String8& path)
636{
637 String8 name;
638 String8 remain = path;
639
640 sp<AaptDir> subdir = this;
641 while (name = remain.walkPath(&remain), remain != "") {
642 subdir = subdir->makeDir(name);
643 }
644
645 ssize_t i = subdir->mDirs.indexOfKey(name);
646 if (i >= 0) {
647 return subdir->mDirs.valueAt(i);
648 }
649 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
650 subdir->mDirs.add(name, dir);
651 return dir;
652}
653
654void AaptDir::removeFile(const String8& name)
655{
656 mFiles.removeItem(name);
657}
658
659void AaptDir::removeDir(const String8& name)
660{
661 mDirs.removeItem(name);
662}
663
Adam Lesinski09384302014-01-22 16:07:42 -0800664status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
665 const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -0800666{
667 sp<AaptGroup> group;
668 if (mFiles.indexOfKey(leafName) >= 0) {
669 group = mFiles.valueFor(leafName);
670 } else {
671 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
672 mFiles.add(leafName, group);
673 }
674
Adam Lesinski09384302014-01-22 16:07:42 -0800675 return group->addFile(file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800676}
677
678ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
679 const AaptGroupEntry& kind, const String8& resType,
Adam Lesinski09384302014-01-22 16:07:42 -0800680 sp<FilePathStore>& fullResPaths, const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -0800681{
682 Vector<String8> fileNames;
683 {
684 DIR* dir = NULL;
685
686 dir = opendir(srcDir.string());
687 if (dir == NULL) {
688 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
689 return UNKNOWN_ERROR;
690 }
691
692 /*
693 * Slurp the filenames out of the directory.
694 */
695 while (1) {
696 struct dirent* entry;
697
698 entry = readdir(dir);
699 if (entry == NULL)
700 break;
701
702 if (isHidden(srcDir.string(), entry->d_name))
703 continue;
704
705 String8 name(entry->d_name);
706 fileNames.add(name);
707 // Add fully qualified path for dependency purposes
708 // if we're collecting them
709 if (fullResPaths != NULL) {
710 fullResPaths->add(srcDir.appendPathCopy(name));
711 }
712 }
713 closedir(dir);
714 }
715
716 ssize_t count = 0;
717
718 /*
719 * Stash away the files and recursively descend into subdirectories.
720 */
721 const size_t N = fileNames.size();
722 size_t i;
723 for (i = 0; i < N; i++) {
724 String8 pathName(srcDir);
725 FileType type;
726
727 pathName.appendPath(fileNames[i].string());
728 type = getFileType(pathName.string());
729 if (type == kFileTypeDirectory) {
730 sp<AaptDir> subdir;
731 bool notAdded = false;
732 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
733 subdir = mDirs.valueFor(fileNames[i]);
734 } else {
735 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
736 notAdded = true;
737 }
738 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Adam Lesinski09384302014-01-22 16:07:42 -0800739 resType, fullResPaths, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800740 if (res < NO_ERROR) {
741 return res;
742 }
743 if (res > 0 && notAdded) {
744 mDirs.add(fileNames[i], subdir);
745 }
746 count += res;
747 } else if (type == kFileTypeRegular) {
748 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
Adam Lesinski09384302014-01-22 16:07:42 -0800749 status_t err = addLeafFile(fileNames[i], file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800750 if (err != NO_ERROR) {
751 return err;
752 }
753
754 count++;
755
756 } else {
757 if (bundle->getVerbose())
758 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
759 }
760 }
761
762 return count;
763}
764
765status_t AaptDir::validate() const
766{
767 const size_t NF = mFiles.size();
768 const size_t ND = mDirs.size();
769 size_t i;
770 for (i = 0; i < NF; i++) {
771 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
772 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
773 "Invalid filename. Unable to add.");
774 return UNKNOWN_ERROR;
775 }
776
777 size_t j;
778 for (j = i+1; j < NF; j++) {
779 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
780 mFiles.valueAt(j)->getLeaf().string()) == 0) {
781 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
782 "File is case-insensitive equivalent to: %s",
783 mFiles.valueAt(j)->getPrintableSource().string());
784 return UNKNOWN_ERROR;
785 }
786
787 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
788 // (this is mostly caught by the "marked" stuff, below)
789 }
790
791 for (j = 0; j < ND; j++) {
792 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
793 mDirs.valueAt(j)->getLeaf().string()) == 0) {
794 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
795 "File conflicts with dir from: %s",
796 mDirs.valueAt(j)->getPrintableSource().string());
797 return UNKNOWN_ERROR;
798 }
799 }
800 }
801
802 for (i = 0; i < ND; i++) {
803 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
804 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
805 "Invalid directory name, unable to add.");
806 return UNKNOWN_ERROR;
807 }
808
809 size_t j;
810 for (j = i+1; j < ND; j++) {
811 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
812 mDirs.valueAt(j)->getLeaf().string()) == 0) {
813 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
814 "Directory is case-insensitive equivalent to: %s",
815 mDirs.valueAt(j)->getPrintableSource().string());
816 return UNKNOWN_ERROR;
817 }
818 }
819
820 status_t err = mDirs.valueAt(i)->validate();
821 if (err != NO_ERROR) {
822 return err;
823 }
824 }
825
826 return NO_ERROR;
827}
828
829void AaptDir::print(const String8& prefix) const
830{
831 const size_t ND=getDirs().size();
832 size_t i;
833 for (i=0; i<ND; i++) {
834 getDirs().valueAt(i)->print(prefix);
835 }
836
837 const size_t NF=getFiles().size();
838 for (i=0; i<NF; i++) {
839 getFiles().valueAt(i)->print(prefix);
840 }
841}
842
843String8 AaptDir::getPrintableSource() const
844{
845 if (mFiles.size() > 0) {
846 // Arbitrarily pull the first file out of the list as the source dir.
847 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
848 }
849 if (mDirs.size() > 0) {
850 // Or arbitrarily pull the first dir out of the list as the source dir.
851 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
852 }
853
854 // Should never hit this case, but to be safe...
855 return mPath;
856
857}
858
859// =========================================================================
860// =========================================================================
861// =========================================================================
862
863status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
864{
865 status_t err = NO_ERROR;
866 size_t N = javaSymbols->mSymbols.size();
867 for (size_t i=0; i<N; i++) {
868 const String8& name = javaSymbols->mSymbols.keyAt(i);
869 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
870 ssize_t pos = mSymbols.indexOfKey(name);
871 if (pos < 0) {
872 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
873 err = UNKNOWN_ERROR;
874 continue;
875 }
876 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
877 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
878 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
879 }
880
881 N = javaSymbols->mNestedSymbols.size();
882 for (size_t i=0; i<N; i++) {
883 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
884 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
885 ssize_t pos = mNestedSymbols.indexOfKey(name);
886 if (pos < 0) {
887 SourcePos pos;
888 pos.error("Java symbol dir %s not defined\n", name.string());
889 err = UNKNOWN_ERROR;
890 continue;
891 }
892 //printf("**** applying java symbols in dir %s\n", name.string());
893 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
894 if (myerr != NO_ERROR) {
895 err = myerr;
896 }
897 }
898
899 return err;
900}
901
902// =========================================================================
903// =========================================================================
904// =========================================================================
905
906AaptAssets::AaptAssets()
907 : AaptDir(String8(), String8()),
Narayan Kamath91447d82014-01-21 15:32:36 +0000908 mHavePrivateSymbols(false),
909 mChanged(false), mHaveIncludedAssets(false),
Adam Lesinskifab50872014-04-16 14:40:42 -0700910 mRes(NULL) {}
Adam Lesinski282e1812014-01-23 18:17:42 -0800911
912const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
913 if (mChanged) {
914 }
915 return mGroupEntries;
916}
917
918status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
919{
920 mChanged = true;
921 return AaptDir::addFile(name, file);
922}
923
924sp<AaptFile> AaptAssets::addFile(
925 const String8& filePath, const AaptGroupEntry& entry,
926 const String8& srcDir, sp<AaptGroup>* outGroup,
927 const String8& resType)
928{
929 sp<AaptDir> dir = this;
930 sp<AaptGroup> group;
931 sp<AaptFile> file;
932 String8 root, remain(filePath), partialPath;
933 while (remain.length() > 0) {
934 root = remain.walkPath(&remain);
935 partialPath.appendPath(root);
936
937 const String8 rootStr(root);
938
939 if (remain.length() == 0) {
940 ssize_t i = dir->getFiles().indexOfKey(rootStr);
941 if (i >= 0) {
942 group = dir->getFiles().valueAt(i);
943 } else {
944 group = new AaptGroup(rootStr, filePath);
945 status_t res = dir->addFile(rootStr, group);
946 if (res != NO_ERROR) {
947 return NULL;
948 }
949 }
950 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
951 status_t res = group->addFile(file);
952 if (res != NO_ERROR) {
953 return NULL;
954 }
955 break;
956
957 } else {
958 ssize_t i = dir->getDirs().indexOfKey(rootStr);
959 if (i >= 0) {
960 dir = dir->getDirs().valueAt(i);
961 } else {
962 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
963 status_t res = dir->addDir(rootStr, subdir);
964 if (res != NO_ERROR) {
965 return NULL;
966 }
967 dir = subdir;
968 }
969 }
970 }
971
972 mGroupEntries.add(entry);
973 if (outGroup) *outGroup = group;
974 return file;
975}
976
977void AaptAssets::addResource(const String8& leafName, const String8& path,
978 const sp<AaptFile>& file, const String8& resType)
979{
980 sp<AaptDir> res = AaptDir::makeDir(kResString);
981 String8 dirname = file->getGroupEntry().toDirName(resType);
982 sp<AaptDir> subdir = res->makeDir(dirname);
983 sp<AaptGroup> grr = new AaptGroup(leafName, path);
984 grr->addFile(file);
985
986 subdir->addFile(leafName, grr);
987}
988
989
990ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
991{
992 int count;
993 int totalCount = 0;
994 FileType type;
995 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
996 const size_t dirCount =resDirs.size();
997 sp<AaptAssets> current = this;
998
999 const int N = bundle->getFileSpecCount();
1000
1001 /*
1002 * If a package manifest was specified, include that first.
1003 */
1004 if (bundle->getAndroidManifestFile() != NULL) {
1005 // place at root of zip.
1006 String8 srcFile(bundle->getAndroidManifestFile());
1007 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1008 NULL, String8());
1009 totalCount++;
1010 }
1011
1012 /*
1013 * If a directory of custom assets was supplied, slurp 'em up.
1014 */
Adam Lesinski09384302014-01-22 16:07:42 -08001015 const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
1016 const int AN = assetDirs.size();
1017 for (int i = 0; i < AN; i++) {
1018 FileType type = getFileType(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001019 if (type == kFileTypeNonexistent) {
Adam Lesinski09384302014-01-22 16:07:42 -08001020 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001021 return UNKNOWN_ERROR;
1022 }
1023 if (type != kFileTypeDirectory) {
Adam Lesinski09384302014-01-22 16:07:42 -08001024 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001025 return UNKNOWN_ERROR;
1026 }
1027
Adam Lesinski09384302014-01-22 16:07:42 -08001028 String8 assetRoot(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001029 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1030 AaptGroupEntry group;
1031 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Adam Lesinski09384302014-01-22 16:07:42 -08001032 String8(), mFullAssetPaths, true);
Adam Lesinski282e1812014-01-23 18:17:42 -08001033 if (count < 0) {
1034 totalCount = count;
1035 goto bail;
1036 }
1037 if (count > 0) {
1038 mGroupEntries.add(group);
1039 }
1040 totalCount += count;
1041
Adam Lesinski09384302014-01-22 16:07:42 -08001042 if (bundle->getVerbose()) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001043 printf("Found %d custom asset file%s in %s\n",
Adam Lesinski09384302014-01-22 16:07:42 -08001044 count, (count==1) ? "" : "s", assetDirs[i]);
1045 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001046 }
1047
1048 /*
1049 * If a directory of resource-specific assets was supplied, slurp 'em up.
1050 */
1051 for (size_t i=0; i<dirCount; i++) {
1052 const char *res = resDirs[i];
1053 if (res) {
1054 type = getFileType(res);
1055 if (type == kFileTypeNonexistent) {
1056 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1057 return UNKNOWN_ERROR;
1058 }
1059 if (type == kFileTypeDirectory) {
1060 if (i>0) {
1061 sp<AaptAssets> nextOverlay = new AaptAssets();
1062 current->setOverlay(nextOverlay);
1063 current = nextOverlay;
1064 current->setFullResPaths(mFullResPaths);
1065 }
1066 count = current->slurpResourceTree(bundle, String8(res));
Bryan Mawhinney9ab9b932014-01-24 16:18:13 +00001067 if (i > 0 && count > 0) {
1068 count = current->filter(bundle);
1069 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001070
1071 if (count < 0) {
1072 totalCount = count;
1073 goto bail;
1074 }
1075 totalCount += count;
1076 }
1077 else {
1078 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1079 return UNKNOWN_ERROR;
1080 }
1081 }
1082
1083 }
1084 /*
1085 * Now do any additional raw files.
1086 */
1087 for (int arg=0; arg<N; arg++) {
1088 const char* assetDir = bundle->getFileSpecEntry(arg);
1089
1090 FileType type = getFileType(assetDir);
1091 if (type == kFileTypeNonexistent) {
1092 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1093 return UNKNOWN_ERROR;
1094 }
1095 if (type != kFileTypeDirectory) {
1096 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1097 return UNKNOWN_ERROR;
1098 }
1099
1100 String8 assetRoot(assetDir);
1101
1102 if (bundle->getVerbose())
1103 printf("Processing raw dir '%s'\n", (const char*) assetDir);
1104
1105 /*
1106 * Do a recursive traversal of subdir tree. We don't make any
1107 * guarantees about ordering, so we're okay with an inorder search
1108 * using whatever order the OS happens to hand back to us.
1109 */
1110 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
1111 if (count < 0) {
1112 /* failure; report error and remove archive */
1113 totalCount = count;
1114 goto bail;
1115 }
1116 totalCount += count;
1117
1118 if (bundle->getVerbose())
1119 printf("Found %d asset file%s in %s\n",
1120 count, (count==1) ? "" : "s", assetDir);
1121 }
1122
1123 count = validate();
1124 if (count != NO_ERROR) {
1125 totalCount = count;
1126 goto bail;
1127 }
1128
1129 count = filter(bundle);
1130 if (count != NO_ERROR) {
1131 totalCount = count;
1132 goto bail;
1133 }
1134
1135bail:
1136 return totalCount;
1137}
1138
1139ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1140 const AaptGroupEntry& kind,
1141 const String8& resType,
1142 sp<FilePathStore>& fullResPaths)
1143{
1144 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
1145 if (res > 0) {
1146 mGroupEntries.add(kind);
1147 }
1148
1149 return res;
1150}
1151
1152ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1153{
1154 ssize_t err = 0;
1155
1156 DIR* dir = opendir(srcDir.string());
1157 if (dir == NULL) {
1158 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1159 return UNKNOWN_ERROR;
1160 }
1161
1162 status_t count = 0;
1163
1164 /*
1165 * Run through the directory, looking for dirs that match the
1166 * expected pattern.
1167 */
1168 while (1) {
1169 struct dirent* entry = readdir(dir);
1170 if (entry == NULL) {
1171 break;
1172 }
1173
1174 if (isHidden(srcDir.string(), entry->d_name)) {
1175 continue;
1176 }
1177
1178 String8 subdirName(srcDir);
1179 subdirName.appendPath(entry->d_name);
1180
1181 AaptGroupEntry group;
1182 String8 resType;
1183 bool b = group.initFromDirName(entry->d_name, &resType);
1184 if (!b) {
Adam Lesinskifab50872014-04-16 14:40:42 -07001185 fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
Adam Lesinski282e1812014-01-23 18:17:42 -08001186 entry->d_name);
1187 err = -1;
1188 continue;
1189 }
1190
1191 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
1192 int maxResInt = atoi(bundle->getMaxResVersion());
1193 const char *verString = group.getVersionString().string();
1194 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
1195 if (dirVersionInt > maxResInt) {
1196 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
1197 continue;
1198 }
1199 }
1200
1201 FileType type = getFileType(subdirName.string());
1202
1203 if (type == kFileTypeDirectory) {
1204 sp<AaptDir> dir = makeDir(resType);
1205 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1206 resType, mFullResPaths);
1207 if (res < 0) {
1208 count = res;
1209 goto bail;
1210 }
1211 if (res > 0) {
1212 mGroupEntries.add(group);
1213 count += res;
1214 }
1215
1216 // Only add this directory if we don't already have a resource dir
1217 // for the current type. This ensures that we only add the dir once
1218 // for all configs.
1219 sp<AaptDir> rdir = resDir(resType);
1220 if (rdir == NULL) {
1221 mResDirs.add(dir);
1222 }
1223 } else {
1224 if (bundle->getVerbose()) {
1225 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
1226 }
1227 }
1228 }
1229
1230bail:
1231 closedir(dir);
1232 dir = NULL;
1233
1234 if (err != 0) {
1235 return err;
1236 }
1237 return count;
1238}
1239
1240ssize_t
1241AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
1242{
1243 int count = 0;
1244 SortedVector<AaptGroupEntry> entries;
1245
1246 ZipFile* zip = new ZipFile;
1247 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1248 if (err != NO_ERROR) {
1249 fprintf(stderr, "error opening zip file %s\n", filename);
1250 count = err;
1251 delete zip;
1252 return -1;
1253 }
1254
1255 const int N = zip->getNumEntries();
1256 for (int i=0; i<N; i++) {
1257 ZipEntry* entry = zip->getEntryByIndex(i);
1258 if (entry->getDeleted()) {
1259 continue;
1260 }
1261
1262 String8 entryName(entry->getFileName());
1263
1264 String8 dirName = entryName.getPathDir();
1265 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1266
1267 String8 resType;
1268 AaptGroupEntry kind;
1269
1270 String8 remain;
1271 if (entryName.walkPath(&remain) == kResourceDir) {
1272 // these are the resources, pull their type out of the directory name
1273 kind.initFromDirName(remain.walkPath().string(), &resType);
1274 } else {
1275 // these are untyped and don't have an AaptGroupEntry
1276 }
1277 if (entries.indexOf(kind) < 0) {
1278 entries.add(kind);
1279 mGroupEntries.add(kind);
1280 }
1281
1282 // use the one from the zip file if they both exist.
1283 dir->removeFile(entryName.getPathLeaf());
1284
1285 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
1286 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
1287 if (err != NO_ERROR) {
1288 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
1289 count = err;
1290 goto bail;
1291 }
1292 file->setCompressionMethod(entry->getCompressionMethod());
1293
1294#if 0
1295 if (entryName == "AndroidManifest.xml") {
1296 printf("AndroidManifest.xml\n");
1297 }
1298 printf("\n\nfile: %s\n", entryName.string());
1299#endif
1300
1301 size_t len = entry->getUncompressedLen();
1302 void* data = zip->uncompress(entry);
1303 void* buf = file->editData(len);
1304 memcpy(buf, data, len);
1305
1306#if 0
1307 const int OFF = 0;
1308 const unsigned char* p = (unsigned char*)data;
1309 const unsigned char* end = p+len;
1310 p += OFF;
1311 for (int i=0; i<32 && p < end; i++) {
1312 printf("0x%03x ", i*0x10 + OFF);
1313 for (int j=0; j<0x10 && p < end; j++) {
1314 printf(" %02x", *p);
1315 p++;
1316 }
1317 printf("\n");
1318 }
1319#endif
1320
1321 free(data);
1322
1323 count++;
1324 }
1325
1326bail:
1327 delete zip;
1328 return count;
1329}
1330
1331status_t AaptAssets::filter(Bundle* bundle)
1332{
Adam Lesinskifab50872014-04-16 14:40:42 -07001333 WeakResourceFilter reqFilter;
Adam Lesinski282e1812014-01-23 18:17:42 -08001334 status_t err = reqFilter.parse(bundle->getConfigurations());
1335 if (err != NO_ERROR) {
1336 return err;
1337 }
1338
Adam Lesinskifab50872014-04-16 14:40:42 -07001339 uint32_t preferredDensity = 0;
1340 if (bundle->getPreferredDensity().size() > 0) {
1341 ResTable_config preferredConfig;
1342 if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
1343 fprintf(stderr, "Error parsing preferred density: %s\n",
1344 bundle->getPreferredDensity().string());
1345 return UNKNOWN_ERROR;
1346 }
1347 preferredDensity = preferredConfig.density;
Adam Lesinski282e1812014-01-23 18:17:42 -08001348 }
1349
Adam Lesinskifab50872014-04-16 14:40:42 -07001350 if (reqFilter.isEmpty() && preferredDensity == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001351 return NO_ERROR;
1352 }
1353
1354 if (bundle->getVerbose()) {
1355 if (!reqFilter.isEmpty()) {
1356 printf("Applying required filter: %s\n",
Adam Lesinskifab50872014-04-16 14:40:42 -07001357 bundle->getConfigurations().string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001358 }
Adam Lesinskifab50872014-04-16 14:40:42 -07001359 if (preferredDensity > 0) {
1360 printf("Applying preferred density filter: %s\n",
1361 bundle->getPreferredDensity().string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001362 }
1363 }
1364
1365 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1366 const size_t ND = resdirs.size();
1367 for (size_t i=0; i<ND; i++) {
1368 const sp<AaptDir>& dir = resdirs.itemAt(i);
1369 if (dir->getLeaf() == kValuesDir) {
1370 // The "value" dir is special since a single file defines
1371 // multiple resources, so we can not do filtering on the
1372 // files themselves.
1373 continue;
1374 }
1375 if (dir->getLeaf() == kMipmapDir) {
1376 // We also skip the "mipmap" directory, since the point of this
1377 // is to include all densities without stripping. If you put
1378 // other configurations in here as well they won't be stripped
1379 // either... So don't do that. Seriously. What is wrong with you?
1380 continue;
1381 }
1382
1383 const size_t NG = dir->getFiles().size();
1384 for (size_t j=0; j<NG; j++) {
1385 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
1386
1387 // First remove any configurations we know we don't need.
1388 for (size_t k=0; k<grp->getFiles().size(); k++) {
1389 sp<AaptFile> file = grp->getFiles().valueAt(k);
1390 if (k == 0 && grp->getFiles().size() == 1) {
1391 // If this is the only file left, we need to keep it.
1392 // Otherwise the resource IDs we are using will be inconsistent
1393 // with what we get when not stripping. Sucky, but at least
1394 // for now we can rely on the back-end doing another filtering
1395 // pass to take this out and leave us with this resource name
1396 // containing no entries.
1397 continue;
1398 }
1399 if (file->getPath().getPathExtension() == ".xml") {
1400 // We can't remove .xml files at this point, because when
1401 // we parse them they may add identifier resources, so
1402 // removing them can cause our resource identifiers to
1403 // become inconsistent.
1404 continue;
1405 }
1406 const ResTable_config& config(file->getGroupEntry().toParams());
1407 if (!reqFilter.match(config)) {
1408 if (bundle->getVerbose()) {
1409 printf("Pruning unneeded resource: %s\n",
1410 file->getPrintableSource().string());
1411 }
1412 grp->removeFile(k);
1413 k--;
1414 }
1415 }
1416
1417 // Quick check: no preferred filters, nothing more to do.
Adam Lesinskifab50872014-04-16 14:40:42 -07001418 if (preferredDensity == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001419 continue;
1420 }
1421
Adam Lesinski8cf61842013-10-18 13:42:09 -07001422 // Get the preferred density if there is one. We do not match exactly for density.
1423 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
1424 // pick xhdpi.
Adam Lesinskifab50872014-04-16 14:40:42 -07001425 for (size_t k=0; k<grp->getFiles().size(); k++) {
1426 sp<AaptFile> file = grp->getFiles().valueAt(k);
1427 if (k == 0 && grp->getFiles().size() == 1) {
1428 // If this is the only file left, we need to keep it.
1429 // Otherwise the resource IDs we are using will be inconsistent
1430 // with what we get when not stripping. Sucky, but at least
1431 // for now we can rely on the back-end doing another filtering
1432 // pass to take this out and leave us with this resource name
1433 // containing no entries.
1434 continue;
1435 }
1436 if (file->getPath().getPathExtension() == ".xml") {
1437 // We can't remove .xml files at this point, because when
1438 // we parse them they may add identifier resources, so
1439 // removing them can cause our resource identifiers to
1440 // become inconsistent.
1441 continue;
1442 }
1443 const ResTable_config& config(file->getGroupEntry().toParams());
1444 if (config.density != 0 && config.density != preferredDensity) {
1445 // This is a resource we would prefer not to have. Check
1446 // to see if have a similar variation that we would like
1447 // to have and, if so, we can drop it.
1448 uint32_t bestDensity = config.density;
Adam Lesinski8cf61842013-10-18 13:42:09 -07001449
Adam Lesinskifab50872014-04-16 14:40:42 -07001450 for (size_t m=0; m<grp->getFiles().size(); m++) {
1451 if (m == k) {
1452 continue;
Adam Lesinski282e1812014-01-23 18:17:42 -08001453 }
Adam Lesinski8cf61842013-10-18 13:42:09 -07001454
Adam Lesinskifab50872014-04-16 14:40:42 -07001455 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
1456 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
1457 if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
1458 // See if there is a better density resource
1459 if (mconfig.density < bestDensity &&
1460 mconfig.density > preferredDensity &&
1461 bestDensity > preferredDensity) {
1462 // This density is between our best density and
1463 // the preferred density, therefore it is better.
1464 bestDensity = mconfig.density;
1465 } else if (mconfig.density > bestDensity &&
1466 bestDensity < preferredDensity) {
1467 // This density is better than our best density and
1468 // our best density was smaller than our preferred
1469 // density, so it is better.
1470 bestDensity = mconfig.density;
Adam Lesinski8cf61842013-10-18 13:42:09 -07001471 }
Adam Lesinski8cf61842013-10-18 13:42:09 -07001472 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001473 }
Adam Lesinskifab50872014-04-16 14:40:42 -07001474
1475 if (bestDensity != config.density) {
1476 if (bundle->getVerbose()) {
1477 printf("Pruning unneeded resource: %s\n",
1478 file->getPrintableSource().string());
1479 }
1480 grp->removeFile(k);
1481 k--;
1482 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001483 }
1484 }
1485 }
1486 }
1487
1488 return NO_ERROR;
1489}
1490
1491sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1492{
1493 sp<AaptSymbols> sym = mSymbols.valueFor(name);
1494 if (sym == NULL) {
1495 sym = new AaptSymbols();
1496 mSymbols.add(name, sym);
1497 }
1498 return sym;
1499}
1500
1501sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
1502{
1503 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
1504 if (sym == NULL) {
1505 sym = new AaptSymbols();
1506 mJavaSymbols.add(name, sym);
1507 }
1508 return sym;
1509}
1510
1511status_t AaptAssets::applyJavaSymbols()
1512{
1513 size_t N = mJavaSymbols.size();
1514 for (size_t i=0; i<N; i++) {
1515 const String8& name = mJavaSymbols.keyAt(i);
1516 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
1517 ssize_t pos = mSymbols.indexOfKey(name);
1518 if (pos < 0) {
1519 SourcePos pos;
1520 pos.error("Java symbol dir %s not defined\n", name.string());
1521 return UNKNOWN_ERROR;
1522 }
1523 //printf("**** applying java symbols in dir %s\n", name.string());
1524 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1525 if (err != NO_ERROR) {
1526 return err;
1527 }
1528 }
1529
1530 return NO_ERROR;
1531}
1532
1533bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
1534 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
1535 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
1536 // sym.isJavaSymbol ? 1 : 0);
1537 if (!mHavePrivateSymbols) return true;
1538 if (sym.isPublic) return true;
1539 if (includePrivate && sym.isJavaSymbol) return true;
1540 return false;
1541}
1542
1543status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1544{
1545 if (!mHaveIncludedAssets) {
1546 // Add in all includes.
1547 const Vector<const char*>& incl = bundle->getPackageIncludes();
1548 const size_t N=incl.size();
1549 for (size_t i=0; i<N; i++) {
1550 if (bundle->getVerbose())
1551 printf("Including resources from package: %s\n", incl[i]);
1552 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
1553 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
1554 incl[i]);
1555 return UNKNOWN_ERROR;
1556 }
1557 }
1558 mHaveIncludedAssets = true;
1559 }
1560
1561 return NO_ERROR;
1562}
1563
1564status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
1565{
1566 const ResTable& res = getIncludedResources();
1567 // XXX dirty!
Narayan Kamath00b31442014-01-27 17:32:37 +00001568 return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
Adam Lesinski282e1812014-01-23 18:17:42 -08001569}
1570
1571const ResTable& AaptAssets::getIncludedResources() const
1572{
1573 return mIncludedAssets.getResources(false);
1574}
1575
1576void AaptAssets::print(const String8& prefix) const
1577{
1578 String8 innerPrefix(prefix);
1579 innerPrefix.append(" ");
1580 String8 innerInnerPrefix(innerPrefix);
1581 innerInnerPrefix.append(" ");
1582 printf("%sConfigurations:\n", prefix.string());
1583 const size_t N=mGroupEntries.size();
1584 for (size_t i=0; i<N; i++) {
1585 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
1586 printf("%s %s\n", prefix.string(),
1587 cname != "" ? cname.string() : "(default)");
1588 }
1589
1590 printf("\n%sFiles:\n", prefix.string());
1591 AaptDir::print(innerPrefix);
1592
1593 printf("\n%sResource Dirs:\n", prefix.string());
1594 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1595 const size_t NR = resdirs.size();
1596 for (size_t i=0; i<NR; i++) {
1597 const sp<AaptDir>& d = resdirs.itemAt(i);
1598 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
1599 d->print(innerInnerPrefix);
1600 }
1601}
1602
1603sp<AaptDir> AaptAssets::resDir(const String8& name) const
1604{
1605 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1606 const size_t N = resdirs.size();
1607 for (size_t i=0; i<N; i++) {
1608 const sp<AaptDir>& d = resdirs.itemAt(i);
1609 if (d->getLeaf() == name) {
1610 return d;
1611 }
1612 }
1613 return NULL;
1614}
1615
1616bool
1617valid_symbol_name(const String8& symbol)
1618{
1619 static char const * const KEYWORDS[] = {
1620 "abstract", "assert", "boolean", "break",
1621 "byte", "case", "catch", "char", "class", "const", "continue",
1622 "default", "do", "double", "else", "enum", "extends", "final",
1623 "finally", "float", "for", "goto", "if", "implements", "import",
1624 "instanceof", "int", "interface", "long", "native", "new", "package",
1625 "private", "protected", "public", "return", "short", "static",
1626 "strictfp", "super", "switch", "synchronized", "this", "throw",
1627 "throws", "transient", "try", "void", "volatile", "while",
1628 "true", "false", "null",
1629 NULL
1630 };
1631 const char*const* k = KEYWORDS;
1632 const char*const s = symbol.string();
1633 while (*k) {
1634 if (0 == strcmp(s, *k)) {
1635 return false;
1636 }
1637 k++;
1638 }
1639 return true;
1640}