blob: d346731e63e22eafb640263a297893892e92b345 [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4
5#include "AaptAssets.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07006#include "AaptConfig.h"
7#include "AaptUtil.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08008#include "Main.h"
Adam Lesinskifab50872014-04-16 14:40:42 -07009#include "ResourceFilter.h"
Adam Lesinski282e1812014-01-23 18:17:42 -080010
11#include <utils/misc.h>
12#include <utils/SortedVector.h>
13
14#include <ctype.h>
15#include <dirent.h>
16#include <errno.h>
17
Adam Lesinski282e1812014-01-23 18:17:42 -080018static const char* kAssetDir = "assets";
19static const char* kResourceDir = "res";
20static const char* kValuesDir = "values";
21static const char* kMipmapDir = "mipmap";
22static const char* kInvalidChars = "/\\:";
23static const size_t kMaxAssetFileName = 100;
24
25static const String8 kResString(kResourceDir);
26
27/*
28 * Names of asset files must meet the following criteria:
29 *
30 * - the filename length must be less than kMaxAssetFileName bytes long
31 * (and can't be empty)
32 * - all characters must be 7-bit printable ASCII
33 * - none of { '/' '\\' ':' }
34 *
35 * Pass in just the filename, not the full path.
36 */
37static bool validateFileName(const char* fileName)
38{
39 const char* cp = fileName;
40 size_t len = 0;
41
42 while (*cp != '\0') {
43 if ((*cp & 0x80) != 0)
44 return false; // reject high ASCII
45 if (*cp < 0x20 || *cp >= 0x7f)
46 return false; // reject control chars and 0x7f
47 if (strchr(kInvalidChars, *cp) != NULL)
48 return false; // reject path sep chars
49 cp++;
50 len++;
51 }
52
53 if (len < 1 || len > kMaxAssetFileName)
54 return false; // reject empty or too long
55
56 return true;
57}
58
59// The default to use if no other ignore pattern is defined.
60const char * const gDefaultIgnoreAssets =
61 "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
62// The ignore pattern that can be passed via --ignore-assets in Main.cpp
63const char * gUserIgnoreAssets = NULL;
64
65static bool isHidden(const char *root, const char *path)
66{
67 // Patterns syntax:
68 // - Delimiter is :
69 // - Entry can start with the flag ! to avoid printing a warning
70 // about the file being ignored.
71 // - Entry can have the flag "<dir>" to match only directories
72 // or <file> to match only files. Default is to match both.
73 // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
74 // where prefix/suffix must have at least 1 character (so that
75 // we don't match a '*' catch-all pattern.)
76 // - The special filenames "." and ".." are always ignored.
77 // - Otherwise the full string is matched.
78 // - match is not case-sensitive.
79
80 if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
81 return true;
82 }
83
84 const char *delim = ":";
85 const char *p = gUserIgnoreAssets;
86 if (!p || !p[0]) {
87 p = getenv("ANDROID_AAPT_IGNORE");
88 }
89 if (!p || !p[0]) {
90 p = gDefaultIgnoreAssets;
91 }
92 char *patterns = strdup(p);
93
94 bool ignore = false;
95 bool chatty = true;
96 char *matchedPattern = NULL;
97
98 String8 fullPath(root);
99 fullPath.appendPath(path);
100 FileType type = getFileType(fullPath);
101
102 int plen = strlen(path);
103
104 // Note: we don't have strtok_r under mingw.
105 for(char *token = strtok(patterns, delim);
106 !ignore && token != NULL;
107 token = strtok(NULL, delim)) {
108 chatty = token[0] != '!';
109 if (!chatty) token++; // skip !
110 if (strncasecmp(token, "<dir>" , 5) == 0) {
111 if (type != kFileTypeDirectory) continue;
112 token += 5;
113 }
114 if (strncasecmp(token, "<file>", 6) == 0) {
115 if (type != kFileTypeRegular) continue;
116 token += 6;
117 }
118
119 matchedPattern = token;
120 int n = strlen(token);
121
122 if (token[0] == '*') {
123 // Match *suffix
124 token++;
125 n--;
126 if (n <= plen) {
127 ignore = strncasecmp(token, path + plen - n, n) == 0;
128 }
129 } else if (n > 1 && token[n - 1] == '*') {
130 // Match prefix*
131 ignore = strncasecmp(token, path, n - 1) == 0;
132 } else {
133 ignore = strcasecmp(token, path) == 0;
134 }
135 }
136
137 if (ignore && chatty) {
138 fprintf(stderr, " (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
139 type == kFileTypeDirectory ? "dir" : "file",
140 path,
141 matchedPattern ? matchedPattern : "");
142 }
143
144 free(patterns);
145 return ignore;
146}
147
148// =========================================================================
149// =========================================================================
150// =========================================================================
151
Narayan Kamath91447d82014-01-21 15:32:36 +0000152/* static */
153inline bool isAlpha(const String8& string) {
154 const size_t length = string.length();
155 for (size_t i = 0; i < length; ++i) {
156 if (!isalpha(string[i])) {
157 return false;
158 }
159 }
160
161 return true;
162}
163
164/* static */
165inline bool isNumber(const String8& string) {
166 const size_t length = string.length();
167 for (size_t i = 0; i < length; ++i) {
168 if (!isdigit(string[i])) {
169 return false;
170 }
171 }
172
173 return true;
174}
175
176void AaptLocaleValue::setLanguage(const char* languageChars) {
177 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700178 while ((*languageChars) != '\0' && i < sizeof(language)/sizeof(language[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000179 language[i++] = tolower(*languageChars);
180 languageChars++;
181 }
182}
183
184void AaptLocaleValue::setRegion(const char* regionChars) {
185 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700186 while ((*regionChars) != '\0' && i < sizeof(region)/sizeof(region[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000187 region[i++] = toupper(*regionChars);
188 regionChars++;
189 }
190}
191
192void AaptLocaleValue::setScript(const char* scriptChars) {
193 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700194 while ((*scriptChars) != '\0' && i < sizeof(script)/sizeof(script[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000195 if (i == 0) {
196 script[i++] = toupper(*scriptChars);
197 } else {
198 script[i++] = tolower(*scriptChars);
199 }
200 scriptChars++;
201 }
202}
203
204void AaptLocaleValue::setVariant(const char* variantChars) {
205 size_t i = 0;
Andreas Gampeb8dc7bc2014-10-01 19:07:51 -0700206 while ((*variantChars) != '\0' && i < sizeof(variant)/sizeof(variant[0])) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000207 variant[i++] = *variantChars;
208 variantChars++;
209 }
210}
211
212bool AaptLocaleValue::initFromFilterString(const String8& str) {
213 // A locale (as specified in the filter) is an underscore separated name such
214 // as "en_US", "en_Latn_US", or "en_US_POSIX".
Adam Lesinskifab50872014-04-16 14:40:42 -0700215 Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_');
Narayan Kamath91447d82014-01-21 15:32:36 +0000216
217 const int numTags = parts.size();
218 bool valid = false;
219 if (numTags >= 1) {
220 const String8& lang = parts[0];
221 if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
222 setLanguage(lang.string());
223 valid = true;
224 }
225 }
226
227 if (!valid || numTags == 1) {
228 return valid;
229 }
230
231 // At this point, valid == true && numTags > 1.
232 const String8& part2 = parts[1];
233 if ((part2.length() == 2 && isAlpha(part2)) ||
234 (part2.length() == 3 && isNumber(part2))) {
235 setRegion(part2.string());
236 } else if (part2.length() == 4 && isAlpha(part2)) {
237 setScript(part2.string());
238 } else if (part2.length() >= 5 && part2.length() <= 8) {
239 setVariant(part2.string());
240 } else {
241 valid = false;
242 }
243
244 if (!valid || numTags == 2) {
245 return valid;
246 }
247
248 // At this point, valid == true && numTags > 1.
249 const String8& part3 = parts[2];
250 if (((part3.length() == 2 && isAlpha(part3)) ||
251 (part3.length() == 3 && isNumber(part3))) && script[0]) {
252 setRegion(part3.string());
253 } else if (part3.length() >= 5 && part3.length() <= 8) {
254 setVariant(part3.string());
255 } else {
256 valid = false;
257 }
258
259 if (!valid || numTags == 3) {
260 return valid;
261 }
262
263 const String8& part4 = parts[3];
264 if (part4.length() >= 5 && part4.length() <= 8) {
265 setVariant(part4.string());
266 } else {
267 valid = false;
268 }
269
270 if (!valid || numTags > 4) {
271 return false;
272 }
273
274 return true;
275}
276
277int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
278 const int size = parts.size();
279 int currentIndex = startIndex;
280
281 String8 part = parts[currentIndex];
282 if (part[0] == 'b' && part[1] == '+') {
283 // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
284 // except that the separator is "+" and not "-".
Adam Lesinskifab50872014-04-16 14:40:42 -0700285 Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+');
Narayan Kamath91447d82014-01-21 15:32:36 +0000286 subtags.removeItemsAt(0);
287 if (subtags.size() == 1) {
288 setLanguage(subtags[0]);
289 } else if (subtags.size() == 2) {
290 setLanguage(subtags[0]);
291
292 // The second tag can either be a region, a variant or a script.
293 switch (subtags[1].size()) {
294 case 2:
295 case 3:
296 setRegion(subtags[1]);
297 break;
298 case 4:
299 setScript(subtags[1]);
300 break;
301 case 5:
302 case 6:
303 case 7:
304 case 8:
305 setVariant(subtags[1]);
306 break;
307 default:
308 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n",
309 part.string());
310 return -1;
311 }
312 } else if (subtags.size() == 3) {
313 // The language is always the first subtag.
314 setLanguage(subtags[0]);
315
316 // The second subtag can either be a script or a region code.
317 // If its size is 4, it's a script code, else it's a region code.
318 bool hasRegion = false;
319 if (subtags[1].size() == 4) {
320 setScript(subtags[1]);
321 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
322 setRegion(subtags[1]);
323 hasRegion = true;
324 } else {
325 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", part.string());
326 return -1;
327 }
328
329 // The third tag can either be a region code (if the second tag was
330 // a script), else a variant code.
331 if (subtags[2].size() > 4) {
332 setVariant(subtags[2]);
333 } else {
334 setRegion(subtags[2]);
335 }
336 } else if (subtags.size() == 4) {
337 setLanguage(subtags[0]);
338 setScript(subtags[1]);
339 setRegion(subtags[2]);
340 setVariant(subtags[3]);
341 } else {
342 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name: %s\n", part.string());
343 return -1;
344 }
345
346 return ++currentIndex;
347 } else {
Narayan Kamath7f1a8952015-02-10 16:11:55 +0000348 if ((part.length() == 2 || part.length() == 3)
349 && isAlpha(part) && strcmp("car", part.string())) {
Narayan Kamath91447d82014-01-21 15:32:36 +0000350 setLanguage(part);
351 if (++currentIndex == size) {
352 return size;
353 }
354 } else {
355 return currentIndex;
356 }
357
358 part = parts[currentIndex];
359 if (part.string()[0] == 'r' && part.length() == 3) {
360 setRegion(part.string() + 1);
361 if (++currentIndex == size) {
362 return size;
363 }
364 }
365 }
366
367 return currentIndex;
368}
369
Narayan Kamath91447d82014-01-21 15:32:36 +0000370void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
371 config.unpackLanguage(language);
372 config.unpackRegion(region);
373 if (config.localeScript[0]) {
374 memcpy(script, config.localeScript, sizeof(config.localeScript));
375 }
376
377 if (config.localeVariant[0]) {
378 memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
379 }
380}
381
382void AaptLocaleValue::writeTo(ResTable_config* out) const {
383 out->packLanguage(language);
384 out->packRegion(region);
385
386 if (script[0]) {
387 memcpy(out->localeScript, script, sizeof(out->localeScript));
388 }
389
390 if (variant[0]) {
391 memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
392 }
393}
394
Adam Lesinski282e1812014-01-23 18:17:42 -0800395bool
396AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
397{
Adam Lesinskifab50872014-04-16 14:40:42 -0700398 const char* q = strchr(dir, '-');
399 size_t typeLen;
400 if (q != NULL) {
401 typeLen = q - dir;
402 } else {
403 typeLen = strlen(dir);
404 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800405
Adam Lesinskifab50872014-04-16 14:40:42 -0700406 String8 type(dir, typeLen);
407 if (!isValidResourceType(type)) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800408 return false;
409 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800410
Adam Lesinskifab50872014-04-16 14:40:42 -0700411 if (q != NULL) {
412 if (!AaptConfig::parse(String8(q + 1), &mParams)) {
413 return false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800414 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800415 }
416
Adam Lesinskifab50872014-04-16 14:40:42 -0700417 *resType = type;
Adam Lesinski282e1812014-01-23 18:17:42 -0800418 return true;
419}
420
421String8
Adam Lesinski282e1812014-01-23 18:17:42 -0800422AaptGroupEntry::toDirName(const String8& resType) const
423{
424 String8 s = resType;
Adam Lesinskifab50872014-04-16 14:40:42 -0700425 String8 params = mParams.toString();
426 if (params.length() > 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800427 if (s.length() > 0) {
428 s += "-";
429 }
Adam Lesinskifab50872014-04-16 14:40:42 -0700430 s += params;
Adam Lesinski282e1812014-01-23 18:17:42 -0800431 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800432 return s;
433}
434
Adam Lesinski282e1812014-01-23 18:17:42 -0800435
436// =========================================================================
437// =========================================================================
438// =========================================================================
439
440void* AaptFile::editData(size_t size)
441{
442 if (size <= mBufferSize) {
443 mDataSize = size;
444 return mData;
445 }
446 size_t allocSize = (size*3)/2;
447 void* buf = realloc(mData, allocSize);
448 if (buf == NULL) {
449 return NULL;
450 }
451 mData = buf;
452 mDataSize = size;
453 mBufferSize = allocSize;
454 return buf;
455}
456
Adam Lesinskide898ff2014-01-29 18:20:45 -0800457void* AaptFile::editDataInRange(size_t offset, size_t size)
458{
459 return (void*)(((uint8_t*) editData(offset + size)) + offset);
460}
461
Adam Lesinski282e1812014-01-23 18:17:42 -0800462void* AaptFile::editData(size_t* outSize)
463{
464 if (outSize) {
465 *outSize = mDataSize;
466 }
467 return mData;
468}
469
470void* AaptFile::padData(size_t wordSize)
471{
472 const size_t extra = mDataSize%wordSize;
473 if (extra == 0) {
474 return mData;
475 }
476
477 size_t initial = mDataSize;
478 void* data = editData(initial+(wordSize-extra));
479 if (data != NULL) {
480 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
481 }
482 return data;
483}
484
485status_t AaptFile::writeData(const void* data, size_t size)
486{
487 size_t end = mDataSize;
488 size_t total = size + end;
489 void* buf = editData(total);
490 if (buf == NULL) {
491 return UNKNOWN_ERROR;
492 }
493 memcpy(((char*)buf)+end, data, size);
494 return NO_ERROR;
495}
496
497void AaptFile::clearData()
498{
499 if (mData != NULL) free(mData);
500 mData = NULL;
501 mDataSize = 0;
502 mBufferSize = 0;
503}
504
505String8 AaptFile::getPrintableSource() const
506{
507 if (hasData()) {
508 String8 name(mGroupEntry.toDirName(String8()));
509 name.appendPath(mPath);
510 name.append(" #generated");
511 return name;
512 }
513 return mSourceFile;
514}
515
516// =========================================================================
517// =========================================================================
518// =========================================================================
519
Adam Lesinski09384302014-01-22 16:07:42 -0800520status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
Adam Lesinski282e1812014-01-23 18:17:42 -0800521{
Adam Lesinski09384302014-01-22 16:07:42 -0800522 ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
523 if (index >= 0 && overwriteDuplicate) {
524 fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
525 mFiles[index]->getSourceFile().string(),
526 file->getSourceFile().string());
527 removeFile(index);
528 index = -1;
529 }
530
531 if (index < 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800532 file->mPath = mPath;
533 mFiles.add(file->getGroupEntry(), file);
534 return NO_ERROR;
535 }
536
Adam Lesinski48f05d22014-05-12 22:13:02 -0700537 // Check if the version is automatically applied. This is a common source of
538 // error.
539 ConfigDescription withoutVersion = file->getGroupEntry().toParams();
540 withoutVersion.version = 0;
541 AaptConfig::applyVersionForCompatibility(&withoutVersion);
Adam Lesinski282e1812014-01-23 18:17:42 -0800542
Adam Lesinski48f05d22014-05-12 22:13:02 -0700543 const sp<AaptFile>& originalFile = mFiles.valueAt(index);
544 SourcePos(file->getSourceFile(), -1)
545 .error("Duplicate file.\n%s: Original is here. %s",
546 originalFile->getPrintableSource().string(),
547 (withoutVersion.version != 0) ? "The version qualifier may be implied." : "");
Adam Lesinski282e1812014-01-23 18:17:42 -0800548 return UNKNOWN_ERROR;
549}
550
551void AaptGroup::removeFile(size_t index)
552{
553 mFiles.removeItemsAt(index);
554}
555
556void AaptGroup::print(const String8& prefix) const
557{
558 printf("%s%s\n", prefix.string(), getPath().string());
559 const size_t N=mFiles.size();
560 size_t i;
561 for (i=0; i<N; i++) {
562 sp<AaptFile> file = mFiles.valueAt(i);
563 const AaptGroupEntry& e = file->getGroupEntry();
564 if (file->hasData()) {
565 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
566 (int)file->getSize());
567 } else {
568 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
569 file->getPrintableSource().string());
570 }
571 //printf("%s File Group Entry: %s\n", prefix.string(),
572 // file->getGroupEntry().toDirName(String8()).string());
573 }
574}
575
576String8 AaptGroup::getPrintableSource() const
577{
578 if (mFiles.size() > 0) {
579 // Arbitrarily pull the first source file out of the list.
580 return mFiles.valueAt(0)->getPrintableSource();
581 }
582
583 // Should never hit this case, but to be safe...
584 return getPath();
585
586}
587
588// =========================================================================
589// =========================================================================
590// =========================================================================
591
592status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
593{
594 if (mFiles.indexOfKey(name) >= 0) {
595 return ALREADY_EXISTS;
596 }
597 mFiles.add(name, file);
598 return NO_ERROR;
599}
600
601status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
602{
603 if (mDirs.indexOfKey(name) >= 0) {
604 return ALREADY_EXISTS;
605 }
606 mDirs.add(name, dir);
607 return NO_ERROR;
608}
609
610sp<AaptDir> AaptDir::makeDir(const String8& path)
611{
612 String8 name;
613 String8 remain = path;
614
615 sp<AaptDir> subdir = this;
616 while (name = remain.walkPath(&remain), remain != "") {
617 subdir = subdir->makeDir(name);
618 }
619
620 ssize_t i = subdir->mDirs.indexOfKey(name);
621 if (i >= 0) {
622 return subdir->mDirs.valueAt(i);
623 }
624 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
625 subdir->mDirs.add(name, dir);
626 return dir;
627}
628
629void AaptDir::removeFile(const String8& name)
630{
631 mFiles.removeItem(name);
632}
633
634void AaptDir::removeDir(const String8& name)
635{
636 mDirs.removeItem(name);
637}
638
Adam Lesinski09384302014-01-22 16:07:42 -0800639status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
640 const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -0800641{
642 sp<AaptGroup> group;
643 if (mFiles.indexOfKey(leafName) >= 0) {
644 group = mFiles.valueFor(leafName);
645 } else {
646 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
647 mFiles.add(leafName, group);
648 }
649
Adam Lesinski09384302014-01-22 16:07:42 -0800650 return group->addFile(file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800651}
652
653ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
654 const AaptGroupEntry& kind, const String8& resType,
Adam Lesinski09384302014-01-22 16:07:42 -0800655 sp<FilePathStore>& fullResPaths, const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -0800656{
657 Vector<String8> fileNames;
658 {
659 DIR* dir = NULL;
660
661 dir = opendir(srcDir.string());
662 if (dir == NULL) {
663 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
664 return UNKNOWN_ERROR;
665 }
666
667 /*
668 * Slurp the filenames out of the directory.
669 */
670 while (1) {
671 struct dirent* entry;
672
673 entry = readdir(dir);
674 if (entry == NULL)
675 break;
676
677 if (isHidden(srcDir.string(), entry->d_name))
678 continue;
679
680 String8 name(entry->d_name);
681 fileNames.add(name);
682 // Add fully qualified path for dependency purposes
683 // if we're collecting them
684 if (fullResPaths != NULL) {
685 fullResPaths->add(srcDir.appendPathCopy(name));
686 }
687 }
688 closedir(dir);
689 }
690
691 ssize_t count = 0;
692
693 /*
694 * Stash away the files and recursively descend into subdirectories.
695 */
696 const size_t N = fileNames.size();
697 size_t i;
698 for (i = 0; i < N; i++) {
699 String8 pathName(srcDir);
700 FileType type;
701
702 pathName.appendPath(fileNames[i].string());
703 type = getFileType(pathName.string());
704 if (type == kFileTypeDirectory) {
705 sp<AaptDir> subdir;
706 bool notAdded = false;
707 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
708 subdir = mDirs.valueFor(fileNames[i]);
709 } else {
710 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
711 notAdded = true;
712 }
713 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Adam Lesinski09384302014-01-22 16:07:42 -0800714 resType, fullResPaths, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800715 if (res < NO_ERROR) {
716 return res;
717 }
718 if (res > 0 && notAdded) {
719 mDirs.add(fileNames[i], subdir);
720 }
721 count += res;
722 } else if (type == kFileTypeRegular) {
723 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
Adam Lesinski09384302014-01-22 16:07:42 -0800724 status_t err = addLeafFile(fileNames[i], file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -0800725 if (err != NO_ERROR) {
726 return err;
727 }
728
729 count++;
730
731 } else {
732 if (bundle->getVerbose())
733 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
734 }
735 }
736
737 return count;
738}
739
740status_t AaptDir::validate() const
741{
742 const size_t NF = mFiles.size();
743 const size_t ND = mDirs.size();
744 size_t i;
745 for (i = 0; i < NF; i++) {
746 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
747 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
748 "Invalid filename. Unable to add.");
749 return UNKNOWN_ERROR;
750 }
751
752 size_t j;
753 for (j = i+1; j < NF; j++) {
754 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
755 mFiles.valueAt(j)->getLeaf().string()) == 0) {
756 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
757 "File is case-insensitive equivalent to: %s",
758 mFiles.valueAt(j)->getPrintableSource().string());
759 return UNKNOWN_ERROR;
760 }
761
762 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
763 // (this is mostly caught by the "marked" stuff, below)
764 }
765
766 for (j = 0; j < ND; j++) {
767 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
768 mDirs.valueAt(j)->getLeaf().string()) == 0) {
769 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
770 "File conflicts with dir from: %s",
771 mDirs.valueAt(j)->getPrintableSource().string());
772 return UNKNOWN_ERROR;
773 }
774 }
775 }
776
777 for (i = 0; i < ND; i++) {
778 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
779 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
780 "Invalid directory name, unable to add.");
781 return UNKNOWN_ERROR;
782 }
783
784 size_t j;
785 for (j = i+1; j < ND; j++) {
786 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
787 mDirs.valueAt(j)->getLeaf().string()) == 0) {
788 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
789 "Directory is case-insensitive equivalent to: %s",
790 mDirs.valueAt(j)->getPrintableSource().string());
791 return UNKNOWN_ERROR;
792 }
793 }
794
795 status_t err = mDirs.valueAt(i)->validate();
796 if (err != NO_ERROR) {
797 return err;
798 }
799 }
800
801 return NO_ERROR;
802}
803
804void AaptDir::print(const String8& prefix) const
805{
806 const size_t ND=getDirs().size();
807 size_t i;
808 for (i=0; i<ND; i++) {
809 getDirs().valueAt(i)->print(prefix);
810 }
811
812 const size_t NF=getFiles().size();
813 for (i=0; i<NF; i++) {
814 getFiles().valueAt(i)->print(prefix);
815 }
816}
817
818String8 AaptDir::getPrintableSource() const
819{
820 if (mFiles.size() > 0) {
821 // Arbitrarily pull the first file out of the list as the source dir.
822 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
823 }
824 if (mDirs.size() > 0) {
825 // Or arbitrarily pull the first dir out of the list as the source dir.
826 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
827 }
828
829 // Should never hit this case, but to be safe...
830 return mPath;
831
832}
833
834// =========================================================================
835// =========================================================================
836// =========================================================================
837
838status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
839{
840 status_t err = NO_ERROR;
841 size_t N = javaSymbols->mSymbols.size();
842 for (size_t i=0; i<N; i++) {
843 const String8& name = javaSymbols->mSymbols.keyAt(i);
844 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
845 ssize_t pos = mSymbols.indexOfKey(name);
846 if (pos < 0) {
847 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
848 err = UNKNOWN_ERROR;
849 continue;
850 }
851 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
852 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
853 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
854 }
855
856 N = javaSymbols->mNestedSymbols.size();
857 for (size_t i=0; i<N; i++) {
858 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
859 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
860 ssize_t pos = mNestedSymbols.indexOfKey(name);
861 if (pos < 0) {
862 SourcePos pos;
863 pos.error("Java symbol dir %s not defined\n", name.string());
864 err = UNKNOWN_ERROR;
865 continue;
866 }
867 //printf("**** applying java symbols in dir %s\n", name.string());
868 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
869 if (myerr != NO_ERROR) {
870 err = myerr;
871 }
872 }
873
874 return err;
875}
876
877// =========================================================================
878// =========================================================================
879// =========================================================================
880
881AaptAssets::AaptAssets()
882 : AaptDir(String8(), String8()),
Narayan Kamath91447d82014-01-21 15:32:36 +0000883 mHavePrivateSymbols(false),
884 mChanged(false), mHaveIncludedAssets(false),
Adam Lesinskifab50872014-04-16 14:40:42 -0700885 mRes(NULL) {}
Adam Lesinski282e1812014-01-23 18:17:42 -0800886
887const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
888 if (mChanged) {
889 }
890 return mGroupEntries;
891}
892
893status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
894{
895 mChanged = true;
896 return AaptDir::addFile(name, file);
897}
898
899sp<AaptFile> AaptAssets::addFile(
900 const String8& filePath, const AaptGroupEntry& entry,
901 const String8& srcDir, sp<AaptGroup>* outGroup,
902 const String8& resType)
903{
904 sp<AaptDir> dir = this;
905 sp<AaptGroup> group;
906 sp<AaptFile> file;
907 String8 root, remain(filePath), partialPath;
908 while (remain.length() > 0) {
909 root = remain.walkPath(&remain);
910 partialPath.appendPath(root);
911
912 const String8 rootStr(root);
913
914 if (remain.length() == 0) {
915 ssize_t i = dir->getFiles().indexOfKey(rootStr);
916 if (i >= 0) {
917 group = dir->getFiles().valueAt(i);
918 } else {
919 group = new AaptGroup(rootStr, filePath);
920 status_t res = dir->addFile(rootStr, group);
921 if (res != NO_ERROR) {
922 return NULL;
923 }
924 }
925 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
926 status_t res = group->addFile(file);
927 if (res != NO_ERROR) {
928 return NULL;
929 }
930 break;
931
932 } else {
933 ssize_t i = dir->getDirs().indexOfKey(rootStr);
934 if (i >= 0) {
935 dir = dir->getDirs().valueAt(i);
936 } else {
937 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
938 status_t res = dir->addDir(rootStr, subdir);
939 if (res != NO_ERROR) {
940 return NULL;
941 }
942 dir = subdir;
943 }
944 }
945 }
946
947 mGroupEntries.add(entry);
948 if (outGroup) *outGroup = group;
949 return file;
950}
951
952void AaptAssets::addResource(const String8& leafName, const String8& path,
953 const sp<AaptFile>& file, const String8& resType)
954{
955 sp<AaptDir> res = AaptDir::makeDir(kResString);
956 String8 dirname = file->getGroupEntry().toDirName(resType);
957 sp<AaptDir> subdir = res->makeDir(dirname);
958 sp<AaptGroup> grr = new AaptGroup(leafName, path);
959 grr->addFile(file);
960
961 subdir->addFile(leafName, grr);
962}
963
964
965ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
966{
967 int count;
968 int totalCount = 0;
969 FileType type;
970 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
971 const size_t dirCount =resDirs.size();
972 sp<AaptAssets> current = this;
973
974 const int N = bundle->getFileSpecCount();
975
976 /*
977 * If a package manifest was specified, include that first.
978 */
979 if (bundle->getAndroidManifestFile() != NULL) {
980 // place at root of zip.
981 String8 srcFile(bundle->getAndroidManifestFile());
982 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
983 NULL, String8());
984 totalCount++;
985 }
986
987 /*
988 * If a directory of custom assets was supplied, slurp 'em up.
989 */
Adam Lesinski09384302014-01-22 16:07:42 -0800990 const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
991 const int AN = assetDirs.size();
992 for (int i = 0; i < AN; i++) {
993 FileType type = getFileType(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -0800994 if (type == kFileTypeNonexistent) {
Adam Lesinski09384302014-01-22 16:07:42 -0800995 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -0800996 return UNKNOWN_ERROR;
997 }
998 if (type != kFileTypeDirectory) {
Adam Lesinski09384302014-01-22 16:07:42 -0800999 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001000 return UNKNOWN_ERROR;
1001 }
1002
Adam Lesinski09384302014-01-22 16:07:42 -08001003 String8 assetRoot(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08001004 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1005 AaptGroupEntry group;
1006 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Adam Lesinski09384302014-01-22 16:07:42 -08001007 String8(), mFullAssetPaths, true);
Adam Lesinski282e1812014-01-23 18:17:42 -08001008 if (count < 0) {
1009 totalCount = count;
1010 goto bail;
1011 }
1012 if (count > 0) {
1013 mGroupEntries.add(group);
1014 }
1015 totalCount += count;
1016
Adam Lesinski09384302014-01-22 16:07:42 -08001017 if (bundle->getVerbose()) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001018 printf("Found %d custom asset file%s in %s\n",
Adam Lesinski09384302014-01-22 16:07:42 -08001019 count, (count==1) ? "" : "s", assetDirs[i]);
1020 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001021 }
1022
1023 /*
1024 * If a directory of resource-specific assets was supplied, slurp 'em up.
1025 */
1026 for (size_t i=0; i<dirCount; i++) {
1027 const char *res = resDirs[i];
1028 if (res) {
1029 type = getFileType(res);
1030 if (type == kFileTypeNonexistent) {
1031 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1032 return UNKNOWN_ERROR;
1033 }
1034 if (type == kFileTypeDirectory) {
1035 if (i>0) {
1036 sp<AaptAssets> nextOverlay = new AaptAssets();
1037 current->setOverlay(nextOverlay);
1038 current = nextOverlay;
1039 current->setFullResPaths(mFullResPaths);
1040 }
1041 count = current->slurpResourceTree(bundle, String8(res));
Bryan Mawhinney9ab9b932014-01-24 16:18:13 +00001042 if (i > 0 && count > 0) {
1043 count = current->filter(bundle);
1044 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001045
1046 if (count < 0) {
1047 totalCount = count;
1048 goto bail;
1049 }
1050 totalCount += count;
1051 }
1052 else {
1053 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1054 return UNKNOWN_ERROR;
1055 }
1056 }
1057
1058 }
1059 /*
1060 * Now do any additional raw files.
1061 */
1062 for (int arg=0; arg<N; arg++) {
1063 const char* assetDir = bundle->getFileSpecEntry(arg);
1064
1065 FileType type = getFileType(assetDir);
1066 if (type == kFileTypeNonexistent) {
1067 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1068 return UNKNOWN_ERROR;
1069 }
1070 if (type != kFileTypeDirectory) {
1071 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1072 return UNKNOWN_ERROR;
1073 }
1074
1075 String8 assetRoot(assetDir);
1076
1077 if (bundle->getVerbose())
1078 printf("Processing raw dir '%s'\n", (const char*) assetDir);
1079
1080 /*
1081 * Do a recursive traversal of subdir tree. We don't make any
1082 * guarantees about ordering, so we're okay with an inorder search
1083 * using whatever order the OS happens to hand back to us.
1084 */
1085 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
1086 if (count < 0) {
1087 /* failure; report error and remove archive */
1088 totalCount = count;
1089 goto bail;
1090 }
1091 totalCount += count;
1092
1093 if (bundle->getVerbose())
1094 printf("Found %d asset file%s in %s\n",
1095 count, (count==1) ? "" : "s", assetDir);
1096 }
1097
1098 count = validate();
1099 if (count != NO_ERROR) {
1100 totalCount = count;
1101 goto bail;
1102 }
1103
1104 count = filter(bundle);
1105 if (count != NO_ERROR) {
1106 totalCount = count;
1107 goto bail;
1108 }
1109
1110bail:
1111 return totalCount;
1112}
1113
1114ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1115 const AaptGroupEntry& kind,
1116 const String8& resType,
Adam Lesinski40e8eef2014-09-16 14:43:29 -07001117 sp<FilePathStore>& fullResPaths,
1118 const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -08001119{
Adam Lesinski40e8eef2014-09-16 14:43:29 -07001120 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -08001121 if (res > 0) {
1122 mGroupEntries.add(kind);
1123 }
1124
1125 return res;
1126}
1127
1128ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1129{
1130 ssize_t err = 0;
1131
1132 DIR* dir = opendir(srcDir.string());
1133 if (dir == NULL) {
1134 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1135 return UNKNOWN_ERROR;
1136 }
1137
1138 status_t count = 0;
1139
1140 /*
1141 * Run through the directory, looking for dirs that match the
1142 * expected pattern.
1143 */
1144 while (1) {
1145 struct dirent* entry = readdir(dir);
1146 if (entry == NULL) {
1147 break;
1148 }
1149
1150 if (isHidden(srcDir.string(), entry->d_name)) {
1151 continue;
1152 }
1153
1154 String8 subdirName(srcDir);
1155 subdirName.appendPath(entry->d_name);
1156
1157 AaptGroupEntry group;
1158 String8 resType;
1159 bool b = group.initFromDirName(entry->d_name, &resType);
1160 if (!b) {
Adam Lesinskifab50872014-04-16 14:40:42 -07001161 fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
Adam Lesinski282e1812014-01-23 18:17:42 -08001162 entry->d_name);
1163 err = -1;
1164 continue;
1165 }
1166
1167 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
1168 int maxResInt = atoi(bundle->getMaxResVersion());
1169 const char *verString = group.getVersionString().string();
1170 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
1171 if (dirVersionInt > maxResInt) {
1172 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
1173 continue;
1174 }
1175 }
1176
1177 FileType type = getFileType(subdirName.string());
1178
1179 if (type == kFileTypeDirectory) {
1180 sp<AaptDir> dir = makeDir(resType);
1181 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1182 resType, mFullResPaths);
1183 if (res < 0) {
1184 count = res;
1185 goto bail;
1186 }
1187 if (res > 0) {
1188 mGroupEntries.add(group);
1189 count += res;
1190 }
1191
1192 // Only add this directory if we don't already have a resource dir
1193 // for the current type. This ensures that we only add the dir once
1194 // for all configs.
1195 sp<AaptDir> rdir = resDir(resType);
1196 if (rdir == NULL) {
1197 mResDirs.add(dir);
1198 }
1199 } else {
1200 if (bundle->getVerbose()) {
1201 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
1202 }
1203 }
1204 }
1205
1206bail:
1207 closedir(dir);
1208 dir = NULL;
1209
1210 if (err != 0) {
1211 return err;
1212 }
1213 return count;
1214}
1215
1216ssize_t
Andreas Gampe2412f842014-09-30 20:55:57 -07001217AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename)
Adam Lesinski282e1812014-01-23 18:17:42 -08001218{
1219 int count = 0;
1220 SortedVector<AaptGroupEntry> entries;
1221
1222 ZipFile* zip = new ZipFile;
1223 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1224 if (err != NO_ERROR) {
1225 fprintf(stderr, "error opening zip file %s\n", filename);
1226 count = err;
1227 delete zip;
1228 return -1;
1229 }
1230
1231 const int N = zip->getNumEntries();
1232 for (int i=0; i<N; i++) {
1233 ZipEntry* entry = zip->getEntryByIndex(i);
1234 if (entry->getDeleted()) {
1235 continue;
1236 }
1237
1238 String8 entryName(entry->getFileName());
1239
1240 String8 dirName = entryName.getPathDir();
1241 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1242
1243 String8 resType;
1244 AaptGroupEntry kind;
1245
1246 String8 remain;
1247 if (entryName.walkPath(&remain) == kResourceDir) {
1248 // these are the resources, pull their type out of the directory name
1249 kind.initFromDirName(remain.walkPath().string(), &resType);
1250 } else {
1251 // these are untyped and don't have an AaptGroupEntry
1252 }
1253 if (entries.indexOf(kind) < 0) {
1254 entries.add(kind);
1255 mGroupEntries.add(kind);
1256 }
1257
1258 // use the one from the zip file if they both exist.
1259 dir->removeFile(entryName.getPathLeaf());
1260
1261 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
1262 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
1263 if (err != NO_ERROR) {
1264 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
1265 count = err;
1266 goto bail;
1267 }
1268 file->setCompressionMethod(entry->getCompressionMethod());
1269
1270#if 0
1271 if (entryName == "AndroidManifest.xml") {
1272 printf("AndroidManifest.xml\n");
1273 }
1274 printf("\n\nfile: %s\n", entryName.string());
1275#endif
1276
1277 size_t len = entry->getUncompressedLen();
1278 void* data = zip->uncompress(entry);
1279 void* buf = file->editData(len);
1280 memcpy(buf, data, len);
1281
1282#if 0
1283 const int OFF = 0;
1284 const unsigned char* p = (unsigned char*)data;
1285 const unsigned char* end = p+len;
1286 p += OFF;
1287 for (int i=0; i<32 && p < end; i++) {
1288 printf("0x%03x ", i*0x10 + OFF);
1289 for (int j=0; j<0x10 && p < end; j++) {
1290 printf(" %02x", *p);
1291 p++;
1292 }
1293 printf("\n");
1294 }
1295#endif
1296
1297 free(data);
1298
1299 count++;
1300 }
1301
1302bail:
1303 delete zip;
1304 return count;
1305}
1306
1307status_t AaptAssets::filter(Bundle* bundle)
1308{
Adam Lesinskifab50872014-04-16 14:40:42 -07001309 WeakResourceFilter reqFilter;
Adam Lesinski282e1812014-01-23 18:17:42 -08001310 status_t err = reqFilter.parse(bundle->getConfigurations());
1311 if (err != NO_ERROR) {
1312 return err;
1313 }
1314
Adam Lesinskifab50872014-04-16 14:40:42 -07001315 uint32_t preferredDensity = 0;
1316 if (bundle->getPreferredDensity().size() > 0) {
1317 ResTable_config preferredConfig;
1318 if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
1319 fprintf(stderr, "Error parsing preferred density: %s\n",
1320 bundle->getPreferredDensity().string());
1321 return UNKNOWN_ERROR;
1322 }
1323 preferredDensity = preferredConfig.density;
Adam Lesinski282e1812014-01-23 18:17:42 -08001324 }
1325
Adam Lesinskifab50872014-04-16 14:40:42 -07001326 if (reqFilter.isEmpty() && preferredDensity == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001327 return NO_ERROR;
1328 }
1329
1330 if (bundle->getVerbose()) {
1331 if (!reqFilter.isEmpty()) {
1332 printf("Applying required filter: %s\n",
Adam Lesinskifab50872014-04-16 14:40:42 -07001333 bundle->getConfigurations().string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001334 }
Adam Lesinskifab50872014-04-16 14:40:42 -07001335 if (preferredDensity > 0) {
1336 printf("Applying preferred density filter: %s\n",
1337 bundle->getPreferredDensity().string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001338 }
1339 }
1340
1341 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1342 const size_t ND = resdirs.size();
1343 for (size_t i=0; i<ND; i++) {
1344 const sp<AaptDir>& dir = resdirs.itemAt(i);
1345 if (dir->getLeaf() == kValuesDir) {
1346 // The "value" dir is special since a single file defines
1347 // multiple resources, so we can not do filtering on the
1348 // files themselves.
1349 continue;
1350 }
1351 if (dir->getLeaf() == kMipmapDir) {
1352 // We also skip the "mipmap" directory, since the point of this
1353 // is to include all densities without stripping. If you put
1354 // other configurations in here as well they won't be stripped
1355 // either... So don't do that. Seriously. What is wrong with you?
1356 continue;
1357 }
1358
1359 const size_t NG = dir->getFiles().size();
1360 for (size_t j=0; j<NG; j++) {
1361 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
1362
1363 // First remove any configurations we know we don't need.
1364 for (size_t k=0; k<grp->getFiles().size(); k++) {
1365 sp<AaptFile> file = grp->getFiles().valueAt(k);
1366 if (k == 0 && grp->getFiles().size() == 1) {
1367 // If this is the only file left, we need to keep it.
1368 // Otherwise the resource IDs we are using will be inconsistent
1369 // with what we get when not stripping. Sucky, but at least
1370 // for now we can rely on the back-end doing another filtering
1371 // pass to take this out and leave us with this resource name
1372 // containing no entries.
1373 continue;
1374 }
1375 if (file->getPath().getPathExtension() == ".xml") {
1376 // We can't remove .xml files at this point, because when
1377 // we parse them they may add identifier resources, so
1378 // removing them can cause our resource identifiers to
1379 // become inconsistent.
1380 continue;
1381 }
1382 const ResTable_config& config(file->getGroupEntry().toParams());
1383 if (!reqFilter.match(config)) {
1384 if (bundle->getVerbose()) {
1385 printf("Pruning unneeded resource: %s\n",
1386 file->getPrintableSource().string());
1387 }
1388 grp->removeFile(k);
1389 k--;
1390 }
1391 }
1392
1393 // Quick check: no preferred filters, nothing more to do.
Adam Lesinskifab50872014-04-16 14:40:42 -07001394 if (preferredDensity == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001395 continue;
1396 }
1397
Adam Lesinski8cf61842013-10-18 13:42:09 -07001398 // Get the preferred density if there is one. We do not match exactly for density.
1399 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
1400 // pick xhdpi.
Adam Lesinskifab50872014-04-16 14:40:42 -07001401 for (size_t k=0; k<grp->getFiles().size(); k++) {
1402 sp<AaptFile> file = grp->getFiles().valueAt(k);
1403 if (k == 0 && grp->getFiles().size() == 1) {
1404 // If this is the only file left, we need to keep it.
1405 // Otherwise the resource IDs we are using will be inconsistent
1406 // with what we get when not stripping. Sucky, but at least
1407 // for now we can rely on the back-end doing another filtering
1408 // pass to take this out and leave us with this resource name
1409 // containing no entries.
1410 continue;
1411 }
1412 if (file->getPath().getPathExtension() == ".xml") {
1413 // We can't remove .xml files at this point, because when
1414 // we parse them they may add identifier resources, so
1415 // removing them can cause our resource identifiers to
1416 // become inconsistent.
1417 continue;
1418 }
1419 const ResTable_config& config(file->getGroupEntry().toParams());
1420 if (config.density != 0 && config.density != preferredDensity) {
1421 // This is a resource we would prefer not to have. Check
1422 // to see if have a similar variation that we would like
1423 // to have and, if so, we can drop it.
1424 uint32_t bestDensity = config.density;
Adam Lesinski8cf61842013-10-18 13:42:09 -07001425
Adam Lesinskifab50872014-04-16 14:40:42 -07001426 for (size_t m=0; m<grp->getFiles().size(); m++) {
1427 if (m == k) {
1428 continue;
Adam Lesinski282e1812014-01-23 18:17:42 -08001429 }
Adam Lesinski8cf61842013-10-18 13:42:09 -07001430
Adam Lesinskifab50872014-04-16 14:40:42 -07001431 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
1432 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
1433 if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
1434 // See if there is a better density resource
1435 if (mconfig.density < bestDensity &&
Bryan Mawhinneyb0db8de2014-06-06 13:27:11 +01001436 mconfig.density >= preferredDensity &&
Adam Lesinskifab50872014-04-16 14:40:42 -07001437 bestDensity > preferredDensity) {
Bryan Mawhinneyb0db8de2014-06-06 13:27:11 +01001438 // This density is our preferred density, or between our best density and
Adam Lesinskifab50872014-04-16 14:40:42 -07001439 // the preferred density, therefore it is better.
1440 bestDensity = mconfig.density;
1441 } else if (mconfig.density > bestDensity &&
1442 bestDensity < preferredDensity) {
1443 // This density is better than our best density and
1444 // our best density was smaller than our preferred
1445 // density, so it is better.
1446 bestDensity = mconfig.density;
Adam Lesinski8cf61842013-10-18 13:42:09 -07001447 }
Adam Lesinski8cf61842013-10-18 13:42:09 -07001448 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001449 }
Adam Lesinskifab50872014-04-16 14:40:42 -07001450
1451 if (bestDensity != config.density) {
1452 if (bundle->getVerbose()) {
1453 printf("Pruning unneeded resource: %s\n",
1454 file->getPrintableSource().string());
1455 }
1456 grp->removeFile(k);
1457 k--;
1458 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001459 }
1460 }
1461 }
1462 }
1463
1464 return NO_ERROR;
1465}
1466
1467sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1468{
1469 sp<AaptSymbols> sym = mSymbols.valueFor(name);
1470 if (sym == NULL) {
1471 sym = new AaptSymbols();
1472 mSymbols.add(name, sym);
1473 }
1474 return sym;
1475}
1476
1477sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
1478{
1479 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
1480 if (sym == NULL) {
1481 sym = new AaptSymbols();
1482 mJavaSymbols.add(name, sym);
1483 }
1484 return sym;
1485}
1486
1487status_t AaptAssets::applyJavaSymbols()
1488{
1489 size_t N = mJavaSymbols.size();
1490 for (size_t i=0; i<N; i++) {
1491 const String8& name = mJavaSymbols.keyAt(i);
1492 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
1493 ssize_t pos = mSymbols.indexOfKey(name);
1494 if (pos < 0) {
1495 SourcePos pos;
1496 pos.error("Java symbol dir %s not defined\n", name.string());
1497 return UNKNOWN_ERROR;
1498 }
1499 //printf("**** applying java symbols in dir %s\n", name.string());
1500 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1501 if (err != NO_ERROR) {
1502 return err;
1503 }
1504 }
1505
1506 return NO_ERROR;
1507}
1508
1509bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
1510 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
1511 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
1512 // sym.isJavaSymbol ? 1 : 0);
1513 if (!mHavePrivateSymbols) return true;
1514 if (sym.isPublic) return true;
1515 if (includePrivate && sym.isJavaSymbol) return true;
1516 return false;
1517}
1518
1519status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1520{
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001521 if (mHaveIncludedAssets) {
1522 return NO_ERROR;
Adam Lesinski282e1812014-01-23 18:17:42 -08001523 }
1524
Adam Lesinski833f3cc2014-06-18 15:06:01 -07001525 // Add in all includes.
1526 const Vector<String8>& includes = bundle->getPackageIncludes();
1527 const size_t packageIncludeCount = includes.size();
1528 for (size_t i = 0; i < packageIncludeCount; i++) {
1529 if (bundle->getVerbose()) {
1530 printf("Including resources from package: %s\n", includes[i].string());
1531 }
1532
1533 if (!mIncludedAssets.addAssetPath(includes[i], NULL)) {
1534 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
1535 includes[i].string());
1536 return UNKNOWN_ERROR;
1537 }
1538 }
1539
1540 const String8& featureOfBase = bundle->getFeatureOfPackage();
1541 if (!featureOfBase.isEmpty()) {
1542 if (bundle->getVerbose()) {
1543 printf("Including base feature resources from package: %s\n",
1544 featureOfBase.string());
1545 }
1546
1547 if (!mIncludedAssets.addAssetPath(featureOfBase, NULL)) {
1548 fprintf(stderr, "ERROR: base feature package '%s' not found.\n",
1549 featureOfBase.string());
1550 return UNKNOWN_ERROR;
1551 }
1552 }
1553
1554 mHaveIncludedAssets = true;
1555
Adam Lesinski282e1812014-01-23 18:17:42 -08001556 return NO_ERROR;
1557}
1558
1559status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
1560{
1561 const ResTable& res = getIncludedResources();
1562 // XXX dirty!
Narayan Kamath00b31442014-01-27 17:32:37 +00001563 return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
Adam Lesinski282e1812014-01-23 18:17:42 -08001564}
1565
1566const ResTable& AaptAssets::getIncludedResources() const
1567{
1568 return mIncludedAssets.getResources(false);
1569}
1570
Adam Lesinskiad2d07d2014-08-27 16:21:08 -07001571AssetManager& AaptAssets::getAssetManager()
1572{
1573 return mIncludedAssets;
1574}
1575
Adam Lesinski282e1812014-01-23 18:17:42 -08001576void AaptAssets::print(const String8& prefix) const
1577{
1578 String8 innerPrefix(prefix);
1579 innerPrefix.append(" ");
1580 String8 innerInnerPrefix(innerPrefix);
1581 innerInnerPrefix.append(" ");
1582 printf("%sConfigurations:\n", prefix.string());
1583 const size_t N=mGroupEntries.size();
1584 for (size_t i=0; i<N; i++) {
1585 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
1586 printf("%s %s\n", prefix.string(),
1587 cname != "" ? cname.string() : "(default)");
1588 }
1589
1590 printf("\n%sFiles:\n", prefix.string());
1591 AaptDir::print(innerPrefix);
1592
1593 printf("\n%sResource Dirs:\n", prefix.string());
1594 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1595 const size_t NR = resdirs.size();
1596 for (size_t i=0; i<NR; i++) {
1597 const sp<AaptDir>& d = resdirs.itemAt(i);
1598 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
1599 d->print(innerInnerPrefix);
1600 }
1601}
1602
1603sp<AaptDir> AaptAssets::resDir(const String8& name) const
1604{
1605 const Vector<sp<AaptDir> >& resdirs = mResDirs;
1606 const size_t N = resdirs.size();
1607 for (size_t i=0; i<N; i++) {
1608 const sp<AaptDir>& d = resdirs.itemAt(i);
1609 if (d->getLeaf() == name) {
1610 return d;
1611 }
1612 }
1613 return NULL;
1614}
1615
1616bool
1617valid_symbol_name(const String8& symbol)
1618{
1619 static char const * const KEYWORDS[] = {
1620 "abstract", "assert", "boolean", "break",
1621 "byte", "case", "catch", "char", "class", "const", "continue",
1622 "default", "do", "double", "else", "enum", "extends", "final",
1623 "finally", "float", "for", "goto", "if", "implements", "import",
1624 "instanceof", "int", "interface", "long", "native", "new", "package",
1625 "private", "protected", "public", "return", "short", "static",
1626 "strictfp", "super", "switch", "synchronized", "this", "throw",
1627 "throws", "transient", "try", "void", "volatile", "while",
1628 "true", "false", "null",
1629 NULL
1630 };
1631 const char*const* k = KEYWORDS;
1632 const char*const s = symbol.string();
1633 while (*k) {
1634 if (0 == strcmp(s, *k)) {
1635 return false;
1636 }
1637 k++;
1638 }
1639 return true;
1640}