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