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