blob: 38bfa00d7564e3d0ecbab9696d2f0303d414d006 [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;
1345 }
1346
1347 return false;
1348}
1349
1350bool AaptGroupEntry::getUiModeNightName(const char* name,
1351 ResTable_config* out)
1352{
1353 if (strcmp(name, kWildcardName) == 0) {
1354 if (out) out->uiMode =
1355 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1356 | ResTable_config::UI_MODE_NIGHT_ANY;
1357 return true;
1358 } else if (strcmp(name, "night") == 0) {
1359 if (out) out->uiMode =
1360 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1361 | ResTable_config::UI_MODE_NIGHT_YES;
1362 return true;
1363 } else if (strcmp(name, "notnight") == 0) {
1364 if (out) out->uiMode =
1365 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1366 | ResTable_config::UI_MODE_NIGHT_NO;
1367 return true;
1368 }
1369
1370 return false;
1371}
1372
1373bool AaptGroupEntry::getDensityName(const char* name,
1374 ResTable_config* out)
1375{
1376 if (strcmp(name, kWildcardName) == 0) {
1377 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
1378 return true;
1379 }
1380
1381 if (strcmp(name, "nodpi") == 0) {
1382 if (out) out->density = ResTable_config::DENSITY_NONE;
1383 return true;
1384 }
1385
1386 if (strcmp(name, "ldpi") == 0) {
1387 if (out) out->density = ResTable_config::DENSITY_LOW;
1388 return true;
1389 }
1390
1391 if (strcmp(name, "mdpi") == 0) {
1392 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1393 return true;
1394 }
1395
1396 if (strcmp(name, "tvdpi") == 0) {
1397 if (out) out->density = ResTable_config::DENSITY_TV;
1398 return true;
1399 }
1400
1401 if (strcmp(name, "hdpi") == 0) {
1402 if (out) out->density = ResTable_config::DENSITY_HIGH;
1403 return true;
1404 }
1405
1406 if (strcmp(name, "xhdpi") == 0) {
1407 if (out) out->density = ResTable_config::DENSITY_XHIGH;
1408 return true;
1409 }
1410
1411 if (strcmp(name, "xxhdpi") == 0) {
1412 if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1413 return true;
1414 }
1415
1416 if (strcmp(name, "xxxhdpi") == 0) {
1417 if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
1418 return true;
1419 }
1420
1421 char* c = (char*)name;
1422 while (*c >= '0' && *c <= '9') {
1423 c++;
1424 }
1425
1426 // check that we have 'dpi' after the last digit.
1427 if (toupper(c[0]) != 'D' ||
1428 toupper(c[1]) != 'P' ||
1429 toupper(c[2]) != 'I' ||
1430 c[3] != 0) {
1431 return false;
1432 }
1433
1434 // temporarily replace the first letter with \0 to
1435 // use atoi.
1436 char tmp = c[0];
1437 c[0] = '\0';
1438
1439 int d = atoi(name);
1440 c[0] = tmp;
1441
1442 if (d != 0) {
1443 if (out) out->density = d;
1444 return true;
1445 }
1446
1447 return false;
1448}
1449
1450bool AaptGroupEntry::getTouchscreenName(const char* name,
1451 ResTable_config* out)
1452{
1453 if (strcmp(name, kWildcardName) == 0) {
1454 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1455 return true;
1456 } else if (strcmp(name, "notouch") == 0) {
1457 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1458 return true;
1459 } else if (strcmp(name, "stylus") == 0) {
1460 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1461 return true;
1462 } else if (strcmp(name, "finger") == 0) {
1463 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1464 return true;
1465 }
1466
1467 return false;
1468}
1469
1470bool AaptGroupEntry::getKeysHiddenName(const char* name,
1471 ResTable_config* out)
1472{
1473 uint8_t mask = 0;
1474 uint8_t value = 0;
1475 if (strcmp(name, kWildcardName) == 0) {
1476 mask = ResTable_config::MASK_KEYSHIDDEN;
1477 value = ResTable_config::KEYSHIDDEN_ANY;
1478 } else if (strcmp(name, "keysexposed") == 0) {
1479 mask = ResTable_config::MASK_KEYSHIDDEN;
1480 value = ResTable_config::KEYSHIDDEN_NO;
1481 } else if (strcmp(name, "keyshidden") == 0) {
1482 mask = ResTable_config::MASK_KEYSHIDDEN;
1483 value = ResTable_config::KEYSHIDDEN_YES;
1484 } else if (strcmp(name, "keyssoft") == 0) {
1485 mask = ResTable_config::MASK_KEYSHIDDEN;
1486 value = ResTable_config::KEYSHIDDEN_SOFT;
1487 }
1488
1489 if (mask != 0) {
1490 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1491 return true;
1492 }
1493
1494 return false;
1495}
1496
1497bool AaptGroupEntry::getKeyboardName(const char* name,
1498 ResTable_config* out)
1499{
1500 if (strcmp(name, kWildcardName) == 0) {
1501 if (out) out->keyboard = out->KEYBOARD_ANY;
1502 return true;
1503 } else if (strcmp(name, "nokeys") == 0) {
1504 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1505 return true;
1506 } else if (strcmp(name, "qwerty") == 0) {
1507 if (out) out->keyboard = out->KEYBOARD_QWERTY;
1508 return true;
1509 } else if (strcmp(name, "12key") == 0) {
1510 if (out) out->keyboard = out->KEYBOARD_12KEY;
1511 return true;
1512 }
1513
1514 return false;
1515}
1516
1517bool AaptGroupEntry::getNavHiddenName(const char* name,
1518 ResTable_config* out)
1519{
1520 uint8_t mask = 0;
1521 uint8_t value = 0;
1522 if (strcmp(name, kWildcardName) == 0) {
1523 mask = ResTable_config::MASK_NAVHIDDEN;
1524 value = ResTable_config::NAVHIDDEN_ANY;
1525 } else if (strcmp(name, "navexposed") == 0) {
1526 mask = ResTable_config::MASK_NAVHIDDEN;
1527 value = ResTable_config::NAVHIDDEN_NO;
1528 } else if (strcmp(name, "navhidden") == 0) {
1529 mask = ResTable_config::MASK_NAVHIDDEN;
1530 value = ResTable_config::NAVHIDDEN_YES;
1531 }
1532
1533 if (mask != 0) {
1534 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1535 return true;
1536 }
1537
1538 return false;
1539}
1540
1541bool AaptGroupEntry::getNavigationName(const char* name,
1542 ResTable_config* out)
1543{
1544 if (strcmp(name, kWildcardName) == 0) {
1545 if (out) out->navigation = out->NAVIGATION_ANY;
1546 return true;
1547 } else if (strcmp(name, "nonav") == 0) {
1548 if (out) out->navigation = out->NAVIGATION_NONAV;
1549 return true;
1550 } else if (strcmp(name, "dpad") == 0) {
1551 if (out) out->navigation = out->NAVIGATION_DPAD;
1552 return true;
1553 } else if (strcmp(name, "trackball") == 0) {
1554 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1555 return true;
1556 } else if (strcmp(name, "wheel") == 0) {
1557 if (out) out->navigation = out->NAVIGATION_WHEEL;
1558 return true;
1559 }
1560
1561 return false;
1562}
1563
1564bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
1565{
1566 if (strcmp(name, kWildcardName) == 0) {
1567 if (out) {
1568 out->screenWidth = out->SCREENWIDTH_ANY;
1569 out->screenHeight = out->SCREENHEIGHT_ANY;
1570 }
1571 return true;
1572 }
1573
1574 const char* x = name;
1575 while (*x >= '0' && *x <= '9') x++;
1576 if (x == name || *x != 'x') return false;
1577 String8 xName(name, x-name);
1578 x++;
1579
1580 const char* y = x;
1581 while (*y >= '0' && *y <= '9') y++;
1582 if (y == name || *y != 0) return false;
1583 String8 yName(x, y-x);
1584
1585 uint16_t w = (uint16_t)atoi(xName.string());
1586 uint16_t h = (uint16_t)atoi(yName.string());
1587 if (w < h) {
1588 return false;
1589 }
1590
1591 if (out) {
1592 out->screenWidth = w;
1593 out->screenHeight = h;
1594 }
1595
1596 return true;
1597}
1598
1599bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1600{
1601 if (strcmp(name, kWildcardName) == 0) {
1602 if (out) {
1603 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1604 }
1605 return true;
1606 }
1607
1608 if (*name != 's') return false;
1609 name++;
1610 if (*name != 'w') return false;
1611 name++;
1612 const char* x = name;
1613 while (*x >= '0' && *x <= '9') x++;
1614 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1615 String8 xName(name, x-name);
1616
1617 if (out) {
1618 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1619 }
1620
1621 return true;
1622}
1623
1624bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1625{
1626 if (strcmp(name, kWildcardName) == 0) {
1627 if (out) {
1628 out->screenWidthDp = out->SCREENWIDTH_ANY;
1629 }
1630 return true;
1631 }
1632
1633 if (*name != 'w') return false;
1634 name++;
1635 const char* x = name;
1636 while (*x >= '0' && *x <= '9') x++;
1637 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1638 String8 xName(name, x-name);
1639
1640 if (out) {
1641 out->screenWidthDp = (uint16_t)atoi(xName.string());
1642 }
1643
1644 return true;
1645}
1646
1647bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1648{
1649 if (strcmp(name, kWildcardName) == 0) {
1650 if (out) {
1651 out->screenHeightDp = out->SCREENWIDTH_ANY;
1652 }
1653 return true;
1654 }
1655
1656 if (*name != 'h') return false;
1657 name++;
1658 const char* x = name;
1659 while (*x >= '0' && *x <= '9') x++;
1660 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1661 String8 xName(name, x-name);
1662
1663 if (out) {
1664 out->screenHeightDp = (uint16_t)atoi(xName.string());
1665 }
1666
1667 return true;
1668}
1669
1670bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
1671{
1672 if (strcmp(name, kWildcardName) == 0) {
1673 if (out) {
1674 out->sdkVersion = out->SDKVERSION_ANY;
1675 out->minorVersion = out->MINORVERSION_ANY;
1676 }
1677 return true;
1678 }
1679
1680 if (*name != 'v') {
1681 return false;
1682 }
1683
1684 name++;
1685 const char* s = name;
1686 while (*s >= '0' && *s <= '9') s++;
1687 if (s == name || *s != 0) return false;
1688 String8 sdkName(name, s-name);
1689
1690 if (out) {
1691 out->sdkVersion = (uint16_t)atoi(sdkName.string());
1692 out->minorVersion = 0;
1693 }
1694
1695 return true;
1696}
1697
1698int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1699{
1700 int v = mcc.compare(o.mcc);
1701 if (v == 0) v = mnc.compare(o.mnc);
1702 if (v == 0) v = locale.compare(o.locale);
1703 if (v == 0) v = layoutDirection.compare(o.layoutDirection);
1704 if (v == 0) v = vendor.compare(o.vendor);
1705 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
1706 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1707 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
1708 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1709 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1710 if (v == 0) v = orientation.compare(o.orientation);
1711 if (v == 0) v = uiModeType.compare(o.uiModeType);
1712 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1713 if (v == 0) v = density.compare(o.density);
1714 if (v == 0) v = touchscreen.compare(o.touchscreen);
1715 if (v == 0) v = keysHidden.compare(o.keysHidden);
1716 if (v == 0) v = keyboard.compare(o.keyboard);
1717 if (v == 0) v = navHidden.compare(o.navHidden);
1718 if (v == 0) v = navigation.compare(o.navigation);
1719 if (v == 0) v = screenSize.compare(o.screenSize);
1720 if (v == 0) v = version.compare(o.version);
1721 return v;
1722}
1723
Narayan Kamath91447d82014-01-21 15:32:36 +00001724const ResTable_config AaptGroupEntry::toParams() const
Adam Lesinski282e1812014-01-23 18:17:42 -08001725{
1726 if (!mParamsChanged) {
1727 return mParams;
1728 }
1729
1730 mParamsChanged = false;
Narayan Kamath91447d82014-01-21 15:32:36 +00001731 ResTable_config& params = mParams;
1732 memset(&params, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -08001733 getMccName(mcc.string(), &params);
1734 getMncName(mnc.string(), &params);
Narayan Kamath91447d82014-01-21 15:32:36 +00001735 locale.writeTo(&params);
Adam Lesinski282e1812014-01-23 18:17:42 -08001736 getLayoutDirectionName(layoutDirection.string(), &params);
1737 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
1738 getScreenWidthDpName(screenWidthDp.string(), &params);
1739 getScreenHeightDpName(screenHeightDp.string(), &params);
1740 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1741 getScreenLayoutLongName(screenLayoutLong.string(), &params);
1742 getOrientationName(orientation.string(), &params);
1743 getUiModeTypeName(uiModeType.string(), &params);
1744 getUiModeNightName(uiModeNight.string(), &params);
1745 getDensityName(density.string(), &params);
1746 getTouchscreenName(touchscreen.string(), &params);
1747 getKeysHiddenName(keysHidden.string(), &params);
1748 getKeyboardName(keyboard.string(), &params);
1749 getNavHiddenName(navHidden.string(), &params);
1750 getNavigationName(navigation.string(), &params);
1751 getScreenSizeName(screenSize.string(), &params);
1752 getVersionName(version.string(), &params);
1753
1754 // Fix up version number based on specified parameters.
1755 int minSdk = 0;
1756 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1757 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
1758 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
1759 minSdk = SDK_HONEYCOMB_MR2;
1760 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1761 != ResTable_config::UI_MODE_TYPE_ANY
1762 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1763 != ResTable_config::UI_MODE_NIGHT_ANY) {
1764 minSdk = SDK_FROYO;
1765 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1766 != ResTable_config::SCREENSIZE_ANY
1767 || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1768 != ResTable_config::SCREENLONG_ANY
1769 || params.density != ResTable_config::DENSITY_DEFAULT) {
1770 minSdk = SDK_DONUT;
1771 }
1772
1773 if (minSdk > params.sdkVersion) {
1774 params.sdkVersion = minSdk;
1775 }
1776
1777 return params;
1778}
1779
1780// =========================================================================
1781// =========================================================================
1782// =========================================================================
1783
1784void* AaptFile::editData(size_t size)
1785{
1786 if (size <= mBufferSize) {
1787 mDataSize = size;
1788 return mData;
1789 }
1790 size_t allocSize = (size*3)/2;
1791 void* buf = realloc(mData, allocSize);
1792 if (buf == NULL) {
1793 return NULL;
1794 }
1795 mData = buf;
1796 mDataSize = size;
1797 mBufferSize = allocSize;
1798 return buf;
1799}
1800
Adam Lesinskide898ff2014-01-29 18:20:45 -08001801void* AaptFile::editDataInRange(size_t offset, size_t size)
1802{
1803 return (void*)(((uint8_t*) editData(offset + size)) + offset);
1804}
1805
Adam Lesinski282e1812014-01-23 18:17:42 -08001806void* AaptFile::editData(size_t* outSize)
1807{
1808 if (outSize) {
1809 *outSize = mDataSize;
1810 }
1811 return mData;
1812}
1813
1814void* AaptFile::padData(size_t wordSize)
1815{
1816 const size_t extra = mDataSize%wordSize;
1817 if (extra == 0) {
1818 return mData;
1819 }
1820
1821 size_t initial = mDataSize;
1822 void* data = editData(initial+(wordSize-extra));
1823 if (data != NULL) {
1824 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1825 }
1826 return data;
1827}
1828
1829status_t AaptFile::writeData(const void* data, size_t size)
1830{
1831 size_t end = mDataSize;
1832 size_t total = size + end;
1833 void* buf = editData(total);
1834 if (buf == NULL) {
1835 return UNKNOWN_ERROR;
1836 }
1837 memcpy(((char*)buf)+end, data, size);
1838 return NO_ERROR;
1839}
1840
1841void AaptFile::clearData()
1842{
1843 if (mData != NULL) free(mData);
1844 mData = NULL;
1845 mDataSize = 0;
1846 mBufferSize = 0;
1847}
1848
1849String8 AaptFile::getPrintableSource() const
1850{
1851 if (hasData()) {
1852 String8 name(mGroupEntry.toDirName(String8()));
1853 name.appendPath(mPath);
1854 name.append(" #generated");
1855 return name;
1856 }
1857 return mSourceFile;
1858}
1859
1860// =========================================================================
1861// =========================================================================
1862// =========================================================================
1863
Adam Lesinski09384302014-01-22 16:07:42 -08001864status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
Adam Lesinski282e1812014-01-23 18:17:42 -08001865{
Adam Lesinski09384302014-01-22 16:07:42 -08001866 ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
1867 if (index >= 0 && overwriteDuplicate) {
1868 fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
1869 mFiles[index]->getSourceFile().string(),
1870 file->getSourceFile().string());
1871 removeFile(index);
1872 index = -1;
1873 }
1874
1875 if (index < 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001876 file->mPath = mPath;
1877 mFiles.add(file->getGroupEntry(), file);
1878 return NO_ERROR;
1879 }
1880
1881#if 0
1882 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1883 file->getSourceFile().string(),
1884 file->getGroupEntry().toDirName(String8()).string(),
1885 mLeaf.string(), mPath.string());
1886#endif
1887
1888 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1889 getPrintableSource().string());
1890 return UNKNOWN_ERROR;
1891}
1892
1893void AaptGroup::removeFile(size_t index)
1894{
1895 mFiles.removeItemsAt(index);
1896}
1897
1898void AaptGroup::print(const String8& prefix) const
1899{
1900 printf("%s%s\n", prefix.string(), getPath().string());
1901 const size_t N=mFiles.size();
1902 size_t i;
1903 for (i=0; i<N; i++) {
1904 sp<AaptFile> file = mFiles.valueAt(i);
1905 const AaptGroupEntry& e = file->getGroupEntry();
1906 if (file->hasData()) {
1907 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
1908 (int)file->getSize());
1909 } else {
1910 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1911 file->getPrintableSource().string());
1912 }
1913 //printf("%s File Group Entry: %s\n", prefix.string(),
1914 // file->getGroupEntry().toDirName(String8()).string());
1915 }
1916}
1917
1918String8 AaptGroup::getPrintableSource() const
1919{
1920 if (mFiles.size() > 0) {
1921 // Arbitrarily pull the first source file out of the list.
1922 return mFiles.valueAt(0)->getPrintableSource();
1923 }
1924
1925 // Should never hit this case, but to be safe...
1926 return getPath();
1927
1928}
1929
1930// =========================================================================
1931// =========================================================================
1932// =========================================================================
1933
1934status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1935{
1936 if (mFiles.indexOfKey(name) >= 0) {
1937 return ALREADY_EXISTS;
1938 }
1939 mFiles.add(name, file);
1940 return NO_ERROR;
1941}
1942
1943status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1944{
1945 if (mDirs.indexOfKey(name) >= 0) {
1946 return ALREADY_EXISTS;
1947 }
1948 mDirs.add(name, dir);
1949 return NO_ERROR;
1950}
1951
1952sp<AaptDir> AaptDir::makeDir(const String8& path)
1953{
1954 String8 name;
1955 String8 remain = path;
1956
1957 sp<AaptDir> subdir = this;
1958 while (name = remain.walkPath(&remain), remain != "") {
1959 subdir = subdir->makeDir(name);
1960 }
1961
1962 ssize_t i = subdir->mDirs.indexOfKey(name);
1963 if (i >= 0) {
1964 return subdir->mDirs.valueAt(i);
1965 }
1966 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1967 subdir->mDirs.add(name, dir);
1968 return dir;
1969}
1970
1971void AaptDir::removeFile(const String8& name)
1972{
1973 mFiles.removeItem(name);
1974}
1975
1976void AaptDir::removeDir(const String8& name)
1977{
1978 mDirs.removeItem(name);
1979}
1980
Adam Lesinski09384302014-01-22 16:07:42 -08001981status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
1982 const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -08001983{
1984 sp<AaptGroup> group;
1985 if (mFiles.indexOfKey(leafName) >= 0) {
1986 group = mFiles.valueFor(leafName);
1987 } else {
1988 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1989 mFiles.add(leafName, group);
1990 }
1991
Adam Lesinski09384302014-01-22 16:07:42 -08001992 return group->addFile(file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -08001993}
1994
1995ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1996 const AaptGroupEntry& kind, const String8& resType,
Adam Lesinski09384302014-01-22 16:07:42 -08001997 sp<FilePathStore>& fullResPaths, const bool overwrite)
Adam Lesinski282e1812014-01-23 18:17:42 -08001998{
1999 Vector<String8> fileNames;
2000 {
2001 DIR* dir = NULL;
2002
2003 dir = opendir(srcDir.string());
2004 if (dir == NULL) {
2005 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2006 return UNKNOWN_ERROR;
2007 }
2008
2009 /*
2010 * Slurp the filenames out of the directory.
2011 */
2012 while (1) {
2013 struct dirent* entry;
2014
2015 entry = readdir(dir);
2016 if (entry == NULL)
2017 break;
2018
2019 if (isHidden(srcDir.string(), entry->d_name))
2020 continue;
2021
2022 String8 name(entry->d_name);
2023 fileNames.add(name);
2024 // Add fully qualified path for dependency purposes
2025 // if we're collecting them
2026 if (fullResPaths != NULL) {
2027 fullResPaths->add(srcDir.appendPathCopy(name));
2028 }
2029 }
2030 closedir(dir);
2031 }
2032
2033 ssize_t count = 0;
2034
2035 /*
2036 * Stash away the files and recursively descend into subdirectories.
2037 */
2038 const size_t N = fileNames.size();
2039 size_t i;
2040 for (i = 0; i < N; i++) {
2041 String8 pathName(srcDir);
2042 FileType type;
2043
2044 pathName.appendPath(fileNames[i].string());
2045 type = getFileType(pathName.string());
2046 if (type == kFileTypeDirectory) {
2047 sp<AaptDir> subdir;
2048 bool notAdded = false;
2049 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
2050 subdir = mDirs.valueFor(fileNames[i]);
2051 } else {
2052 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
2053 notAdded = true;
2054 }
2055 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Adam Lesinski09384302014-01-22 16:07:42 -08002056 resType, fullResPaths, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -08002057 if (res < NO_ERROR) {
2058 return res;
2059 }
2060 if (res > 0 && notAdded) {
2061 mDirs.add(fileNames[i], subdir);
2062 }
2063 count += res;
2064 } else if (type == kFileTypeRegular) {
2065 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
Adam Lesinski09384302014-01-22 16:07:42 -08002066 status_t err = addLeafFile(fileNames[i], file, overwrite);
Adam Lesinski282e1812014-01-23 18:17:42 -08002067 if (err != NO_ERROR) {
2068 return err;
2069 }
2070
2071 count++;
2072
2073 } else {
2074 if (bundle->getVerbose())
2075 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
2076 }
2077 }
2078
2079 return count;
2080}
2081
2082status_t AaptDir::validate() const
2083{
2084 const size_t NF = mFiles.size();
2085 const size_t ND = mDirs.size();
2086 size_t i;
2087 for (i = 0; i < NF; i++) {
2088 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
2089 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2090 "Invalid filename. Unable to add.");
2091 return UNKNOWN_ERROR;
2092 }
2093
2094 size_t j;
2095 for (j = i+1; j < NF; j++) {
2096 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2097 mFiles.valueAt(j)->getLeaf().string()) == 0) {
2098 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2099 "File is case-insensitive equivalent to: %s",
2100 mFiles.valueAt(j)->getPrintableSource().string());
2101 return UNKNOWN_ERROR;
2102 }
2103
2104 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
2105 // (this is mostly caught by the "marked" stuff, below)
2106 }
2107
2108 for (j = 0; j < ND; j++) {
2109 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2110 mDirs.valueAt(j)->getLeaf().string()) == 0) {
2111 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2112 "File conflicts with dir from: %s",
2113 mDirs.valueAt(j)->getPrintableSource().string());
2114 return UNKNOWN_ERROR;
2115 }
2116 }
2117 }
2118
2119 for (i = 0; i < ND; i++) {
2120 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
2121 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2122 "Invalid directory name, unable to add.");
2123 return UNKNOWN_ERROR;
2124 }
2125
2126 size_t j;
2127 for (j = i+1; j < ND; j++) {
2128 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
2129 mDirs.valueAt(j)->getLeaf().string()) == 0) {
2130 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2131 "Directory is case-insensitive equivalent to: %s",
2132 mDirs.valueAt(j)->getPrintableSource().string());
2133 return UNKNOWN_ERROR;
2134 }
2135 }
2136
2137 status_t err = mDirs.valueAt(i)->validate();
2138 if (err != NO_ERROR) {
2139 return err;
2140 }
2141 }
2142
2143 return NO_ERROR;
2144}
2145
2146void AaptDir::print(const String8& prefix) const
2147{
2148 const size_t ND=getDirs().size();
2149 size_t i;
2150 for (i=0; i<ND; i++) {
2151 getDirs().valueAt(i)->print(prefix);
2152 }
2153
2154 const size_t NF=getFiles().size();
2155 for (i=0; i<NF; i++) {
2156 getFiles().valueAt(i)->print(prefix);
2157 }
2158}
2159
2160String8 AaptDir::getPrintableSource() const
2161{
2162 if (mFiles.size() > 0) {
2163 // Arbitrarily pull the first file out of the list as the source dir.
2164 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
2165 }
2166 if (mDirs.size() > 0) {
2167 // Or arbitrarily pull the first dir out of the list as the source dir.
2168 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
2169 }
2170
2171 // Should never hit this case, but to be safe...
2172 return mPath;
2173
2174}
2175
2176// =========================================================================
2177// =========================================================================
2178// =========================================================================
2179
2180status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
2181{
2182 status_t err = NO_ERROR;
2183 size_t N = javaSymbols->mSymbols.size();
2184 for (size_t i=0; i<N; i++) {
2185 const String8& name = javaSymbols->mSymbols.keyAt(i);
2186 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
2187 ssize_t pos = mSymbols.indexOfKey(name);
2188 if (pos < 0) {
2189 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
2190 err = UNKNOWN_ERROR;
2191 continue;
2192 }
2193 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
2194 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
2195 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
2196 }
2197
2198 N = javaSymbols->mNestedSymbols.size();
2199 for (size_t i=0; i<N; i++) {
2200 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
2201 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
2202 ssize_t pos = mNestedSymbols.indexOfKey(name);
2203 if (pos < 0) {
2204 SourcePos pos;
2205 pos.error("Java symbol dir %s not defined\n", name.string());
2206 err = UNKNOWN_ERROR;
2207 continue;
2208 }
2209 //printf("**** applying java symbols in dir %s\n", name.string());
2210 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2211 if (myerr != NO_ERROR) {
2212 err = myerr;
2213 }
2214 }
2215
2216 return err;
2217}
2218
2219// =========================================================================
2220// =========================================================================
2221// =========================================================================
2222
2223AaptAssets::AaptAssets()
2224 : AaptDir(String8(), String8()),
Narayan Kamath91447d82014-01-21 15:32:36 +00002225 mHavePrivateSymbols(false),
2226 mChanged(false), mHaveIncludedAssets(false),
2227 mRes(NULL)
Adam Lesinski282e1812014-01-23 18:17:42 -08002228{
2229}
2230
2231const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
2232 if (mChanged) {
2233 }
2234 return mGroupEntries;
2235}
2236
2237status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
2238{
2239 mChanged = true;
2240 return AaptDir::addFile(name, file);
2241}
2242
2243sp<AaptFile> AaptAssets::addFile(
2244 const String8& filePath, const AaptGroupEntry& entry,
2245 const String8& srcDir, sp<AaptGroup>* outGroup,
2246 const String8& resType)
2247{
2248 sp<AaptDir> dir = this;
2249 sp<AaptGroup> group;
2250 sp<AaptFile> file;
2251 String8 root, remain(filePath), partialPath;
2252 while (remain.length() > 0) {
2253 root = remain.walkPath(&remain);
2254 partialPath.appendPath(root);
2255
2256 const String8 rootStr(root);
2257
2258 if (remain.length() == 0) {
2259 ssize_t i = dir->getFiles().indexOfKey(rootStr);
2260 if (i >= 0) {
2261 group = dir->getFiles().valueAt(i);
2262 } else {
2263 group = new AaptGroup(rootStr, filePath);
2264 status_t res = dir->addFile(rootStr, group);
2265 if (res != NO_ERROR) {
2266 return NULL;
2267 }
2268 }
2269 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2270 status_t res = group->addFile(file);
2271 if (res != NO_ERROR) {
2272 return NULL;
2273 }
2274 break;
2275
2276 } else {
2277 ssize_t i = dir->getDirs().indexOfKey(rootStr);
2278 if (i >= 0) {
2279 dir = dir->getDirs().valueAt(i);
2280 } else {
2281 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2282 status_t res = dir->addDir(rootStr, subdir);
2283 if (res != NO_ERROR) {
2284 return NULL;
2285 }
2286 dir = subdir;
2287 }
2288 }
2289 }
2290
2291 mGroupEntries.add(entry);
2292 if (outGroup) *outGroup = group;
2293 return file;
2294}
2295
2296void AaptAssets::addResource(const String8& leafName, const String8& path,
2297 const sp<AaptFile>& file, const String8& resType)
2298{
2299 sp<AaptDir> res = AaptDir::makeDir(kResString);
2300 String8 dirname = file->getGroupEntry().toDirName(resType);
2301 sp<AaptDir> subdir = res->makeDir(dirname);
2302 sp<AaptGroup> grr = new AaptGroup(leafName, path);
2303 grr->addFile(file);
2304
2305 subdir->addFile(leafName, grr);
2306}
2307
2308
2309ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2310{
2311 int count;
2312 int totalCount = 0;
2313 FileType type;
2314 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2315 const size_t dirCount =resDirs.size();
2316 sp<AaptAssets> current = this;
2317
2318 const int N = bundle->getFileSpecCount();
2319
2320 /*
2321 * If a package manifest was specified, include that first.
2322 */
2323 if (bundle->getAndroidManifestFile() != NULL) {
2324 // place at root of zip.
2325 String8 srcFile(bundle->getAndroidManifestFile());
2326 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2327 NULL, String8());
2328 totalCount++;
2329 }
2330
2331 /*
2332 * If a directory of custom assets was supplied, slurp 'em up.
2333 */
Adam Lesinski09384302014-01-22 16:07:42 -08002334 const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
2335 const int AN = assetDirs.size();
2336 for (int i = 0; i < AN; i++) {
2337 FileType type = getFileType(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08002338 if (type == kFileTypeNonexistent) {
Adam Lesinski09384302014-01-22 16:07:42 -08002339 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08002340 return UNKNOWN_ERROR;
2341 }
2342 if (type != kFileTypeDirectory) {
Adam Lesinski09384302014-01-22 16:07:42 -08002343 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08002344 return UNKNOWN_ERROR;
2345 }
2346
Adam Lesinski09384302014-01-22 16:07:42 -08002347 String8 assetRoot(assetDirs[i]);
Adam Lesinski282e1812014-01-23 18:17:42 -08002348 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2349 AaptGroupEntry group;
2350 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Adam Lesinski09384302014-01-22 16:07:42 -08002351 String8(), mFullAssetPaths, true);
Adam Lesinski282e1812014-01-23 18:17:42 -08002352 if (count < 0) {
2353 totalCount = count;
2354 goto bail;
2355 }
2356 if (count > 0) {
2357 mGroupEntries.add(group);
2358 }
2359 totalCount += count;
2360
Adam Lesinski09384302014-01-22 16:07:42 -08002361 if (bundle->getVerbose()) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002362 printf("Found %d custom asset file%s in %s\n",
Adam Lesinski09384302014-01-22 16:07:42 -08002363 count, (count==1) ? "" : "s", assetDirs[i]);
2364 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002365 }
2366
2367 /*
2368 * If a directory of resource-specific assets was supplied, slurp 'em up.
2369 */
2370 for (size_t i=0; i<dirCount; i++) {
2371 const char *res = resDirs[i];
2372 if (res) {
2373 type = getFileType(res);
2374 if (type == kFileTypeNonexistent) {
2375 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2376 return UNKNOWN_ERROR;
2377 }
2378 if (type == kFileTypeDirectory) {
2379 if (i>0) {
2380 sp<AaptAssets> nextOverlay = new AaptAssets();
2381 current->setOverlay(nextOverlay);
2382 current = nextOverlay;
2383 current->setFullResPaths(mFullResPaths);
2384 }
2385 count = current->slurpResourceTree(bundle, String8(res));
Bryan Mawhinney9ab9b932014-01-24 16:18:13 +00002386 if (i > 0 && count > 0) {
2387 count = current->filter(bundle);
2388 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002389
2390 if (count < 0) {
2391 totalCount = count;
2392 goto bail;
2393 }
2394 totalCount += count;
2395 }
2396 else {
2397 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2398 return UNKNOWN_ERROR;
2399 }
2400 }
2401
2402 }
2403 /*
2404 * Now do any additional raw files.
2405 */
2406 for (int arg=0; arg<N; arg++) {
2407 const char* assetDir = bundle->getFileSpecEntry(arg);
2408
2409 FileType type = getFileType(assetDir);
2410 if (type == kFileTypeNonexistent) {
2411 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2412 return UNKNOWN_ERROR;
2413 }
2414 if (type != kFileTypeDirectory) {
2415 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2416 return UNKNOWN_ERROR;
2417 }
2418
2419 String8 assetRoot(assetDir);
2420
2421 if (bundle->getVerbose())
2422 printf("Processing raw dir '%s'\n", (const char*) assetDir);
2423
2424 /*
2425 * Do a recursive traversal of subdir tree. We don't make any
2426 * guarantees about ordering, so we're okay with an inorder search
2427 * using whatever order the OS happens to hand back to us.
2428 */
2429 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2430 if (count < 0) {
2431 /* failure; report error and remove archive */
2432 totalCount = count;
2433 goto bail;
2434 }
2435 totalCount += count;
2436
2437 if (bundle->getVerbose())
2438 printf("Found %d asset file%s in %s\n",
2439 count, (count==1) ? "" : "s", assetDir);
2440 }
2441
2442 count = validate();
2443 if (count != NO_ERROR) {
2444 totalCount = count;
2445 goto bail;
2446 }
2447
2448 count = filter(bundle);
2449 if (count != NO_ERROR) {
2450 totalCount = count;
2451 goto bail;
2452 }
2453
2454bail:
2455 return totalCount;
2456}
2457
2458ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2459 const AaptGroupEntry& kind,
2460 const String8& resType,
2461 sp<FilePathStore>& fullResPaths)
2462{
2463 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2464 if (res > 0) {
2465 mGroupEntries.add(kind);
2466 }
2467
2468 return res;
2469}
2470
2471ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2472{
2473 ssize_t err = 0;
2474
2475 DIR* dir = opendir(srcDir.string());
2476 if (dir == NULL) {
2477 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2478 return UNKNOWN_ERROR;
2479 }
2480
2481 status_t count = 0;
2482
2483 /*
2484 * Run through the directory, looking for dirs that match the
2485 * expected pattern.
2486 */
2487 while (1) {
2488 struct dirent* entry = readdir(dir);
2489 if (entry == NULL) {
2490 break;
2491 }
2492
2493 if (isHidden(srcDir.string(), entry->d_name)) {
2494 continue;
2495 }
2496
2497 String8 subdirName(srcDir);
2498 subdirName.appendPath(entry->d_name);
2499
2500 AaptGroupEntry group;
2501 String8 resType;
2502 bool b = group.initFromDirName(entry->d_name, &resType);
2503 if (!b) {
2504 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2505 entry->d_name);
2506 err = -1;
2507 continue;
2508 }
2509
2510 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2511 int maxResInt = atoi(bundle->getMaxResVersion());
2512 const char *verString = group.getVersionString().string();
2513 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2514 if (dirVersionInt > maxResInt) {
2515 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2516 continue;
2517 }
2518 }
2519
2520 FileType type = getFileType(subdirName.string());
2521
2522 if (type == kFileTypeDirectory) {
2523 sp<AaptDir> dir = makeDir(resType);
2524 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2525 resType, mFullResPaths);
2526 if (res < 0) {
2527 count = res;
2528 goto bail;
2529 }
2530 if (res > 0) {
2531 mGroupEntries.add(group);
2532 count += res;
2533 }
2534
2535 // Only add this directory if we don't already have a resource dir
2536 // for the current type. This ensures that we only add the dir once
2537 // for all configs.
2538 sp<AaptDir> rdir = resDir(resType);
2539 if (rdir == NULL) {
2540 mResDirs.add(dir);
2541 }
2542 } else {
2543 if (bundle->getVerbose()) {
2544 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
2545 }
2546 }
2547 }
2548
2549bail:
2550 closedir(dir);
2551 dir = NULL;
2552
2553 if (err != 0) {
2554 return err;
2555 }
2556 return count;
2557}
2558
2559ssize_t
2560AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2561{
2562 int count = 0;
2563 SortedVector<AaptGroupEntry> entries;
2564
2565 ZipFile* zip = new ZipFile;
2566 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2567 if (err != NO_ERROR) {
2568 fprintf(stderr, "error opening zip file %s\n", filename);
2569 count = err;
2570 delete zip;
2571 return -1;
2572 }
2573
2574 const int N = zip->getNumEntries();
2575 for (int i=0; i<N; i++) {
2576 ZipEntry* entry = zip->getEntryByIndex(i);
2577 if (entry->getDeleted()) {
2578 continue;
2579 }
2580
2581 String8 entryName(entry->getFileName());
2582
2583 String8 dirName = entryName.getPathDir();
2584 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2585
2586 String8 resType;
2587 AaptGroupEntry kind;
2588
2589 String8 remain;
2590 if (entryName.walkPath(&remain) == kResourceDir) {
2591 // these are the resources, pull their type out of the directory name
2592 kind.initFromDirName(remain.walkPath().string(), &resType);
2593 } else {
2594 // these are untyped and don't have an AaptGroupEntry
2595 }
2596 if (entries.indexOf(kind) < 0) {
2597 entries.add(kind);
2598 mGroupEntries.add(kind);
2599 }
2600
2601 // use the one from the zip file if they both exist.
2602 dir->removeFile(entryName.getPathLeaf());
2603
2604 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2605 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2606 if (err != NO_ERROR) {
2607 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2608 count = err;
2609 goto bail;
2610 }
2611 file->setCompressionMethod(entry->getCompressionMethod());
2612
2613#if 0
2614 if (entryName == "AndroidManifest.xml") {
2615 printf("AndroidManifest.xml\n");
2616 }
2617 printf("\n\nfile: %s\n", entryName.string());
2618#endif
2619
2620 size_t len = entry->getUncompressedLen();
2621 void* data = zip->uncompress(entry);
2622 void* buf = file->editData(len);
2623 memcpy(buf, data, len);
2624
2625#if 0
2626 const int OFF = 0;
2627 const unsigned char* p = (unsigned char*)data;
2628 const unsigned char* end = p+len;
2629 p += OFF;
2630 for (int i=0; i<32 && p < end; i++) {
2631 printf("0x%03x ", i*0x10 + OFF);
2632 for (int j=0; j<0x10 && p < end; j++) {
2633 printf(" %02x", *p);
2634 p++;
2635 }
2636 printf("\n");
2637 }
2638#endif
2639
2640 free(data);
2641
2642 count++;
2643 }
2644
2645bail:
2646 delete zip;
2647 return count;
2648}
2649
2650status_t AaptAssets::filter(Bundle* bundle)
2651{
2652 ResourceFilter reqFilter;
2653 status_t err = reqFilter.parse(bundle->getConfigurations());
2654 if (err != NO_ERROR) {
2655 return err;
2656 }
2657
2658 ResourceFilter prefFilter;
2659 err = prefFilter.parse(bundle->getPreferredConfigurations());
2660 if (err != NO_ERROR) {
2661 return err;
2662 }
2663
2664 if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2665 return NO_ERROR;
2666 }
2667
2668 if (bundle->getVerbose()) {
2669 if (!reqFilter.isEmpty()) {
2670 printf("Applying required filter: %s\n",
2671 bundle->getConfigurations());
2672 }
2673 if (!prefFilter.isEmpty()) {
2674 printf("Applying preferred filter: %s\n",
2675 bundle->getPreferredConfigurations());
2676 }
2677 }
2678
2679 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2680 const size_t ND = resdirs.size();
2681 for (size_t i=0; i<ND; i++) {
2682 const sp<AaptDir>& dir = resdirs.itemAt(i);
2683 if (dir->getLeaf() == kValuesDir) {
2684 // The "value" dir is special since a single file defines
2685 // multiple resources, so we can not do filtering on the
2686 // files themselves.
2687 continue;
2688 }
2689 if (dir->getLeaf() == kMipmapDir) {
2690 // We also skip the "mipmap" directory, since the point of this
2691 // is to include all densities without stripping. If you put
2692 // other configurations in here as well they won't be stripped
2693 // either... So don't do that. Seriously. What is wrong with you?
2694 continue;
2695 }
2696
2697 const size_t NG = dir->getFiles().size();
2698 for (size_t j=0; j<NG; j++) {
2699 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2700
2701 // First remove any configurations we know we don't need.
2702 for (size_t k=0; k<grp->getFiles().size(); k++) {
2703 sp<AaptFile> file = grp->getFiles().valueAt(k);
2704 if (k == 0 && grp->getFiles().size() == 1) {
2705 // If this is the only file left, we need to keep it.
2706 // Otherwise the resource IDs we are using will be inconsistent
2707 // with what we get when not stripping. Sucky, but at least
2708 // for now we can rely on the back-end doing another filtering
2709 // pass to take this out and leave us with this resource name
2710 // containing no entries.
2711 continue;
2712 }
2713 if (file->getPath().getPathExtension() == ".xml") {
2714 // We can't remove .xml files at this point, because when
2715 // we parse them they may add identifier resources, so
2716 // removing them can cause our resource identifiers to
2717 // become inconsistent.
2718 continue;
2719 }
2720 const ResTable_config& config(file->getGroupEntry().toParams());
2721 if (!reqFilter.match(config)) {
2722 if (bundle->getVerbose()) {
2723 printf("Pruning unneeded resource: %s\n",
2724 file->getPrintableSource().string());
2725 }
2726 grp->removeFile(k);
2727 k--;
2728 }
2729 }
2730
2731 // Quick check: no preferred filters, nothing more to do.
2732 if (prefFilter.isEmpty()) {
2733 continue;
2734 }
2735
Adam Lesinski8cf61842013-10-18 13:42:09 -07002736 // Get the preferred density if there is one. We do not match exactly for density.
2737 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2738 // pick xhdpi.
2739 uint32_t preferredDensity = 0;
Narayan Kamath91447d82014-01-21 15:32:36 +00002740 const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
Adam Lesinski8cf61842013-10-18 13:42:09 -07002741 if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
Narayan Kamath91447d82014-01-21 15:32:36 +00002742 preferredDensity = (*preferredConfigs)[0].intValue;
Adam Lesinski8cf61842013-10-18 13:42:09 -07002743 }
2744
Adam Lesinski282e1812014-01-23 18:17:42 -08002745 // Now deal with preferred configurations.
2746 for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2747 for (size_t k=0; k<grp->getFiles().size(); k++) {
2748 sp<AaptFile> file = grp->getFiles().valueAt(k);
2749 if (k == 0 && grp->getFiles().size() == 1) {
2750 // If this is the only file left, we need to keep it.
2751 // Otherwise the resource IDs we are using will be inconsistent
2752 // with what we get when not stripping. Sucky, but at least
2753 // for now we can rely on the back-end doing another filtering
2754 // pass to take this out and leave us with this resource name
2755 // containing no entries.
2756 continue;
2757 }
2758 if (file->getPath().getPathExtension() == ".xml") {
2759 // We can't remove .xml files at this point, because when
2760 // we parse them they may add identifier resources, so
2761 // removing them can cause our resource identifiers to
2762 // become inconsistent.
2763 continue;
2764 }
2765 const ResTable_config& config(file->getGroupEntry().toParams());
2766 if (!prefFilter.match(axis, config)) {
2767 // This is a resource we would prefer not to have. Check
2768 // to see if have a similar variation that we would like
2769 // to have and, if so, we can drop it.
Adam Lesinski8cf61842013-10-18 13:42:09 -07002770
2771 uint32_t bestDensity = config.density;
2772
Adam Lesinski282e1812014-01-23 18:17:42 -08002773 for (size_t m=0; m<grp->getFiles().size(); m++) {
2774 if (m == k) continue;
2775 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2776 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2777 if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
Adam Lesinski8cf61842013-10-18 13:42:09 -07002778 if (axis == AXIS_DENSITY && preferredDensity > 0) {
2779 // See if there is a better density resource
2780 if (mconfig.density < bestDensity &&
2781 mconfig.density > preferredDensity &&
2782 bestDensity > preferredDensity) {
2783 // This density is between our best density and
2784 // the preferred density, therefore it is better.
2785 bestDensity = mconfig.density;
2786 } else if (mconfig.density > bestDensity &&
2787 bestDensity < preferredDensity) {
2788 // This density is better than our best density and
2789 // our best density was smaller than our preferred
2790 // density, so it is better.
2791 bestDensity = mconfig.density;
2792 }
2793 } else if (prefFilter.match(axis, mconfig)) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002794 if (bundle->getVerbose()) {
2795 printf("Pruning unneeded resource: %s\n",
2796 file->getPrintableSource().string());
2797 }
2798 grp->removeFile(k);
2799 k--;
2800 break;
2801 }
2802 }
2803 }
Adam Lesinski8cf61842013-10-18 13:42:09 -07002804
2805 if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2806 bestDensity != config.density) {
2807 if (bundle->getVerbose()) {
2808 printf("Pruning unneeded resource: %s\n",
2809 file->getPrintableSource().string());
2810 }
2811 grp->removeFile(k);
2812 k--;
2813 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002814 }
2815 }
2816 }
2817 }
2818 }
2819
2820 return NO_ERROR;
2821}
2822
2823sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2824{
2825 sp<AaptSymbols> sym = mSymbols.valueFor(name);
2826 if (sym == NULL) {
2827 sym = new AaptSymbols();
2828 mSymbols.add(name, sym);
2829 }
2830 return sym;
2831}
2832
2833sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2834{
2835 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2836 if (sym == NULL) {
2837 sym = new AaptSymbols();
2838 mJavaSymbols.add(name, sym);
2839 }
2840 return sym;
2841}
2842
2843status_t AaptAssets::applyJavaSymbols()
2844{
2845 size_t N = mJavaSymbols.size();
2846 for (size_t i=0; i<N; i++) {
2847 const String8& name = mJavaSymbols.keyAt(i);
2848 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2849 ssize_t pos = mSymbols.indexOfKey(name);
2850 if (pos < 0) {
2851 SourcePos pos;
2852 pos.error("Java symbol dir %s not defined\n", name.string());
2853 return UNKNOWN_ERROR;
2854 }
2855 //printf("**** applying java symbols in dir %s\n", name.string());
2856 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2857 if (err != NO_ERROR) {
2858 return err;
2859 }
2860 }
2861
2862 return NO_ERROR;
2863}
2864
2865bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2866 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2867 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2868 // sym.isJavaSymbol ? 1 : 0);
2869 if (!mHavePrivateSymbols) return true;
2870 if (sym.isPublic) return true;
2871 if (includePrivate && sym.isJavaSymbol) return true;
2872 return false;
2873}
2874
2875status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2876{
2877 if (!mHaveIncludedAssets) {
2878 // Add in all includes.
2879 const Vector<const char*>& incl = bundle->getPackageIncludes();
2880 const size_t N=incl.size();
2881 for (size_t i=0; i<N; i++) {
2882 if (bundle->getVerbose())
2883 printf("Including resources from package: %s\n", incl[i]);
2884 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2885 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2886 incl[i]);
2887 return UNKNOWN_ERROR;
2888 }
2889 }
2890 mHaveIncludedAssets = true;
2891 }
2892
2893 return NO_ERROR;
2894}
2895
2896status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2897{
2898 const ResTable& res = getIncludedResources();
2899 // XXX dirty!
Narayan Kamath00b31442014-01-27 17:32:37 +00002900 return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
Adam Lesinski282e1812014-01-23 18:17:42 -08002901}
2902
2903const ResTable& AaptAssets::getIncludedResources() const
2904{
2905 return mIncludedAssets.getResources(false);
2906}
2907
2908void AaptAssets::print(const String8& prefix) const
2909{
2910 String8 innerPrefix(prefix);
2911 innerPrefix.append(" ");
2912 String8 innerInnerPrefix(innerPrefix);
2913 innerInnerPrefix.append(" ");
2914 printf("%sConfigurations:\n", prefix.string());
2915 const size_t N=mGroupEntries.size();
2916 for (size_t i=0; i<N; i++) {
2917 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2918 printf("%s %s\n", prefix.string(),
2919 cname != "" ? cname.string() : "(default)");
2920 }
2921
2922 printf("\n%sFiles:\n", prefix.string());
2923 AaptDir::print(innerPrefix);
2924
2925 printf("\n%sResource Dirs:\n", prefix.string());
2926 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2927 const size_t NR = resdirs.size();
2928 for (size_t i=0; i<NR; i++) {
2929 const sp<AaptDir>& d = resdirs.itemAt(i);
2930 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
2931 d->print(innerInnerPrefix);
2932 }
2933}
2934
2935sp<AaptDir> AaptAssets::resDir(const String8& name) const
2936{
2937 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2938 const size_t N = resdirs.size();
2939 for (size_t i=0; i<N; i++) {
2940 const sp<AaptDir>& d = resdirs.itemAt(i);
2941 if (d->getLeaf() == name) {
2942 return d;
2943 }
2944 }
2945 return NULL;
2946}
2947
2948bool
2949valid_symbol_name(const String8& symbol)
2950{
2951 static char const * const KEYWORDS[] = {
2952 "abstract", "assert", "boolean", "break",
2953 "byte", "case", "catch", "char", "class", "const", "continue",
2954 "default", "do", "double", "else", "enum", "extends", "final",
2955 "finally", "float", "for", "goto", "if", "implements", "import",
2956 "instanceof", "int", "interface", "long", "native", "new", "package",
2957 "private", "protected", "public", "return", "short", "static",
2958 "strictfp", "super", "switch", "synchronized", "this", "throw",
2959 "throws", "transient", "try", "void", "volatile", "while",
2960 "true", "false", "null",
2961 NULL
2962 };
2963 const char*const* k = KEYWORDS;
2964 const char*const s = symbol.string();
2965 while (*k) {
2966 if (0 == strcmp(s, *k)) {
2967 return false;
2968 }
2969 k++;
2970 }
2971 return true;
2972}