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