blob: e0dab785bc472384442ddf936722f989020111b6 [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"
6#include "ResourceFilter.h"
7#include "Main.h"
8
9#include <utils/misc.h>
10#include <utils/SortedVector.h>
11
12#include <ctype.h>
13#include <dirent.h>
14#include <errno.h>
15
16static const char* kDefaultLocale = "default";
17static const char* kWildcardName = "any";
18static 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 */ void AaptLocaleValue::splitAndLowerCase(const char* const chars,
153 Vector<String8>* parts, const char separator) {
154 const char *p = chars;
155 const char *q;
156 while (NULL != (q = strchr(p, separator))) {
157 String8 val(p, q - p);
158 val.toLower();
159 parts->add(val);
160 p = q+1;
161 }
162
163 if (p < chars + strlen(chars)) {
164 String8 val(p);
165 val.toLower();
166 parts->add(val);
167 }
168}
169
170/* static */
171inline bool isAlpha(const String8& string) {
172 const size_t length = string.length();
173 for (size_t i = 0; i < length; ++i) {
174 if (!isalpha(string[i])) {
175 return false;
176 }
177 }
178
179 return true;
180}
181
182/* static */
183inline bool isNumber(const String8& string) {
184 const size_t length = string.length();
185 for (size_t i = 0; i < length; ++i) {
186 if (!isdigit(string[i])) {
187 return false;
188 }
189 }
190
191 return true;
192}
193
194void AaptLocaleValue::setLanguage(const char* languageChars) {
195 size_t i = 0;
196 while ((*languageChars) != '\0') {
197 language[i++] = tolower(*languageChars);
198 languageChars++;
199 }
200}
201
202void AaptLocaleValue::setRegion(const char* regionChars) {
203 size_t i = 0;
204 while ((*regionChars) != '\0') {
205 region[i++] = toupper(*regionChars);
206 regionChars++;
207 }
208}
209
210void AaptLocaleValue::setScript(const char* scriptChars) {
211 size_t i = 0;
212 while ((*scriptChars) != '\0') {
213 if (i == 0) {
214 script[i++] = toupper(*scriptChars);
215 } else {
216 script[i++] = tolower(*scriptChars);
217 }
218 scriptChars++;
219 }
220}
221
222void AaptLocaleValue::setVariant(const char* variantChars) {
223 size_t i = 0;
224 while ((*variantChars) != '\0') {
225 variant[i++] = *variantChars;
226 variantChars++;
227 }
228}
229
230bool AaptLocaleValue::initFromFilterString(const String8& str) {
231 // A locale (as specified in the filter) is an underscore separated name such
232 // as "en_US", "en_Latn_US", or "en_US_POSIX".
233 Vector<String8> parts;
234 splitAndLowerCase(str.string(), &parts, '_');
235
236 const int numTags = parts.size();
237 bool valid = false;
238 if (numTags >= 1) {
239 const String8& lang = parts[0];
240 if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
241 setLanguage(lang.string());
242 valid = true;
243 }
244 }
245
246 if (!valid || numTags == 1) {
247 return valid;
248 }
249
250 // At this point, valid == true && numTags > 1.
251 const String8& part2 = parts[1];
252 if ((part2.length() == 2 && isAlpha(part2)) ||
253 (part2.length() == 3 && isNumber(part2))) {
254 setRegion(part2.string());
255 } else if (part2.length() == 4 && isAlpha(part2)) {
256 setScript(part2.string());
257 } else if (part2.length() >= 5 && part2.length() <= 8) {
258 setVariant(part2.string());
259 } else {
260 valid = false;
261 }
262
263 if (!valid || numTags == 2) {
264 return valid;
265 }
266
267 // At this point, valid == true && numTags > 1.
268 const String8& part3 = parts[2];
269 if (((part3.length() == 2 && isAlpha(part3)) ||
270 (part3.length() == 3 && isNumber(part3))) && script[0]) {
271 setRegion(part3.string());
272 } else if (part3.length() >= 5 && part3.length() <= 8) {
273 setVariant(part3.string());
274 } else {
275 valid = false;
276 }
277
278 if (!valid || numTags == 3) {
279 return valid;
280 }
281
282 const String8& part4 = parts[3];
283 if (part4.length() >= 5 && part4.length() <= 8) {
284 setVariant(part4.string());
285 } else {
286 valid = false;
287 }
288
289 if (!valid || numTags > 4) {
290 return false;
291 }
292
293 return true;
294}
295
296int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
297 const int size = parts.size();
298 int currentIndex = startIndex;
299
300 String8 part = parts[currentIndex];
301 if (part[0] == 'b' && part[1] == '+') {
302 // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
303 // except that the separator is "+" and not "-".
304 Vector<String8> subtags;
305 AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+');
306 subtags.removeItemsAt(0);
307 if (subtags.size() == 1) {
308 setLanguage(subtags[0]);
309 } else if (subtags.size() == 2) {
310 setLanguage(subtags[0]);
311
312 // The second tag can either be a region, a variant or a script.
313 switch (subtags[1].size()) {
314 case 2:
315 case 3:
316 setRegion(subtags[1]);
317 break;
318 case 4:
319 setScript(subtags[1]);
320 break;
321 case 5:
322 case 6:
323 case 7:
324 case 8:
325 setVariant(subtags[1]);
326 break;
327 default:
328 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n",
329 part.string());
330 return -1;
331 }
332 } else if (subtags.size() == 3) {
333 // The language is always the first subtag.
334 setLanguage(subtags[0]);
335
336 // The second subtag can either be a script or a region code.
337 // If its size is 4, it's a script code, else it's a region code.
338 bool hasRegion = false;
339 if (subtags[1].size() == 4) {
340 setScript(subtags[1]);
341 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
342 setRegion(subtags[1]);
343 hasRegion = true;
344 } else {
345 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", part.string());
346 return -1;
347 }
348
349 // The third tag can either be a region code (if the second tag was
350 // a script), else a variant code.
351 if (subtags[2].size() > 4) {
352 setVariant(subtags[2]);
353 } else {
354 setRegion(subtags[2]);
355 }
356 } else if (subtags.size() == 4) {
357 setLanguage(subtags[0]);
358 setScript(subtags[1]);
359 setRegion(subtags[2]);
360 setVariant(subtags[3]);
361 } else {
362 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name: %s\n", part.string());
363 return -1;
364 }
365
366 return ++currentIndex;
367 } else {
368 if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) {
369 setLanguage(part);
370 if (++currentIndex == size) {
371 return size;
372 }
373 } else {
374 return currentIndex;
375 }
376
377 part = parts[currentIndex];
378 if (part.string()[0] == 'r' && part.length() == 3) {
379 setRegion(part.string() + 1);
380 if (++currentIndex == size) {
381 return size;
382 }
383 }
384 }
385
386 return currentIndex;
387}
388
389
390String8 AaptLocaleValue::toDirName() const {
391 String8 dirName("");
392 if (language[0]) {
393 dirName += language;
394 } else {
395 return dirName;
396 }
397
398 if (script[0]) {
399 dirName += "-s";
400 dirName += script;
401 }
402
403 if (region[0]) {
404 dirName += "-r";
405 dirName += region;
406 }
407
408 if (variant[0]) {
409 dirName += "-v";
410 dirName += variant;
411 }
412
413 return dirName;
414}
415
416void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
417 config.unpackLanguage(language);
418 config.unpackRegion(region);
419 if (config.localeScript[0]) {
420 memcpy(script, config.localeScript, sizeof(config.localeScript));
421 }
422
423 if (config.localeVariant[0]) {
424 memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
425 }
426}
427
428void AaptLocaleValue::writeTo(ResTable_config* out) const {
429 out->packLanguage(language);
430 out->packRegion(region);
431
432 if (script[0]) {
433 memcpy(out->localeScript, script, sizeof(out->localeScript));
434 }
435
436 if (variant[0]) {
437 memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
438 }
439}
440
441
442/* static */ bool
443AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value)
Adam Lesinski282e1812014-01-23 18:17:42 -0800444{
445 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000446 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800447
448 // IMSI - MCC
449 if (getMccName(part.string(), &config)) {
450 *axis = AXIS_MCC;
Narayan Kamath91447d82014-01-21 15:32:36 +0000451 value->intValue = config.mcc;
452 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800453 }
454
455 // IMSI - MNC
456 if (getMncName(part.string(), &config)) {
457 *axis = AXIS_MNC;
Narayan Kamath91447d82014-01-21 15:32:36 +0000458 value->intValue = config.mnc;
459 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800460 }
461
462 // locale - language
Narayan Kamath91447d82014-01-21 15:32:36 +0000463 if (value->localeValue.initFromFilterString(part)) {
464 *axis = AXIS_LOCALE;
465 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800466 }
467
468 // layout direction
469 if (getLayoutDirectionName(part.string(), &config)) {
470 *axis = AXIS_LAYOUTDIR;
Narayan Kamath91447d82014-01-21 15:32:36 +0000471 value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
472 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800473 }
474
475 // smallest screen dp width
476 if (getSmallestScreenWidthDpName(part.string(), &config)) {
477 *axis = AXIS_SMALLESTSCREENWIDTHDP;
Narayan Kamath91447d82014-01-21 15:32:36 +0000478 value->intValue = config.smallestScreenWidthDp;
479 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800480 }
481
482 // screen dp width
483 if (getScreenWidthDpName(part.string(), &config)) {
484 *axis = AXIS_SCREENWIDTHDP;
Narayan Kamath91447d82014-01-21 15:32:36 +0000485 value->intValue = config.screenWidthDp;
486 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800487 }
488
489 // screen dp height
490 if (getScreenHeightDpName(part.string(), &config)) {
491 *axis = AXIS_SCREENHEIGHTDP;
Narayan Kamath91447d82014-01-21 15:32:36 +0000492 value->intValue = config.screenHeightDp;
493 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800494 }
495
496 // screen layout size
497 if (getScreenLayoutSizeName(part.string(), &config)) {
498 *axis = AXIS_SCREENLAYOUTSIZE;
Narayan Kamath91447d82014-01-21 15:32:36 +0000499 value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
500 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800501 }
502
503 // screen layout long
504 if (getScreenLayoutLongName(part.string(), &config)) {
505 *axis = AXIS_SCREENLAYOUTLONG;
Narayan Kamath91447d82014-01-21 15:32:36 +0000506 value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
507 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800508 }
509
510 // orientation
511 if (getOrientationName(part.string(), &config)) {
512 *axis = AXIS_ORIENTATION;
Narayan Kamath91447d82014-01-21 15:32:36 +0000513 value->intValue = config.orientation;
514 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800515 }
516
517 // ui mode type
518 if (getUiModeTypeName(part.string(), &config)) {
519 *axis = AXIS_UIMODETYPE;
Narayan Kamath91447d82014-01-21 15:32:36 +0000520 value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
521 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800522 }
523
524 // ui mode night
525 if (getUiModeNightName(part.string(), &config)) {
526 *axis = AXIS_UIMODENIGHT;
Narayan Kamath91447d82014-01-21 15:32:36 +0000527 value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
528 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800529 }
530
531 // density
532 if (getDensityName(part.string(), &config)) {
533 *axis = AXIS_DENSITY;
Narayan Kamath91447d82014-01-21 15:32:36 +0000534 value->intValue = config.density;
535 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800536 }
537
538 // touchscreen
539 if (getTouchscreenName(part.string(), &config)) {
540 *axis = AXIS_TOUCHSCREEN;
Narayan Kamath91447d82014-01-21 15:32:36 +0000541 value->intValue = config.touchscreen;
542 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800543 }
544
545 // keyboard hidden
546 if (getKeysHiddenName(part.string(), &config)) {
547 *axis = AXIS_KEYSHIDDEN;
Narayan Kamath91447d82014-01-21 15:32:36 +0000548 value->intValue = config.inputFlags;
549 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800550 }
551
552 // keyboard
553 if (getKeyboardName(part.string(), &config)) {
554 *axis = AXIS_KEYBOARD;
Narayan Kamath91447d82014-01-21 15:32:36 +0000555 value->intValue = config.keyboard;
556 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800557 }
558
559 // navigation hidden
560 if (getNavHiddenName(part.string(), &config)) {
561 *axis = AXIS_NAVHIDDEN;
Narayan Kamath91447d82014-01-21 15:32:36 +0000562 value->intValue = config.inputFlags;
Adam Lesinski282e1812014-01-23 18:17:42 -0800563 return 0;
564 }
565
566 // navigation
567 if (getNavigationName(part.string(), &config)) {
568 *axis = AXIS_NAVIGATION;
Narayan Kamath91447d82014-01-21 15:32:36 +0000569 value->intValue = config.navigation;
570 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800571 }
572
573 // screen size
574 if (getScreenSizeName(part.string(), &config)) {
575 *axis = AXIS_SCREENSIZE;
Narayan Kamath91447d82014-01-21 15:32:36 +0000576 value->intValue = config.screenSize;
577 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800578 }
579
580 // version
581 if (getVersionName(part.string(), &config)) {
582 *axis = AXIS_VERSION;
Narayan Kamath91447d82014-01-21 15:32:36 +0000583 value->intValue = config.version;
584 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800585 }
586
Narayan Kamath91447d82014-01-21 15:32:36 +0000587 return false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800588}
589
Narayan Kamath91447d82014-01-21 15:32:36 +0000590AxisValue
Adam Lesinski282e1812014-01-23 18:17:42 -0800591AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
592{
Narayan Kamath91447d82014-01-21 15:32:36 +0000593 AxisValue value;
Adam Lesinski282e1812014-01-23 18:17:42 -0800594 switch (axis) {
595 case AXIS_MCC:
Narayan Kamath91447d82014-01-21 15:32:36 +0000596 value.intValue = config.mcc;
597 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800598 case AXIS_MNC:
Narayan Kamath91447d82014-01-21 15:32:36 +0000599 value.intValue = config.mnc;
600 break;
601 case AXIS_LOCALE:
602 value.localeValue.initFromResTable(config);
603 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800604 case AXIS_LAYOUTDIR:
Narayan Kamath91447d82014-01-21 15:32:36 +0000605 value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
606 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800607 case AXIS_SCREENLAYOUTSIZE:
Narayan Kamath91447d82014-01-21 15:32:36 +0000608 value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
609 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800610 case AXIS_ORIENTATION:
Narayan Kamath91447d82014-01-21 15:32:36 +0000611 value.intValue = config.orientation;
612 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800613 case AXIS_UIMODETYPE:
Narayan Kamath91447d82014-01-21 15:32:36 +0000614 value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
615 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800616 case AXIS_UIMODENIGHT:
Narayan Kamath91447d82014-01-21 15:32:36 +0000617 value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
618 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800619 case AXIS_DENSITY:
Narayan Kamath91447d82014-01-21 15:32:36 +0000620 value.intValue = config.density;
621 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800622 case AXIS_TOUCHSCREEN:
Narayan Kamath91447d82014-01-21 15:32:36 +0000623 value.intValue = config.touchscreen;
624 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800625 case AXIS_KEYSHIDDEN:
Narayan Kamath91447d82014-01-21 15:32:36 +0000626 value.intValue = config.inputFlags;
627 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800628 case AXIS_KEYBOARD:
Narayan Kamath91447d82014-01-21 15:32:36 +0000629 value.intValue = config.keyboard;
630 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800631 case AXIS_NAVIGATION:
Narayan Kamath91447d82014-01-21 15:32:36 +0000632 value.intValue = config.navigation;
633 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800634 case AXIS_SCREENSIZE:
Narayan Kamath91447d82014-01-21 15:32:36 +0000635 value.intValue = config.screenSize;
636 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800637 case AXIS_SMALLESTSCREENWIDTHDP:
Narayan Kamath91447d82014-01-21 15:32:36 +0000638 value.intValue = config.smallestScreenWidthDp;
639 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800640 case AXIS_SCREENWIDTHDP:
Narayan Kamath91447d82014-01-21 15:32:36 +0000641 value.intValue = config.screenWidthDp;
642 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800643 case AXIS_SCREENHEIGHTDP:
Narayan Kamath91447d82014-01-21 15:32:36 +0000644 value.intValue = config.screenHeightDp;
645 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800646 case AXIS_VERSION:
Narayan Kamath91447d82014-01-21 15:32:36 +0000647 value.intValue = config.version;
648 break;
Adam Lesinski282e1812014-01-23 18:17:42 -0800649 }
Narayan Kamath91447d82014-01-21 15:32:36 +0000650
651 return value;
Adam Lesinski282e1812014-01-23 18:17:42 -0800652}
653
654bool
655AaptGroupEntry::configSameExcept(const ResTable_config& config,
656 const ResTable_config& otherConfig, int axis)
657{
658 for (int i=AXIS_START; i<=AXIS_END; i++) {
659 if (i == axis) {
660 continue;
661 }
662 if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
663 return false;
664 }
665 }
666 return true;
667}
668
669bool
670AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
671{
672 mParamsChanged = true;
673
674 Vector<String8> parts;
Narayan Kamath91447d82014-01-21 15:32:36 +0000675 AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
Adam Lesinski282e1812014-01-23 18:17:42 -0800676
Narayan Kamath91447d82014-01-21 15:32:36 +0000677 String8 mcc, mnc, layoutsize, layoutlong, orient, den;
Adam Lesinski282e1812014-01-23 18:17:42 -0800678 String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
679 String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
680
Narayan Kamath91447d82014-01-21 15:32:36 +0000681 AaptLocaleValue locale;
682 int numLocaleComponents = 0;
Adam Lesinski282e1812014-01-23 18:17:42 -0800683
684 const int N = parts.size();
685 int index = 0;
686 String8 part = parts[index];
687
688 // resource type
689 if (!isValidResourceType(part)) {
690 return false;
691 }
692 *resType = part;
693
694 index++;
695 if (index == N) {
696 goto success;
697 }
698 part = parts[index];
699
700 // imsi - mcc
701 if (getMccName(part.string())) {
702 mcc = part;
703
704 index++;
705 if (index == N) {
706 goto success;
707 }
708 part = parts[index];
709 } else {
710 //printf("not mcc: %s\n", part.string());
711 }
712
713 // imsi - mnc
714 if (getMncName(part.string())) {
715 mnc = part;
716
717 index++;
718 if (index == N) {
719 goto success;
720 }
721 part = parts[index];
722 } else {
Narayan Kamath91447d82014-01-21 15:32:36 +0000723 //printf("not mnc: %s\n", part.string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800724 }
725
Narayan Kamath91447d82014-01-21 15:32:36 +0000726 index = locale.initFromDirName(parts, index);
727 if (index == -1) {
728 return false;
729 }
730 if (index >= N){
731 goto success;
Adam Lesinski282e1812014-01-23 18:17:42 -0800732 }
733
Narayan Kamath91447d82014-01-21 15:32:36 +0000734 part = parts[index];
Adam Lesinski282e1812014-01-23 18:17:42 -0800735 if (getLayoutDirectionName(part.string())) {
736 layoutDir = part;
737
738 index++;
739 if (index == N) {
740 goto success;
741 }
742 part = parts[index];
743 } else {
744 //printf("not layout direction: %s\n", part.string());
745 }
746
747 if (getSmallestScreenWidthDpName(part.string())) {
748 smallestwidthdp = part;
749
750 index++;
751 if (index == N) {
752 goto success;
753 }
754 part = parts[index];
755 } else {
756 //printf("not smallest screen width dp: %s\n", part.string());
757 }
758
759 if (getScreenWidthDpName(part.string())) {
760 widthdp = part;
761
762 index++;
763 if (index == N) {
764 goto success;
765 }
766 part = parts[index];
767 } else {
768 //printf("not screen width dp: %s\n", part.string());
769 }
770
771 if (getScreenHeightDpName(part.string())) {
772 heightdp = part;
773
774 index++;
775 if (index == N) {
776 goto success;
777 }
778 part = parts[index];
779 } else {
780 //printf("not screen height dp: %s\n", part.string());
781 }
782
783 if (getScreenLayoutSizeName(part.string())) {
784 layoutsize = part;
785
786 index++;
787 if (index == N) {
788 goto success;
789 }
790 part = parts[index];
791 } else {
792 //printf("not screen layout size: %s\n", part.string());
793 }
794
795 if (getScreenLayoutLongName(part.string())) {
796 layoutlong = part;
797
798 index++;
799 if (index == N) {
800 goto success;
801 }
802 part = parts[index];
803 } else {
804 //printf("not screen layout long: %s\n", part.string());
805 }
806
807 // orientation
808 if (getOrientationName(part.string())) {
809 orient = part;
810
811 index++;
812 if (index == N) {
813 goto success;
814 }
815 part = parts[index];
816 } else {
817 //printf("not orientation: %s\n", part.string());
818 }
819
820 // ui mode type
821 if (getUiModeTypeName(part.string())) {
822 uiModeType = part;
823
824 index++;
825 if (index == N) {
826 goto success;
827 }
828 part = parts[index];
829 } else {
830 //printf("not ui mode type: %s\n", part.string());
831 }
832
833 // ui mode night
834 if (getUiModeNightName(part.string())) {
835 uiModeNight = part;
836
837 index++;
838 if (index == N) {
839 goto success;
840 }
841 part = parts[index];
842 } else {
843 //printf("not ui mode night: %s\n", part.string());
844 }
845
846 // density
847 if (getDensityName(part.string())) {
848 den = part;
849
850 index++;
851 if (index == N) {
852 goto success;
853 }
854 part = parts[index];
855 } else {
856 //printf("not density: %s\n", part.string());
857 }
858
859 // touchscreen
860 if (getTouchscreenName(part.string())) {
861 touch = part;
862
863 index++;
864 if (index == N) {
865 goto success;
866 }
867 part = parts[index];
868 } else {
869 //printf("not touchscreen: %s\n", part.string());
870 }
871
872 // keyboard hidden
873 if (getKeysHiddenName(part.string())) {
874 keysHidden = part;
875
876 index++;
877 if (index == N) {
878 goto success;
879 }
880 part = parts[index];
881 } else {
882 //printf("not keysHidden: %s\n", part.string());
883 }
884
885 // keyboard
886 if (getKeyboardName(part.string())) {
887 key = part;
888
889 index++;
890 if (index == N) {
891 goto success;
892 }
893 part = parts[index];
894 } else {
895 //printf("not keyboard: %s\n", part.string());
896 }
897
898 // navigation hidden
899 if (getNavHiddenName(part.string())) {
900 navHidden = part;
901
902 index++;
903 if (index == N) {
904 goto success;
905 }
906 part = parts[index];
907 } else {
908 //printf("not navHidden: %s\n", part.string());
909 }
910
911 if (getNavigationName(part.string())) {
912 nav = part;
913
914 index++;
915 if (index == N) {
916 goto success;
917 }
918 part = parts[index];
919 } else {
920 //printf("not navigation: %s\n", part.string());
921 }
922
923 if (getScreenSizeName(part.string())) {
924 size = part;
925
926 index++;
927 if (index == N) {
928 goto success;
929 }
930 part = parts[index];
931 } else {
932 //printf("not screen size: %s\n", part.string());
933 }
934
935 if (getVersionName(part.string())) {
936 vers = part;
937
938 index++;
939 if (index == N) {
940 goto success;
941 }
942 part = parts[index];
943 } else {
944 //printf("not version: %s\n", part.string());
945 }
946
947 // if there are extra parts, it doesn't match
948 return false;
949
950success:
951 this->mcc = mcc;
952 this->mnc = mnc;
Narayan Kamath91447d82014-01-21 15:32:36 +0000953 this->locale = locale;
Adam Lesinski282e1812014-01-23 18:17:42 -0800954 this->screenLayoutSize = layoutsize;
955 this->screenLayoutLong = layoutlong;
956 this->smallestScreenWidthDp = smallestwidthdp;
957 this->screenWidthDp = widthdp;
958 this->screenHeightDp = heightdp;
959 this->orientation = orient;
960 this->uiModeType = uiModeType;
961 this->uiModeNight = uiModeNight;
962 this->density = den;
963 this->touchscreen = touch;
964 this->keysHidden = keysHidden;
965 this->keyboard = key;
966 this->navHidden = navHidden;
967 this->navigation = nav;
968 this->screenSize = size;
969 this->layoutDirection = layoutDir;
970 this->version = vers;
971
972 // what is this anyway?
973 this->vendor = "";
974
975 return true;
976}
977
978String8
979AaptGroupEntry::toString() const
980{
981 String8 s = this->mcc;
982 s += ",";
983 s += this->mnc;
984 s += ",";
Narayan Kamath91447d82014-01-21 15:32:36 +0000985 s += locale.toDirName();
Adam Lesinski282e1812014-01-23 18:17:42 -0800986 s += ",";
987 s += layoutDirection;
988 s += ",";
989 s += smallestScreenWidthDp;
990 s += ",";
991 s += screenWidthDp;
992 s += ",";
993 s += screenHeightDp;
994 s += ",";
995 s += screenLayoutSize;
996 s += ",";
997 s += screenLayoutLong;
998 s += ",";
999 s += this->orientation;
1000 s += ",";
1001 s += uiModeType;
1002 s += ",";
1003 s += uiModeNight;
1004 s += ",";
1005 s += density;
1006 s += ",";
1007 s += touchscreen;
1008 s += ",";
1009 s += keysHidden;
1010 s += ",";
1011 s += keyboard;
1012 s += ",";
1013 s += navHidden;
1014 s += ",";
1015 s += navigation;
1016 s += ",";
1017 s += screenSize;
1018 s += ",";
1019 s += version;
1020 return s;
1021}
1022
1023String8
1024AaptGroupEntry::toDirName(const String8& resType) const
1025{
1026 String8 s = resType;
1027 if (this->mcc != "") {
1028 if (s.length() > 0) {
1029 s += "-";
1030 }
1031 s += mcc;
1032 }
1033 if (this->mnc != "") {
1034 if (s.length() > 0) {
1035 s += "-";
1036 }
1037 s += mnc;
1038 }
Narayan Kamath91447d82014-01-21 15:32:36 +00001039
1040 const String8 localeComponent = locale.toDirName();
1041 if (localeComponent != "") {
1042 if (s.length() > 0) {
1043 s += "-";
1044 }
1045 s += localeComponent;
Adam Lesinski282e1812014-01-23 18:17:42 -08001046 }
Narayan Kamath91447d82014-01-21 15:32:36 +00001047
Adam Lesinski282e1812014-01-23 18:17:42 -08001048 if (this->layoutDirection != "") {
1049 if (s.length() > 0) {
1050 s += "-";
1051 }
1052 s += layoutDirection;
1053 }
1054 if (this->smallestScreenWidthDp != "") {
1055 if (s.length() > 0) {
1056 s += "-";
1057 }
1058 s += smallestScreenWidthDp;
1059 }
1060 if (this->screenWidthDp != "") {
1061 if (s.length() > 0) {
1062 s += "-";
1063 }
1064 s += screenWidthDp;
1065 }
1066 if (this->screenHeightDp != "") {
1067 if (s.length() > 0) {
1068 s += "-";
1069 }
1070 s += screenHeightDp;
1071 }
1072 if (this->screenLayoutSize != "") {
1073 if (s.length() > 0) {
1074 s += "-";
1075 }
1076 s += screenLayoutSize;
1077 }
1078 if (this->screenLayoutLong != "") {
1079 if (s.length() > 0) {
1080 s += "-";
1081 }
1082 s += screenLayoutLong;
1083 }
1084 if (this->orientation != "") {
1085 if (s.length() > 0) {
1086 s += "-";
1087 }
1088 s += orientation;
1089 }
1090 if (this->uiModeType != "") {
1091 if (s.length() > 0) {
1092 s += "-";
1093 }
1094 s += uiModeType;
1095 }
1096 if (this->uiModeNight != "") {
1097 if (s.length() > 0) {
1098 s += "-";
1099 }
1100 s += uiModeNight;
1101 }
1102 if (this->density != "") {
1103 if (s.length() > 0) {
1104 s += "-";
1105 }
1106 s += density;
1107 }
1108 if (this->touchscreen != "") {
1109 if (s.length() > 0) {
1110 s += "-";
1111 }
1112 s += touchscreen;
1113 }
1114 if (this->keysHidden != "") {
1115 if (s.length() > 0) {
1116 s += "-";
1117 }
1118 s += keysHidden;
1119 }
1120 if (this->keyboard != "") {
1121 if (s.length() > 0) {
1122 s += "-";
1123 }
1124 s += keyboard;
1125 }
1126 if (this->navHidden != "") {
1127 if (s.length() > 0) {
1128 s += "-";
1129 }
1130 s += navHidden;
1131 }
1132 if (this->navigation != "") {
1133 if (s.length() > 0) {
1134 s += "-";
1135 }
1136 s += navigation;
1137 }
1138 if (this->screenSize != "") {
1139 if (s.length() > 0) {
1140 s += "-";
1141 }
1142 s += screenSize;
1143 }
1144 if (this->version != "") {
1145 if (s.length() > 0) {
1146 s += "-";
1147 }
1148 s += version;
1149 }
1150
1151 return s;
1152}
1153
1154bool AaptGroupEntry::getMccName(const char* name,
1155 ResTable_config* out)
1156{
1157 if (strcmp(name, kWildcardName) == 0) {
1158 if (out) out->mcc = 0;
1159 return true;
1160 }
1161 const char* c = name;
1162 if (tolower(*c) != 'm') return false;
1163 c++;
1164 if (tolower(*c) != 'c') return false;
1165 c++;
1166 if (tolower(*c) != 'c') return false;
1167 c++;
1168
1169 const char* val = c;
1170
1171 while (*c >= '0' && *c <= '9') {
1172 c++;
1173 }
1174 if (*c != 0) return false;
1175 if (c-val != 3) return false;
1176
1177 int d = atoi(val);
1178 if (d != 0) {
1179 if (out) out->mcc = d;
1180 return true;
1181 }
1182
1183 return false;
1184}
1185
1186bool AaptGroupEntry::getMncName(const char* name,
1187 ResTable_config* out)
1188{
1189 if (strcmp(name, kWildcardName) == 0) {
1190 if (out) out->mcc = 0;
1191 return true;
1192 }
1193 const char* c = name;
1194 if (tolower(*c) != 'm') return false;
1195 c++;
1196 if (tolower(*c) != 'n') return false;
1197 c++;
1198 if (tolower(*c) != 'c') return false;
1199 c++;
1200
1201 const char* val = c;
1202
1203 while (*c >= '0' && *c <= '9') {
1204 c++;
1205 }
1206 if (*c != 0) return false;
1207 if (c-val == 0 || c-val > 3) return false;
1208
1209 if (out) {
1210 out->mnc = atoi(val);
1211 if (out->mnc == 0) {
1212 out->mnc = ACONFIGURATION_MNC_ZERO;
1213 }
1214 }
1215
1216 return true;
1217}
1218
Adam Lesinski282e1812014-01-23 18:17:42 -08001219bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
1220{
1221 if (strcmp(name, kWildcardName) == 0) {
1222 if (out) out->screenLayout =
1223 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1224 | ResTable_config::LAYOUTDIR_ANY;
1225 return true;
1226 } else if (strcmp(name, "ldltr") == 0) {
1227 if (out) out->screenLayout =
1228 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1229 | ResTable_config::LAYOUTDIR_LTR;
1230 return true;
1231 } else if (strcmp(name, "ldrtl") == 0) {
1232 if (out) out->screenLayout =
1233 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1234 | ResTable_config::LAYOUTDIR_RTL;
1235 return true;
1236 }
1237
1238 return false;
1239}
1240
1241bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
1242 ResTable_config* out)
1243{
1244 if (strcmp(name, kWildcardName) == 0) {
1245 if (out) out->screenLayout =
1246 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1247 | ResTable_config::SCREENSIZE_ANY;
1248 return true;
1249 } else if (strcmp(name, "small") == 0) {
1250 if (out) out->screenLayout =
1251 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1252 | ResTable_config::SCREENSIZE_SMALL;
1253 return true;
1254 } else if (strcmp(name, "normal") == 0) {
1255 if (out) out->screenLayout =
1256 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1257 | ResTable_config::SCREENSIZE_NORMAL;
1258 return true;
1259 } else if (strcmp(name, "large") == 0) {
1260 if (out) out->screenLayout =
1261 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1262 | ResTable_config::SCREENSIZE_LARGE;
1263 return true;
1264 } else if (strcmp(name, "xlarge") == 0) {
1265 if (out) out->screenLayout =
1266 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1267 | ResTable_config::SCREENSIZE_XLARGE;
1268 return true;
1269 }
1270
1271 return false;
1272}
1273
1274bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
1275 ResTable_config* out)
1276{
1277 if (strcmp(name, kWildcardName) == 0) {
1278 if (out) out->screenLayout =
1279 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1280 | ResTable_config::SCREENLONG_ANY;
1281 return true;
1282 } else if (strcmp(name, "long") == 0) {
1283 if (out) out->screenLayout =
1284 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1285 | ResTable_config::SCREENLONG_YES;
1286 return true;
1287 } else if (strcmp(name, "notlong") == 0) {
1288 if (out) out->screenLayout =
1289 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1290 | ResTable_config::SCREENLONG_NO;
1291 return true;
1292 }
1293
1294 return false;
1295}
1296
1297bool AaptGroupEntry::getOrientationName(const char* name,
1298 ResTable_config* out)
1299{
1300 if (strcmp(name, kWildcardName) == 0) {
1301 if (out) out->orientation = out->ORIENTATION_ANY;
1302 return true;
1303 } else if (strcmp(name, "port") == 0) {
1304 if (out) out->orientation = out->ORIENTATION_PORT;
1305 return true;
1306 } else if (strcmp(name, "land") == 0) {
1307 if (out) out->orientation = out->ORIENTATION_LAND;
1308 return true;
1309 } else if (strcmp(name, "square") == 0) {
1310 if (out) out->orientation = out->ORIENTATION_SQUARE;
1311 return true;
1312 }
1313
1314 return false;
1315}
1316
1317bool AaptGroupEntry::getUiModeTypeName(const char* name,
1318 ResTable_config* out)
1319{
1320 if (strcmp(name, kWildcardName) == 0) {
1321 if (out) out->uiMode =
1322 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1323 | ResTable_config::UI_MODE_TYPE_ANY;
1324 return true;
1325 } else if (strcmp(name, "desk") == 0) {
1326 if (out) out->uiMode =
1327 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1328 | ResTable_config::UI_MODE_TYPE_DESK;
1329 return true;
1330 } else if (strcmp(name, "car") == 0) {
1331 if (out) out->uiMode =
1332 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1333 | ResTable_config::UI_MODE_TYPE_CAR;
1334 return true;
1335 } else if (strcmp(name, "television") == 0) {
1336 if (out) out->uiMode =
1337 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1338 | ResTable_config::UI_MODE_TYPE_TELEVISION;
1339 return true;
1340 } else if (strcmp(name, "appliance") == 0) {
1341 if (out) out->uiMode =
1342 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1343 | ResTable_config::UI_MODE_TYPE_APPLIANCE;
1344 return true;
John Spurlock6c191292014-04-03 16:37:27 -04001345 } else if (strcmp(name, "watch") == 0) {
1346 if (out) out->uiMode =
1347 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1348 | ResTable_config::UI_MODE_TYPE_WATCH;
1349 return true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001350 }
1351
1352 return false;
1353}
1354
1355bool AaptGroupEntry::getUiModeNightName(const char* name,
1356 ResTable_config* out)
1357{
1358 if (strcmp(name, kWildcardName) == 0) {
1359 if (out) out->uiMode =
1360 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1361 | ResTable_config::UI_MODE_NIGHT_ANY;
1362 return true;
1363 } else if (strcmp(name, "night") == 0) {
1364 if (out) out->uiMode =
1365 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1366 | ResTable_config::UI_MODE_NIGHT_YES;
1367 return true;
1368 } else if (strcmp(name, "notnight") == 0) {
1369 if (out) out->uiMode =
1370 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1371 | ResTable_config::UI_MODE_NIGHT_NO;
1372 return true;
1373 }
1374
1375 return false;
1376}
1377
1378bool AaptGroupEntry::getDensityName(const char* name,
1379 ResTable_config* out)
1380{
1381 if (strcmp(name, kWildcardName) == 0) {
1382 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
1383 return true;
1384 }
1385
1386 if (strcmp(name, "nodpi") == 0) {
1387 if (out) out->density = ResTable_config::DENSITY_NONE;
1388 return true;
1389 }
1390
1391 if (strcmp(name, "ldpi") == 0) {
1392 if (out) out->density = ResTable_config::DENSITY_LOW;
1393 return true;
1394 }
1395
1396 if (strcmp(name, "mdpi") == 0) {
1397 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1398 return true;
1399 }
1400
1401 if (strcmp(name, "tvdpi") == 0) {
1402 if (out) out->density = ResTable_config::DENSITY_TV;
1403 return true;
1404 }
1405
1406 if (strcmp(name, "hdpi") == 0) {
1407 if (out) out->density = ResTable_config::DENSITY_HIGH;
1408 return true;
1409 }
1410
1411 if (strcmp(name, "xhdpi") == 0) {
1412 if (out) out->density = ResTable_config::DENSITY_XHIGH;
1413 return true;
1414 }
1415
1416 if (strcmp(name, "xxhdpi") == 0) {
1417 if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1418 return true;
1419 }
1420
1421 if (strcmp(name, "xxxhdpi") == 0) {
1422 if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
1423 return true;
1424 }
1425
1426 char* c = (char*)name;
1427 while (*c >= '0' && *c <= '9') {
1428 c++;
1429 }
1430
1431 // check that we have 'dpi' after the last digit.
1432 if (toupper(c[0]) != 'D' ||
1433 toupper(c[1]) != 'P' ||
1434 toupper(c[2]) != 'I' ||
1435 c[3] != 0) {
1436 return false;
1437 }
1438
1439 // temporarily replace the first letter with \0 to
1440 // use atoi.
1441 char tmp = c[0];
1442 c[0] = '\0';
1443
1444 int d = atoi(name);
1445 c[0] = tmp;
1446
1447 if (d != 0) {
1448 if (out) out->density = d;
1449 return true;
1450 }
1451
1452 return false;
1453}
1454
1455bool AaptGroupEntry::getTouchscreenName(const char* name,
1456 ResTable_config* out)
1457{
1458 if (strcmp(name, kWildcardName) == 0) {
1459 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1460 return true;
1461 } else if (strcmp(name, "notouch") == 0) {
1462 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1463 return true;
1464 } else if (strcmp(name, "stylus") == 0) {
1465 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1466 return true;
1467 } else if (strcmp(name, "finger") == 0) {
1468 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1469 return true;
1470 }
1471
1472 return false;
1473}
1474
1475bool AaptGroupEntry::getKeysHiddenName(const char* name,
1476 ResTable_config* out)
1477{
1478 uint8_t mask = 0;
1479 uint8_t value = 0;
1480 if (strcmp(name, kWildcardName) == 0) {
1481 mask = ResTable_config::MASK_KEYSHIDDEN;
1482 value = ResTable_config::KEYSHIDDEN_ANY;
1483 } else if (strcmp(name, "keysexposed") == 0) {
1484 mask = ResTable_config::MASK_KEYSHIDDEN;
1485 value = ResTable_config::KEYSHIDDEN_NO;
1486 } else if (strcmp(name, "keyshidden") == 0) {
1487 mask = ResTable_config::MASK_KEYSHIDDEN;
1488 value = ResTable_config::KEYSHIDDEN_YES;
1489 } else if (strcmp(name, "keyssoft") == 0) {
1490 mask = ResTable_config::MASK_KEYSHIDDEN;
1491 value = ResTable_config::KEYSHIDDEN_SOFT;
1492 }
1493
1494 if (mask != 0) {
1495 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1496 return true;
1497 }
1498
1499 return false;
1500}
1501
1502bool AaptGroupEntry::getKeyboardName(const char* name,
1503 ResTable_config* out)
1504{
1505 if (strcmp(name, kWildcardName) == 0) {
1506 if (out) out->keyboard = out->KEYBOARD_ANY;
1507 return true;
1508 } else if (strcmp(name, "nokeys") == 0) {
1509 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1510 return true;
1511 } else if (strcmp(name, "qwerty") == 0) {
1512 if (out) out->keyboard = out->KEYBOARD_QWERTY;
1513 return true;
1514 } else if (strcmp(name, "12key") == 0) {
1515 if (out) out->keyboard = out->KEYBOARD_12KEY;
1516 return true;
1517 }
1518
1519 return false;
1520}
1521
1522bool AaptGroupEntry::getNavHiddenName(const char* name,
1523 ResTable_config* out)
1524{
1525 uint8_t mask = 0;
1526 uint8_t value = 0;
1527 if (strcmp(name, kWildcardName) == 0) {
1528 mask = ResTable_config::MASK_NAVHIDDEN;
1529 value = ResTable_config::NAVHIDDEN_ANY;
1530 } else if (strcmp(name, "navexposed") == 0) {
1531 mask = ResTable_config::MASK_NAVHIDDEN;
1532 value = ResTable_config::NAVHIDDEN_NO;
1533 } else if (strcmp(name, "navhidden") == 0) {
1534 mask = ResTable_config::MASK_NAVHIDDEN;
1535 value = ResTable_config::NAVHIDDEN_YES;
1536 }
1537
1538 if (mask != 0) {
1539 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1540 return true;
1541 }
1542
1543 return false;
1544}
1545
1546bool AaptGroupEntry::getNavigationName(const char* name,
1547 ResTable_config* out)
1548{
1549 if (strcmp(name, kWildcardName) == 0) {
1550 if (out) out->navigation = out->NAVIGATION_ANY;
1551 return true;
1552 } else if (strcmp(name, "nonav") == 0) {
1553 if (out) out->navigation = out->NAVIGATION_NONAV;
1554 return true;
1555 } else if (strcmp(name, "dpad") == 0) {
1556 if (out) out->navigation = out->NAVIGATION_DPAD;
1557 return true;
1558 } else if (strcmp(name, "trackball") == 0) {
1559 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1560 return true;
1561 } else if (strcmp(name, "wheel") == 0) {
1562 if (out) out->navigation = out->NAVIGATION_WHEEL;
1563 return true;
1564 }
1565
1566 return false;
1567}
1568
1569bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
1570{
1571 if (strcmp(name, kWildcardName) == 0) {
1572 if (out) {
1573 out->screenWidth = out->SCREENWIDTH_ANY;
1574 out->screenHeight = out->SCREENHEIGHT_ANY;
1575 }
1576 return true;
1577 }
1578
1579 const char* x = name;
1580 while (*x >= '0' && *x <= '9') x++;
1581 if (x == name || *x != 'x') return false;
1582 String8 xName(name, x-name);
1583 x++;
1584
1585 const char* y = x;
1586 while (*y >= '0' && *y <= '9') y++;
1587 if (y == name || *y != 0) return false;
1588 String8 yName(x, y-x);
1589
1590 uint16_t w = (uint16_t)atoi(xName.string());
1591 uint16_t h = (uint16_t)atoi(yName.string());
1592 if (w < h) {
1593 return false;
1594 }
1595
1596 if (out) {
1597 out->screenWidth = w;
1598 out->screenHeight = h;
1599 }
1600
1601 return true;
1602}
1603
1604bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1605{
1606 if (strcmp(name, kWildcardName) == 0) {
1607 if (out) {
1608 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1609 }
1610 return true;
1611 }
1612
1613 if (*name != 's') return false;
1614 name++;
1615 if (*name != 'w') return false;
1616 name++;
1617 const char* x = name;
1618 while (*x >= '0' && *x <= '9') x++;
1619 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1620 String8 xName(name, x-name);
1621
1622 if (out) {
1623 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1624 }
1625
1626 return true;
1627}
1628
1629bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1630{
1631 if (strcmp(name, kWildcardName) == 0) {
1632 if (out) {
1633 out->screenWidthDp = out->SCREENWIDTH_ANY;
1634 }
1635 return true;
1636 }
1637
1638 if (*name != 'w') return false;
1639 name++;
1640 const char* x = name;
1641 while (*x >= '0' && *x <= '9') x++;
1642 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1643 String8 xName(name, x-name);
1644
1645 if (out) {
1646 out->screenWidthDp = (uint16_t)atoi(xName.string());
1647 }
1648
1649 return true;
1650}
1651
1652bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1653{
1654 if (strcmp(name, kWildcardName) == 0) {
1655 if (out) {
1656 out->screenHeightDp = out->SCREENWIDTH_ANY;
1657 }
1658 return true;
1659 }
1660
1661 if (*name != 'h') return false;
1662 name++;
1663 const char* x = name;
1664 while (*x >= '0' && *x <= '9') x++;
1665 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1666 String8 xName(name, x-name);
1667
1668 if (out) {
1669 out->screenHeightDp = (uint16_t)atoi(xName.string());
1670 }
1671
1672 return true;
1673}
1674
1675bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
1676{
1677 if (strcmp(name, kWildcardName) == 0) {
1678 if (out) {
1679 out->sdkVersion = out->SDKVERSION_ANY;
1680 out->minorVersion = out->MINORVERSION_ANY;
1681 }
1682 return true;
1683 }
1684
1685 if (*name != 'v') {
1686 return false;
1687 }
1688
1689 name++;
1690 const char* s = name;
1691 while (*s >= '0' && *s <= '9') s++;
1692 if (s == name || *s != 0) return false;
1693 String8 sdkName(name, s-name);
1694
1695 if (out) {
1696 out->sdkVersion = (uint16_t)atoi(sdkName.string());
1697 out->minorVersion = 0;
1698 }
1699
1700 return true;
1701}
1702
1703int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1704{
1705 int v = mcc.compare(o.mcc);
1706 if (v == 0) v = mnc.compare(o.mnc);
1707 if (v == 0) v = locale.compare(o.locale);
1708 if (v == 0) v = layoutDirection.compare(o.layoutDirection);
1709 if (v == 0) v = vendor.compare(o.vendor);
1710 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
1711 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1712 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
1713 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1714 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1715 if (v == 0) v = orientation.compare(o.orientation);
1716 if (v == 0) v = uiModeType.compare(o.uiModeType);
1717 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1718 if (v == 0) v = density.compare(o.density);
1719 if (v == 0) v = touchscreen.compare(o.touchscreen);
1720 if (v == 0) v = keysHidden.compare(o.keysHidden);
1721 if (v == 0) v = keyboard.compare(o.keyboard);
1722 if (v == 0) v = navHidden.compare(o.navHidden);
1723 if (v == 0) v = navigation.compare(o.navigation);
1724 if (v == 0) v = screenSize.compare(o.screenSize);
1725 if (v == 0) v = version.compare(o.version);
1726 return v;
1727}
1728
Narayan Kamath91447d82014-01-21 15:32:36 +00001729const ResTable_config AaptGroupEntry::toParams() const
Adam Lesinski282e1812014-01-23 18:17:42 -08001730{
1731 if (!mParamsChanged) {
1732 return mParams;
1733 }
1734
1735 mParamsChanged = false;
Narayan Kamath91447d82014-01-21 15:32:36 +00001736 ResTable_config& params = mParams;
1737 memset(&params, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -08001738 getMccName(mcc.string(), &params);
1739 getMncName(mnc.string(), &params);
Narayan Kamath91447d82014-01-21 15:32:36 +00001740 locale.writeTo(&params);
Adam Lesinski282e1812014-01-23 18:17:42 -08001741 getLayoutDirectionName(layoutDirection.string(), &params);
1742 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
1743 getScreenWidthDpName(screenWidthDp.string(), &params);
1744 getScreenHeightDpName(screenHeightDp.string(), &params);
1745 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1746 getScreenLayoutLongName(screenLayoutLong.string(), &params);
1747 getOrientationName(orientation.string(), &params);
1748 getUiModeTypeName(uiModeType.string(), &params);
1749 getUiModeNightName(uiModeNight.string(), &params);
1750 getDensityName(density.string(), &params);
1751 getTouchscreenName(touchscreen.string(), &params);
1752 getKeysHiddenName(keysHidden.string(), &params);
1753 getKeyboardName(keyboard.string(), &params);
1754 getNavHiddenName(navHidden.string(), &params);
1755 getNavigationName(navigation.string(), &params);
1756 getScreenSizeName(screenSize.string(), &params);
1757 getVersionName(version.string(), &params);
1758
1759 // Fix up version number based on specified parameters.
1760 int minSdk = 0;
1761 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1762 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
1763 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
1764 minSdk = SDK_HONEYCOMB_MR2;
1765 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1766 != ResTable_config::UI_MODE_TYPE_ANY
1767 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1768 != ResTable_config::UI_MODE_NIGHT_ANY) {
1769 minSdk = SDK_FROYO;
1770 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1771 != ResTable_config::SCREENSIZE_ANY
1772 || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1773 != ResTable_config::SCREENLONG_ANY
1774 || params.density != ResTable_config::DENSITY_DEFAULT) {
1775 minSdk = SDK_DONUT;
1776 }
1777
1778 if (minSdk > params.sdkVersion) {
1779 params.sdkVersion = minSdk;
1780 }
1781
1782 return params;
1783}
1784
1785// =========================================================================
1786// =========================================================================
1787// =========================================================================
1788
1789void* AaptFile::editData(size_t size)
1790{
1791 if (size <= mBufferSize) {
1792 mDataSize = size;
1793 return mData;
1794 }
1795 size_t allocSize = (size*3)/2;
1796 void* buf = realloc(mData, allocSize);
1797 if (buf == NULL) {
1798 return NULL;
1799 }
1800 mData = buf;
1801 mDataSize = size;
1802 mBufferSize = allocSize;
1803 return buf;
1804}
1805
Adam Lesinskide898ff2014-01-29 18:20:45 -08001806void* AaptFile::editDataInRange(size_t offset, size_t size)
1807{
1808 return (void*)(((uint8_t*) editData(offset + size)) + offset);
1809}
1810
Adam Lesinski282e1812014-01-23 18:17:42 -08001811void* AaptFile::editData(size_t* outSize)
1812{
1813 if (outSize) {
1814 *outSize = mDataSize;
1815 }
1816 return mData;
1817}
1818
1819void* AaptFile::padData(size_t wordSize)
1820{
1821 const size_t extra = mDataSize%wordSize;
1822 if (extra == 0) {
1823 return mData;
1824 }
1825
1826 size_t initial = mDataSize;
1827 void* data = editData(initial+(wordSize-extra));
1828 if (data != NULL) {
1829 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1830 }
1831 return data;
1832}
1833
1834status_t AaptFile::writeData(const void* data, size_t size)
1835{
1836 size_t end = mDataSize;
1837 size_t total = size + end;
1838 void* buf = editData(total);
1839 if (buf == NULL) {
1840 return UNKNOWN_ERROR;
1841 }
1842 memcpy(((char*)buf)+end, data, size);
1843 return NO_ERROR;
1844}
1845
1846void AaptFile::clearData()
1847{
1848 if (mData != NULL) free(mData);
1849 mData = NULL;
1850 mDataSize = 0;
1851 mBufferSize = 0;
1852}
1853
1854String8 AaptFile::getPrintableSource() const
1855{
1856 if (hasData()) {
1857 String8 name(mGroupEntry.toDirName(String8()));
1858 name.appendPath(mPath);
1859 name.append(" #generated");
1860 return name;
1861 }
1862 return mSourceFile;
1863}
1864
1865// =========================================================================
1866// =========================================================================
1867// =========================================================================
1868
Adam Lesinski09384302014-01-22 16:07:42 -08001869status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
Adam Lesinski282e1812014-01-23 18:17:42 -08001870{
Adam Lesinski09384302014-01-22 16:07:42 -08001871 ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
1872 if (index >= 0 && overwriteDuplicate) {
1873 fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
1874 mFiles[index]->getSourceFile().string(),
1875 file->getSourceFile().string());
1876 removeFile(index);
1877 index = -1;
1878 }
1879
1880 if (index < 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001881 file->mPath = mPath;
1882 mFiles.add(file->getGroupEntry(), file);
1883 return NO_ERROR;
1884 }
1885
1886#if 0
1887 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1888 file->getSourceFile().string(),
1889 file->getGroupEntry().toDirName(String8()).string(),
1890 mLeaf.string(), mPath.string());
1891#endif
1892
1893 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1894 getPrintableSource().string());
1895 return UNKNOWN_ERROR;
1896}
1897
1898void AaptGroup::removeFile(size_t index)
1899{
1900 mFiles.removeItemsAt(index);
1901}
1902
1903void AaptGroup::print(const String8& prefix) const
1904{
1905 printf("%s%s\n", prefix.string(), getPath().string());
1906 const size_t N=mFiles.size();
1907 size_t i;
1908 for (i=0; i<N; i++) {
1909 sp<AaptFile> file = mFiles.valueAt(i);
1910 const AaptGroupEntry& e = file->getGroupEntry();
1911 if (file->hasData()) {
1912 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
1913 (int)file->getSize());
1914 } else {
1915 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1916 file->getPrintableSource().string());
1917 }
1918 //printf("%s File Group Entry: %s\n", prefix.string(),
1919 // file->getGroupEntry().toDirName(String8()).string());
1920 }
1921}
1922
1923String8 AaptGroup::getPrintableSource() const
1924{
1925 if (mFiles.size() > 0) {
1926 // Arbitrarily pull the first source file out of the list.
1927 return mFiles.valueAt(0)->getPrintableSource();
1928 }
1929
1930 // Should never hit this case, but to be safe...
1931 return getPath();
1932
1933}
1934
1935// =========================================================================
1936// =========================================================================
1937// =========================================================================
1938
1939status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1940{
1941 if (mFiles.indexOfKey(name) >= 0) {
1942 return ALREADY_EXISTS;
1943 }
1944 mFiles.add(name, file);
1945 return NO_ERROR;
1946}
1947
1948status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1949{
1950 if (mDirs.indexOfKey(name) >= 0) {
1951 return ALREADY_EXISTS;
1952 }
1953 mDirs.add(name, dir);
1954 return NO_ERROR;
1955}
1956
1957sp<AaptDir> AaptDir::makeDir(const String8& path)
1958{
1959 String8 name;
1960 String8 remain = path;
1961
1962 sp<AaptDir> subdir = this;
1963 while (name = remain.walkPath(&remain), remain != "") {
1964 subdir = subdir->makeDir(name);
1965 }
1966
1967 ssize_t i = subdir->mDirs.indexOfKey(name);
1968 if (i >= 0) {
1969 return subdir->mDirs.valueAt(i);
1970 }
1971 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1972 subdir->mDirs.add(name, dir);
1973 return dir;
1974}
1975
1976void AaptDir::removeFile(const String8& name)
1977{
1978 mFiles.removeItem(name);
1979}
1980
1981void AaptDir::removeDir(const String8& name)
1982{
1983 mDirs.removeItem(name);
1984}
1985
Adam Lesinski09384302014-01-22 16:07:42 -08001986status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
1987 const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -08001988{
1989 sp<AaptGroup> group;
1990 if (mFiles.indexOfKey(leafName) >= 0) {
1991 group = mFiles.valueFor(leafName);
1992 } else {
1993 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1994 mFiles.add(leafName, group);
1995 }
1996
Adam Lesinski09384302014-01-22 16:07:42 -08001997 return group->addFile(file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -08001998}
1999
2000ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
2001 const AaptGroupEntry& kind, const String8& resType,
Adam Lesinski09384302014-01-22 16:07:42 -08002002 sp<FilePathStore>& fullResPaths, const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -08002003{
2004 Vector<String8> fileNames;
2005 {
2006 DIR* dir = NULL;
2007
2008 dir = opendir(srcDir.string());
2009 if (dir == NULL) {
2010 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2011 return UNKNOWN_ERROR;
2012 }
2013
2014 /*
2015 * Slurp the filenames out of the directory.
2016 */
2017 while (1) {
2018 struct dirent* entry;
2019
2020 entry = readdir(dir);
2021 if (entry == NULL)
2022 break;
2023
2024 if (isHidden(srcDir.string(), entry->d_name))
2025 continue;
2026
2027 String8 name(entry->d_name);
2028 fileNames.add(name);
2029 // Add fully qualified path for dependency purposes
2030 // if we're collecting them
2031 if (fullResPaths != NULL) {
2032 fullResPaths->add(srcDir.appendPathCopy(name));
2033 }
2034 }
2035 closedir(dir);
2036 }
2037
2038 ssize_t count = 0;
2039
2040 /*
2041 * Stash away the files and recursively descend into subdirectories.
2042 */
2043 const size_t N = fileNames.size();
2044 size_t i;
2045 for (i = 0; i < N; i++) {
2046 String8 pathName(srcDir);
2047 FileType type;
2048
2049 pathName.appendPath(fileNames[i].string());
2050 type = getFileType(pathName.string());
2051 if (type == kFileTypeDirectory) {
2052 sp<AaptDir> subdir;
2053 bool notAdded = false;
2054 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
2055 subdir = mDirs.valueFor(fileNames[i]);
2056 } else {
2057 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
2058 notAdded = true;
2059 }
2060 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Adam Lesinski09384302014-01-22 16:07:42 -08002061 resType, fullResPaths, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -08002062 if (res < NO_ERROR) {
2063 return res;
2064 }
2065 if (res > 0 && notAdded) {
2066 mDirs.add(fileNames[i], subdir);
2067 }
2068 count += res;
2069 } else if (type == kFileTypeRegular) {
2070 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
Adam Lesinski09384302014-01-22 16:07:42 -08002071 status_t err = addLeafFile(fileNames[i], file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -08002072 if (err != NO_ERROR) {
2073 return err;
2074 }
2075
2076 count++;
2077
2078 } else {
2079 if (bundle->getVerbose())
2080 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
2081 }
2082 }
2083
2084 return count;
2085}
2086
2087status_t AaptDir::validate() const
2088{
2089 const size_t NF = mFiles.size();
2090 const size_t ND = mDirs.size();
2091 size_t i;
2092 for (i = 0; i < NF; i++) {
2093 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
2094 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2095 "Invalid filename. Unable to add.");
2096 return UNKNOWN_ERROR;
2097 }
2098
2099 size_t j;
2100 for (j = i+1; j < NF; j++) {
2101 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2102 mFiles.valueAt(j)->getLeaf().string()) == 0) {
2103 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2104 "File is case-insensitive equivalent to: %s",
2105 mFiles.valueAt(j)->getPrintableSource().string());
2106 return UNKNOWN_ERROR;
2107 }
2108
2109 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
2110 // (this is mostly caught by the "marked" stuff, below)
2111 }
2112
2113 for (j = 0; j < ND; j++) {
2114 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2115 mDirs.valueAt(j)->getLeaf().string()) == 0) {
2116 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2117 "File conflicts with dir from: %s",
2118 mDirs.valueAt(j)->getPrintableSource().string());
2119 return UNKNOWN_ERROR;
2120 }
2121 }
2122 }
2123
2124 for (i = 0; i < ND; i++) {
2125 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
2126 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2127 "Invalid directory name, unable to add.");
2128 return UNKNOWN_ERROR;
2129 }
2130
2131 size_t j;
2132 for (j = i+1; j < ND; j++) {
2133 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
2134 mDirs.valueAt(j)->getLeaf().string()) == 0) {
2135 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2136 "Directory is case-insensitive equivalent to: %s",
2137 mDirs.valueAt(j)->getPrintableSource().string());
2138 return UNKNOWN_ERROR;
2139 }
2140 }
2141
2142 status_t err = mDirs.valueAt(i)->validate();
2143 if (err != NO_ERROR) {
2144 return err;
2145 }
2146 }
2147
2148 return NO_ERROR;
2149}
2150
2151void AaptDir::print(const String8& prefix) const
2152{
2153 const size_t ND=getDirs().size();
2154 size_t i;
2155 for (i=0; i<ND; i++) {
2156 getDirs().valueAt(i)->print(prefix);
2157 }
2158
2159 const size_t NF=getFiles().size();
2160 for (i=0; i<NF; i++) {
2161 getFiles().valueAt(i)->print(prefix);
2162 }
2163}
2164
2165String8 AaptDir::getPrintableSource() const
2166{
2167 if (mFiles.size() > 0) {
2168 // Arbitrarily pull the first file out of the list as the source dir.
2169 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
2170 }
2171 if (mDirs.size() > 0) {
2172 // Or arbitrarily pull the first dir out of the list as the source dir.
2173 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
2174 }
2175
2176 // Should never hit this case, but to be safe...
2177 return mPath;
2178
2179}
2180
2181// =========================================================================
2182// =========================================================================
2183// =========================================================================
2184
2185status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
2186{
2187 status_t err = NO_ERROR;
2188 size_t N = javaSymbols->mSymbols.size();
2189 for (size_t i=0; i<N; i++) {
2190 const String8& name = javaSymbols->mSymbols.keyAt(i);
2191 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
2192 ssize_t pos = mSymbols.indexOfKey(name);
2193 if (pos < 0) {
2194 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
2195 err = UNKNOWN_ERROR;
2196 continue;
2197 }
2198 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
2199 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
2200 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
2201 }
2202
2203 N = javaSymbols->mNestedSymbols.size();
2204 for (size_t i=0; i<N; i++) {
2205 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
2206 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
2207 ssize_t pos = mNestedSymbols.indexOfKey(name);
2208 if (pos < 0) {
2209 SourcePos pos;
2210 pos.error("Java symbol dir %s not defined\n", name.string());
2211 err = UNKNOWN_ERROR;
2212 continue;
2213 }
2214 //printf("**** applying java symbols in dir %s\n", name.string());
2215 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2216 if (myerr != NO_ERROR) {
2217 err = myerr;
2218 }
2219 }
2220
2221 return err;
2222}
2223
2224// =========================================================================
2225// =========================================================================
2226// =========================================================================
2227
2228AaptAssets::AaptAssets()
2229 : AaptDir(String8(), String8()),
Narayan Kamath91447d82014-01-21 15:32:36 +00002230 mHavePrivateSymbols(false),
2231 mChanged(false), mHaveIncludedAssets(false),
2232 mRes(NULL)
Adam Lesinski282e1812014-01-23 18:17:42 -08002233{
2234}
2235
2236const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
2237 if (mChanged) {
2238 }
2239 return mGroupEntries;
2240}
2241
2242status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
2243{
2244 mChanged = true;
2245 return AaptDir::addFile(name, file);
2246}
2247
2248sp<AaptFile> AaptAssets::addFile(
2249 const String8& filePath, const AaptGroupEntry& entry,
2250 const String8& srcDir, sp<AaptGroup>* outGroup,
2251 const String8& resType)
2252{
2253 sp<AaptDir> dir = this;
2254 sp<AaptGroup> group;
2255 sp<AaptFile> file;
2256 String8 root, remain(filePath), partialPath;
2257 while (remain.length() > 0) {
2258 root = remain.walkPath(&remain);
2259 partialPath.appendPath(root);
2260
2261 const String8 rootStr(root);
2262
2263 if (remain.length() == 0) {
2264 ssize_t i = dir->getFiles().indexOfKey(rootStr);
2265 if (i >= 0) {
2266 group = dir->getFiles().valueAt(i);
2267 } else {
2268 group = new AaptGroup(rootStr, filePath);
2269 status_t res = dir->addFile(rootStr, group);
2270 if (res != NO_ERROR) {
2271 return NULL;
2272 }
2273 }
2274 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2275 status_t res = group->addFile(file);
2276 if (res != NO_ERROR) {
2277 return NULL;
2278 }
2279 break;
2280
2281 } else {
2282 ssize_t i = dir->getDirs().indexOfKey(rootStr);
2283 if (i >= 0) {
2284 dir = dir->getDirs().valueAt(i);
2285 } else {
2286 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2287 status_t res = dir->addDir(rootStr, subdir);
2288 if (res != NO_ERROR) {
2289 return NULL;
2290 }
2291 dir = subdir;
2292 }
2293 }
2294 }
2295
2296 mGroupEntries.add(entry);
2297 if (outGroup) *outGroup = group;
2298 return file;
2299}
2300
2301void AaptAssets::addResource(const String8& leafName, const String8& path,
2302 const sp<AaptFile>& file, const String8& resType)
2303{
2304 sp<AaptDir> res = AaptDir::makeDir(kResString);
2305 String8 dirname = file->getGroupEntry().toDirName(resType);
2306 sp<AaptDir> subdir = res->makeDir(dirname);
2307 sp<AaptGroup> grr = new AaptGroup(leafName, path);
2308 grr->addFile(file);
2309
2310 subdir->addFile(leafName, grr);
2311}
2312
2313
2314ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2315{
2316 int count;
2317 int totalCount = 0;
2318 FileType type;
2319 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2320 const size_t dirCount =resDirs.size();
2321 sp<AaptAssets> current = this;
2322
2323 const int N = bundle->getFileSpecCount();
2324
2325 /*
2326 * If a package manifest was specified, include that first.
2327 */
2328 if (bundle->getAndroidManifestFile() != NULL) {
2329 // place at root of zip.
2330 String8 srcFile(bundle->getAndroidManifestFile());
2331 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2332 NULL, String8());
2333 totalCount++;
2334 }
2335
2336 /*
2337 * If a directory of custom assets was supplied, slurp 'em up.
2338 */
Adam Lesinski09384302014-01-22 16:07:42 -08002339 const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
2340 const int AN = assetDirs.size();
2341 for (int i = 0; i < AN; i++) {
2342 FileType type = getFileType(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08002343 if (type == kFileTypeNonexistent) {
Adam Lesinski09384302014-01-22 16:07:42 -08002344 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08002345 return UNKNOWN_ERROR;
2346 }
2347 if (type != kFileTypeDirectory) {
Adam Lesinski09384302014-01-22 16:07:42 -08002348 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08002349 return UNKNOWN_ERROR;
2350 }
2351
Adam Lesinski09384302014-01-22 16:07:42 -08002352 String8 assetRoot(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08002353 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2354 AaptGroupEntry group;
2355 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Adam Lesinski09384302014-01-22 16:07:42 -08002356 String8(), mFullAssetPaths, true);
Adam Lesinski282e1812014-01-23 18:17:42 -08002357 if (count < 0) {
2358 totalCount = count;
2359 goto bail;
2360 }
2361 if (count > 0) {
2362 mGroupEntries.add(group);
2363 }
2364 totalCount += count;
2365
Adam Lesinski09384302014-01-22 16:07:42 -08002366 if (bundle->getVerbose()) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002367 printf("Found %d custom asset file%s in %s\n",
Adam Lesinski09384302014-01-22 16:07:42 -08002368 count, (count==1) ? "" : "s", assetDirs[i]);
2369 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002370 }
2371
2372 /*
2373 * If a directory of resource-specific assets was supplied, slurp 'em up.
2374 */
2375 for (size_t i=0; i<dirCount; i++) {
2376 const char *res = resDirs[i];
2377 if (res) {
2378 type = getFileType(res);
2379 if (type == kFileTypeNonexistent) {
2380 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2381 return UNKNOWN_ERROR;
2382 }
2383 if (type == kFileTypeDirectory) {
2384 if (i>0) {
2385 sp<AaptAssets> nextOverlay = new AaptAssets();
2386 current->setOverlay(nextOverlay);
2387 current = nextOverlay;
2388 current->setFullResPaths(mFullResPaths);
2389 }
2390 count = current->slurpResourceTree(bundle, String8(res));
Bryan Mawhinney9ab9b932014-01-24 16:18:13 +00002391 if (i > 0 && count > 0) {
2392 count = current->filter(bundle);
2393 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002394
2395 if (count < 0) {
2396 totalCount = count;
2397 goto bail;
2398 }
2399 totalCount += count;
2400 }
2401 else {
2402 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2403 return UNKNOWN_ERROR;
2404 }
2405 }
2406
2407 }
2408 /*
2409 * Now do any additional raw files.
2410 */
2411 for (int arg=0; arg<N; arg++) {
2412 const char* assetDir = bundle->getFileSpecEntry(arg);
2413
2414 FileType type = getFileType(assetDir);
2415 if (type == kFileTypeNonexistent) {
2416 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2417 return UNKNOWN_ERROR;
2418 }
2419 if (type != kFileTypeDirectory) {
2420 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2421 return UNKNOWN_ERROR;
2422 }
2423
2424 String8 assetRoot(assetDir);
2425
2426 if (bundle->getVerbose())
2427 printf("Processing raw dir '%s'\n", (const char*) assetDir);
2428
2429 /*
2430 * Do a recursive traversal of subdir tree. We don't make any
2431 * guarantees about ordering, so we're okay with an inorder search
2432 * using whatever order the OS happens to hand back to us.
2433 */
2434 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2435 if (count < 0) {
2436 /* failure; report error and remove archive */
2437 totalCount = count;
2438 goto bail;
2439 }
2440 totalCount += count;
2441
2442 if (bundle->getVerbose())
2443 printf("Found %d asset file%s in %s\n",
2444 count, (count==1) ? "" : "s", assetDir);
2445 }
2446
2447 count = validate();
2448 if (count != NO_ERROR) {
2449 totalCount = count;
2450 goto bail;
2451 }
2452
2453 count = filter(bundle);
2454 if (count != NO_ERROR) {
2455 totalCount = count;
2456 goto bail;
2457 }
2458
2459bail:
2460 return totalCount;
2461}
2462
2463ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2464 const AaptGroupEntry& kind,
2465 const String8& resType,
2466 sp<FilePathStore>& fullResPaths)
2467{
2468 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2469 if (res > 0) {
2470 mGroupEntries.add(kind);
2471 }
2472
2473 return res;
2474}
2475
2476ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2477{
2478 ssize_t err = 0;
2479
2480 DIR* dir = opendir(srcDir.string());
2481 if (dir == NULL) {
2482 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2483 return UNKNOWN_ERROR;
2484 }
2485
2486 status_t count = 0;
2487
2488 /*
2489 * Run through the directory, looking for dirs that match the
2490 * expected pattern.
2491 */
2492 while (1) {
2493 struct dirent* entry = readdir(dir);
2494 if (entry == NULL) {
2495 break;
2496 }
2497
2498 if (isHidden(srcDir.string(), entry->d_name)) {
2499 continue;
2500 }
2501
2502 String8 subdirName(srcDir);
2503 subdirName.appendPath(entry->d_name);
2504
2505 AaptGroupEntry group;
2506 String8 resType;
2507 bool b = group.initFromDirName(entry->d_name, &resType);
2508 if (!b) {
2509 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2510 entry->d_name);
2511 err = -1;
2512 continue;
2513 }
2514
2515 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2516 int maxResInt = atoi(bundle->getMaxResVersion());
2517 const char *verString = group.getVersionString().string();
2518 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2519 if (dirVersionInt > maxResInt) {
2520 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2521 continue;
2522 }
2523 }
2524
2525 FileType type = getFileType(subdirName.string());
2526
2527 if (type == kFileTypeDirectory) {
2528 sp<AaptDir> dir = makeDir(resType);
2529 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2530 resType, mFullResPaths);
2531 if (res < 0) {
2532 count = res;
2533 goto bail;
2534 }
2535 if (res > 0) {
2536 mGroupEntries.add(group);
2537 count += res;
2538 }
2539
2540 // Only add this directory if we don't already have a resource dir
2541 // for the current type. This ensures that we only add the dir once
2542 // for all configs.
2543 sp<AaptDir> rdir = resDir(resType);
2544 if (rdir == NULL) {
2545 mResDirs.add(dir);
2546 }
2547 } else {
2548 if (bundle->getVerbose()) {
2549 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
2550 }
2551 }
2552 }
2553
2554bail:
2555 closedir(dir);
2556 dir = NULL;
2557
2558 if (err != 0) {
2559 return err;
2560 }
2561 return count;
2562}
2563
2564ssize_t
2565AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2566{
2567 int count = 0;
2568 SortedVector<AaptGroupEntry> entries;
2569
2570 ZipFile* zip = new ZipFile;
2571 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2572 if (err != NO_ERROR) {
2573 fprintf(stderr, "error opening zip file %s\n", filename);
2574 count = err;
2575 delete zip;
2576 return -1;
2577 }
2578
2579 const int N = zip->getNumEntries();
2580 for (int i=0; i<N; i++) {
2581 ZipEntry* entry = zip->getEntryByIndex(i);
2582 if (entry->getDeleted()) {
2583 continue;
2584 }
2585
2586 String8 entryName(entry->getFileName());
2587
2588 String8 dirName = entryName.getPathDir();
2589 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2590
2591 String8 resType;
2592 AaptGroupEntry kind;
2593
2594 String8 remain;
2595 if (entryName.walkPath(&remain) == kResourceDir) {
2596 // these are the resources, pull their type out of the directory name
2597 kind.initFromDirName(remain.walkPath().string(), &resType);
2598 } else {
2599 // these are untyped and don't have an AaptGroupEntry
2600 }
2601 if (entries.indexOf(kind) < 0) {
2602 entries.add(kind);
2603 mGroupEntries.add(kind);
2604 }
2605
2606 // use the one from the zip file if they both exist.
2607 dir->removeFile(entryName.getPathLeaf());
2608
2609 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2610 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2611 if (err != NO_ERROR) {
2612 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2613 count = err;
2614 goto bail;
2615 }
2616 file->setCompressionMethod(entry->getCompressionMethod());
2617
2618#if 0
2619 if (entryName == "AndroidManifest.xml") {
2620 printf("AndroidManifest.xml\n");
2621 }
2622 printf("\n\nfile: %s\n", entryName.string());
2623#endif
2624
2625 size_t len = entry->getUncompressedLen();
2626 void* data = zip->uncompress(entry);
2627 void* buf = file->editData(len);
2628 memcpy(buf, data, len);
2629
2630#if 0
2631 const int OFF = 0;
2632 const unsigned char* p = (unsigned char*)data;
2633 const unsigned char* end = p+len;
2634 p += OFF;
2635 for (int i=0; i<32 && p < end; i++) {
2636 printf("0x%03x ", i*0x10 + OFF);
2637 for (int j=0; j<0x10 && p < end; j++) {
2638 printf(" %02x", *p);
2639 p++;
2640 }
2641 printf("\n");
2642 }
2643#endif
2644
2645 free(data);
2646
2647 count++;
2648 }
2649
2650bail:
2651 delete zip;
2652 return count;
2653}
2654
2655status_t AaptAssets::filter(Bundle* bundle)
2656{
2657 ResourceFilter reqFilter;
2658 status_t err = reqFilter.parse(bundle->getConfigurations());
2659 if (err != NO_ERROR) {
2660 return err;
2661 }
2662
2663 ResourceFilter prefFilter;
2664 err = prefFilter.parse(bundle->getPreferredConfigurations());
2665 if (err != NO_ERROR) {
2666 return err;
2667 }
2668
2669 if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2670 return NO_ERROR;
2671 }
2672
2673 if (bundle->getVerbose()) {
2674 if (!reqFilter.isEmpty()) {
2675 printf("Applying required filter: %s\n",
2676 bundle->getConfigurations());
2677 }
2678 if (!prefFilter.isEmpty()) {
2679 printf("Applying preferred filter: %s\n",
2680 bundle->getPreferredConfigurations());
2681 }
2682 }
2683
2684 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2685 const size_t ND = resdirs.size();
2686 for (size_t i=0; i<ND; i++) {
2687 const sp<AaptDir>& dir = resdirs.itemAt(i);
2688 if (dir->getLeaf() == kValuesDir) {
2689 // The "value" dir is special since a single file defines
2690 // multiple resources, so we can not do filtering on the
2691 // files themselves.
2692 continue;
2693 }
2694 if (dir->getLeaf() == kMipmapDir) {
2695 // We also skip the "mipmap" directory, since the point of this
2696 // is to include all densities without stripping. If you put
2697 // other configurations in here as well they won't be stripped
2698 // either... So don't do that. Seriously. What is wrong with you?
2699 continue;
2700 }
2701
2702 const size_t NG = dir->getFiles().size();
2703 for (size_t j=0; j<NG; j++) {
2704 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2705
2706 // First remove any configurations we know we don't need.
2707 for (size_t k=0; k<grp->getFiles().size(); k++) {
2708 sp<AaptFile> file = grp->getFiles().valueAt(k);
2709 if (k == 0 && grp->getFiles().size() == 1) {
2710 // If this is the only file left, we need to keep it.
2711 // Otherwise the resource IDs we are using will be inconsistent
2712 // with what we get when not stripping. Sucky, but at least
2713 // for now we can rely on the back-end doing another filtering
2714 // pass to take this out and leave us with this resource name
2715 // containing no entries.
2716 continue;
2717 }
2718 if (file->getPath().getPathExtension() == ".xml") {
2719 // We can't remove .xml files at this point, because when
2720 // we parse them they may add identifier resources, so
2721 // removing them can cause our resource identifiers to
2722 // become inconsistent.
2723 continue;
2724 }
2725 const ResTable_config& config(file->getGroupEntry().toParams());
2726 if (!reqFilter.match(config)) {
2727 if (bundle->getVerbose()) {
2728 printf("Pruning unneeded resource: %s\n",
2729 file->getPrintableSource().string());
2730 }
2731 grp->removeFile(k);
2732 k--;
2733 }
2734 }
2735
2736 // Quick check: no preferred filters, nothing more to do.
2737 if (prefFilter.isEmpty()) {
2738 continue;
2739 }
2740
Adam Lesinski8cf61842013-10-18 13:42:09 -07002741 // Get the preferred density if there is one. We do not match exactly for density.
2742 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2743 // pick xhdpi.
2744 uint32_t preferredDensity = 0;
Narayan Kamath91447d82014-01-21 15:32:36 +00002745 const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
Adam Lesinski8cf61842013-10-18 13:42:09 -07002746 if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
Narayan Kamath91447d82014-01-21 15:32:36 +00002747 preferredDensity = (*preferredConfigs)[0].intValue;
Adam Lesinski8cf61842013-10-18 13:42:09 -07002748 }
2749
Adam Lesinski282e1812014-01-23 18:17:42 -08002750 // Now deal with preferred configurations.
2751 for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2752 for (size_t k=0; k<grp->getFiles().size(); k++) {
2753 sp<AaptFile> file = grp->getFiles().valueAt(k);
2754 if (k == 0 && grp->getFiles().size() == 1) {
2755 // If this is the only file left, we need to keep it.
2756 // Otherwise the resource IDs we are using will be inconsistent
2757 // with what we get when not stripping. Sucky, but at least
2758 // for now we can rely on the back-end doing another filtering
2759 // pass to take this out and leave us with this resource name
2760 // containing no entries.
2761 continue;
2762 }
2763 if (file->getPath().getPathExtension() == ".xml") {
2764 // We can't remove .xml files at this point, because when
2765 // we parse them they may add identifier resources, so
2766 // removing them can cause our resource identifiers to
2767 // become inconsistent.
2768 continue;
2769 }
2770 const ResTable_config& config(file->getGroupEntry().toParams());
2771 if (!prefFilter.match(axis, config)) {
2772 // This is a resource we would prefer not to have. Check
2773 // to see if have a similar variation that we would like
2774 // to have and, if so, we can drop it.
Adam Lesinski8cf61842013-10-18 13:42:09 -07002775
2776 uint32_t bestDensity = config.density;
2777
Adam Lesinski282e1812014-01-23 18:17:42 -08002778 for (size_t m=0; m<grp->getFiles().size(); m++) {
2779 if (m == k) continue;
2780 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2781 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2782 if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
Adam Lesinski8cf61842013-10-18 13:42:09 -07002783 if (axis == AXIS_DENSITY && preferredDensity > 0) {
2784 // See if there is a better density resource
2785 if (mconfig.density < bestDensity &&
2786 mconfig.density > preferredDensity &&
2787 bestDensity > preferredDensity) {
2788 // This density is between our best density and
2789 // the preferred density, therefore it is better.
2790 bestDensity = mconfig.density;
2791 } else if (mconfig.density > bestDensity &&
2792 bestDensity < preferredDensity) {
2793 // This density is better than our best density and
2794 // our best density was smaller than our preferred
2795 // density, so it is better.
2796 bestDensity = mconfig.density;
2797 }
2798 } else if (prefFilter.match(axis, mconfig)) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002799 if (bundle->getVerbose()) {
2800 printf("Pruning unneeded resource: %s\n",
2801 file->getPrintableSource().string());
2802 }
2803 grp->removeFile(k);
2804 k--;
2805 break;
2806 }
2807 }
2808 }
Adam Lesinski8cf61842013-10-18 13:42:09 -07002809
2810 if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2811 bestDensity != config.density) {
2812 if (bundle->getVerbose()) {
2813 printf("Pruning unneeded resource: %s\n",
2814 file->getPrintableSource().string());
2815 }
2816 grp->removeFile(k);
2817 k--;
2818 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002819 }
2820 }
2821 }
2822 }
2823 }
2824
2825 return NO_ERROR;
2826}
2827
2828sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2829{
2830 sp<AaptSymbols> sym = mSymbols.valueFor(name);
2831 if (sym == NULL) {
2832 sym = new AaptSymbols();
2833 mSymbols.add(name, sym);
2834 }
2835 return sym;
2836}
2837
2838sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2839{
2840 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2841 if (sym == NULL) {
2842 sym = new AaptSymbols();
2843 mJavaSymbols.add(name, sym);
2844 }
2845 return sym;
2846}
2847
2848status_t AaptAssets::applyJavaSymbols()
2849{
2850 size_t N = mJavaSymbols.size();
2851 for (size_t i=0; i<N; i++) {
2852 const String8& name = mJavaSymbols.keyAt(i);
2853 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2854 ssize_t pos = mSymbols.indexOfKey(name);
2855 if (pos < 0) {
2856 SourcePos pos;
2857 pos.error("Java symbol dir %s not defined\n", name.string());
2858 return UNKNOWN_ERROR;
2859 }
2860 //printf("**** applying java symbols in dir %s\n", name.string());
2861 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2862 if (err != NO_ERROR) {
2863 return err;
2864 }
2865 }
2866
2867 return NO_ERROR;
2868}
2869
2870bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2871 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2872 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2873 // sym.isJavaSymbol ? 1 : 0);
2874 if (!mHavePrivateSymbols) return true;
2875 if (sym.isPublic) return true;
2876 if (includePrivate && sym.isJavaSymbol) return true;
2877 return false;
2878}
2879
2880status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2881{
2882 if (!mHaveIncludedAssets) {
2883 // Add in all includes.
2884 const Vector<const char*>& incl = bundle->getPackageIncludes();
2885 const size_t N=incl.size();
2886 for (size_t i=0; i<N; i++) {
2887 if (bundle->getVerbose())
2888 printf("Including resources from package: %s\n", incl[i]);
2889 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2890 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2891 incl[i]);
2892 return UNKNOWN_ERROR;
2893 }
2894 }
2895 mHaveIncludedAssets = true;
2896 }
2897
2898 return NO_ERROR;
2899}
2900
2901status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2902{
2903 const ResTable& res = getIncludedResources();
2904 // XXX dirty!
Narayan Kamath00b31442014-01-27 17:32:37 +00002905 return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
Adam Lesinski282e1812014-01-23 18:17:42 -08002906}
2907
2908const ResTable& AaptAssets::getIncludedResources() const
2909{
2910 return mIncludedAssets.getResources(false);
2911}
2912
2913void AaptAssets::print(const String8& prefix) const
2914{
2915 String8 innerPrefix(prefix);
2916 innerPrefix.append(" ");
2917 String8 innerInnerPrefix(innerPrefix);
2918 innerInnerPrefix.append(" ");
2919 printf("%sConfigurations:\n", prefix.string());
2920 const size_t N=mGroupEntries.size();
2921 for (size_t i=0; i<N; i++) {
2922 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2923 printf("%s %s\n", prefix.string(),
2924 cname != "" ? cname.string() : "(default)");
2925 }
2926
2927 printf("\n%sFiles:\n", prefix.string());
2928 AaptDir::print(innerPrefix);
2929
2930 printf("\n%sResource Dirs:\n", prefix.string());
2931 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2932 const size_t NR = resdirs.size();
2933 for (size_t i=0; i<NR; i++) {
2934 const sp<AaptDir>& d = resdirs.itemAt(i);
2935 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
2936 d->print(innerInnerPrefix);
2937 }
2938}
2939
2940sp<AaptDir> AaptAssets::resDir(const String8& name) const
2941{
2942 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2943 const size_t N = resdirs.size();
2944 for (size_t i=0; i<N; i++) {
2945 const sp<AaptDir>& d = resdirs.itemAt(i);
2946 if (d->getLeaf() == name) {
2947 return d;
2948 }
2949 }
2950 return NULL;
2951}
2952
2953bool
2954valid_symbol_name(const String8& symbol)
2955{
2956 static char const * const KEYWORDS[] = {
2957 "abstract", "assert", "boolean", "break",
2958 "byte", "case", "catch", "char", "class", "const", "continue",
2959 "default", "do", "double", "else", "enum", "extends", "final",
2960 "finally", "float", "for", "goto", "if", "implements", "import",
2961 "instanceof", "int", "interface", "long", "native", "new", "package",
2962 "private", "protected", "public", "return", "short", "static",
2963 "strictfp", "super", "switch", "synchronized", "this", "throw",
2964 "throws", "transient", "try", "void", "volatile", "while",
2965 "true", "false", "null",
2966 NULL
2967 };
2968 const char*const* k = KEYWORDS;
2969 const char*const s = symbol.string();
2970 while (*k) {
2971 if (0 == strcmp(s, *k)) {
2972 return false;
2973 }
2974 k++;
2975 }
2976 return true;
2977}