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