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