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