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