blob: 19532e8291f8fc562daf76f7dc1b412cb6ceb6db [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4
5#include "AaptAssets.h"
Dianne Hackborne6b68032011-10-13 16:26:02 -07006#include "ResourceFilter.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007#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";
Dianne Hackborne6b68032011-10-13 16:26:02 -070020static const char* kValuesDir = "values";
21static const char* kMipmapDir = "mipmap";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022static 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
Raphael Moll6c255a32012-05-07 16:16:46 -070059// The default to use if no other ignore pattern is defined.
60const char * const gDefaultIgnoreAssets =
Tor Norbyee0219c82012-06-04 10:38:13 -070061 "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
Raphael Moll6c255a32012-05-07 16:16:46 -070062// The ignore pattern that can be passed via --ignore-assets in Main.cpp
63const char * gUserIgnoreAssets = NULL;
64
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065static bool isHidden(const char *root, const char *path)
66{
Raphael Moll6c255a32012-05-07 16:16:46 -070067 // 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.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
Raphael Moll6c255a32012-05-07 16:16:46 -070080 if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 }
83
Raphael Moll6c255a32012-05-07 16:16:46 -070084 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);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093
Raphael Moll6c255a32012-05-07 16:16:46 -070094 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++;
Ying Wang996b0732012-05-22 11:24:22 -0700125 n--;
Raphael Moll6c255a32012-05-07 16:16:46 -0700126 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146}
147
148// =========================================================================
149// =========================================================================
150// =========================================================================
151
Narayan Kamath788fa412014-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)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444{
445 ResTable_config config;
Narayan Kamath788fa412014-01-21 15:32:36 +0000446 memset(&config, 0, sizeof(ResTable_config));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447
448 // IMSI - MCC
449 if (getMccName(part.string(), &config)) {
450 *axis = AXIS_MCC;
Narayan Kamath788fa412014-01-21 15:32:36 +0000451 value->intValue = config.mcc;
452 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 }
454
455 // IMSI - MNC
456 if (getMncName(part.string(), &config)) {
457 *axis = AXIS_MNC;
Narayan Kamath788fa412014-01-21 15:32:36 +0000458 value->intValue = config.mnc;
459 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 }
461
462 // locale - language
Narayan Kamath788fa412014-01-21 15:32:36 +0000463 if (value->localeValue.initFromFilterString(part)) {
464 *axis = AXIS_LOCALE;
465 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 }
467
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700468 // layout direction
469 if (getLayoutDirectionName(part.string(), &config)) {
470 *axis = AXIS_LAYOUTDIR;
Narayan Kamath788fa412014-01-21 15:32:36 +0000471 value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
472 return true;
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700473 }
474
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700475 // smallest screen dp width
476 if (getSmallestScreenWidthDpName(part.string(), &config)) {
477 *axis = AXIS_SMALLESTSCREENWIDTHDP;
Narayan Kamath788fa412014-01-21 15:32:36 +0000478 value->intValue = config.smallestScreenWidthDp;
479 return true;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700480 }
481
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700482 // screen dp width
483 if (getScreenWidthDpName(part.string(), &config)) {
484 *axis = AXIS_SCREENWIDTHDP;
Narayan Kamath788fa412014-01-21 15:32:36 +0000485 value->intValue = config.screenWidthDp;
486 return true;
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700487 }
488
489 // screen dp height
490 if (getScreenHeightDpName(part.string(), &config)) {
491 *axis = AXIS_SCREENHEIGHTDP;
Narayan Kamath788fa412014-01-21 15:32:36 +0000492 value->intValue = config.screenHeightDp;
493 return true;
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700494 }
495
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700496 // screen layout size
497 if (getScreenLayoutSizeName(part.string(), &config)) {
498 *axis = AXIS_SCREENLAYOUTSIZE;
Narayan Kamath788fa412014-01-21 15:32:36 +0000499 value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
500 return true;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700501 }
502
503 // screen layout long
504 if (getScreenLayoutLongName(part.string(), &config)) {
505 *axis = AXIS_SCREENLAYOUTLONG;
Narayan Kamath788fa412014-01-21 15:32:36 +0000506 value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
507 return true;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700508 }
509
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 // orientation
511 if (getOrientationName(part.string(), &config)) {
512 *axis = AXIS_ORIENTATION;
Narayan Kamath788fa412014-01-21 15:32:36 +0000513 value->intValue = config.orientation;
514 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 }
516
Tobias Haamel27b28b32010-02-09 23:09:17 +0100517 // ui mode type
518 if (getUiModeTypeName(part.string(), &config)) {
519 *axis = AXIS_UIMODETYPE;
Narayan Kamath788fa412014-01-21 15:32:36 +0000520 value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
521 return true;
Tobias Haamel27b28b32010-02-09 23:09:17 +0100522 }
523
524 // ui mode night
525 if (getUiModeNightName(part.string(), &config)) {
526 *axis = AXIS_UIMODENIGHT;
Narayan Kamath788fa412014-01-21 15:32:36 +0000527 value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
528 return true;
Tobias Haamel27b28b32010-02-09 23:09:17 +0100529 }
530
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 // density
532 if (getDensityName(part.string(), &config)) {
533 *axis = AXIS_DENSITY;
Narayan Kamath788fa412014-01-21 15:32:36 +0000534 value->intValue = config.density;
535 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 }
537
538 // touchscreen
539 if (getTouchscreenName(part.string(), &config)) {
540 *axis = AXIS_TOUCHSCREEN;
Narayan Kamath788fa412014-01-21 15:32:36 +0000541 value->intValue = config.touchscreen;
542 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 }
544
545 // keyboard hidden
546 if (getKeysHiddenName(part.string(), &config)) {
547 *axis = AXIS_KEYSHIDDEN;
Narayan Kamath788fa412014-01-21 15:32:36 +0000548 value->intValue = config.inputFlags;
549 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 }
551
552 // keyboard
553 if (getKeyboardName(part.string(), &config)) {
554 *axis = AXIS_KEYBOARD;
Narayan Kamath788fa412014-01-21 15:32:36 +0000555 value->intValue = config.keyboard;
556 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 }
558
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700559 // navigation hidden
560 if (getNavHiddenName(part.string(), &config)) {
561 *axis = AXIS_NAVHIDDEN;
Narayan Kamath788fa412014-01-21 15:32:36 +0000562 value->intValue = config.inputFlags;
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700563 return 0;
564 }
565
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 // navigation
567 if (getNavigationName(part.string(), &config)) {
568 *axis = AXIS_NAVIGATION;
Narayan Kamath788fa412014-01-21 15:32:36 +0000569 value->intValue = config.navigation;
570 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 }
572
573 // screen size
574 if (getScreenSizeName(part.string(), &config)) {
575 *axis = AXIS_SCREENSIZE;
Narayan Kamath788fa412014-01-21 15:32:36 +0000576 value->intValue = config.screenSize;
577 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 }
579
580 // version
581 if (getVersionName(part.string(), &config)) {
582 *axis = AXIS_VERSION;
Narayan Kamath788fa412014-01-21 15:32:36 +0000583 value->intValue = config.version;
584 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 }
586
Narayan Kamath788fa412014-01-21 15:32:36 +0000587 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588}
589
Narayan Kamath788fa412014-01-21 15:32:36 +0000590AxisValue
Dianne Hackborne6b68032011-10-13 16:26:02 -0700591AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
592{
Narayan Kamath788fa412014-01-21 15:32:36 +0000593 AxisValue value;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700594 switch (axis) {
595 case AXIS_MCC:
Narayan Kamath788fa412014-01-21 15:32:36 +0000596 value.intValue = config.mcc;
597 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700598 case AXIS_MNC:
Narayan Kamath788fa412014-01-21 15:32:36 +0000599 value.intValue = config.mnc;
600 break;
601 case AXIS_LOCALE:
602 value.localeValue.initFromResTable(config);
603 break;
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700604 case AXIS_LAYOUTDIR:
Narayan Kamath788fa412014-01-21 15:32:36 +0000605 value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
606 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700607 case AXIS_SCREENLAYOUTSIZE:
Narayan Kamath788fa412014-01-21 15:32:36 +0000608 value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
609 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700610 case AXIS_ORIENTATION:
Narayan Kamath788fa412014-01-21 15:32:36 +0000611 value.intValue = config.orientation;
612 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700613 case AXIS_UIMODETYPE:
Narayan Kamath788fa412014-01-21 15:32:36 +0000614 value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
615 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700616 case AXIS_UIMODENIGHT:
Narayan Kamath788fa412014-01-21 15:32:36 +0000617 value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
618 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700619 case AXIS_DENSITY:
Narayan Kamath788fa412014-01-21 15:32:36 +0000620 value.intValue = config.density;
621 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700622 case AXIS_TOUCHSCREEN:
Narayan Kamath788fa412014-01-21 15:32:36 +0000623 value.intValue = config.touchscreen;
624 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700625 case AXIS_KEYSHIDDEN:
Narayan Kamath788fa412014-01-21 15:32:36 +0000626 value.intValue = config.inputFlags;
627 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700628 case AXIS_KEYBOARD:
Narayan Kamath788fa412014-01-21 15:32:36 +0000629 value.intValue = config.keyboard;
630 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700631 case AXIS_NAVIGATION:
Narayan Kamath788fa412014-01-21 15:32:36 +0000632 value.intValue = config.navigation;
633 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700634 case AXIS_SCREENSIZE:
Narayan Kamath788fa412014-01-21 15:32:36 +0000635 value.intValue = config.screenSize;
636 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700637 case AXIS_SMALLESTSCREENWIDTHDP:
Narayan Kamath788fa412014-01-21 15:32:36 +0000638 value.intValue = config.smallestScreenWidthDp;
639 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700640 case AXIS_SCREENWIDTHDP:
Narayan Kamath788fa412014-01-21 15:32:36 +0000641 value.intValue = config.screenWidthDp;
642 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700643 case AXIS_SCREENHEIGHTDP:
Narayan Kamath788fa412014-01-21 15:32:36 +0000644 value.intValue = config.screenHeightDp;
645 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700646 case AXIS_VERSION:
Narayan Kamath788fa412014-01-21 15:32:36 +0000647 value.intValue = config.version;
648 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700649 }
Narayan Kamath788fa412014-01-21 15:32:36 +0000650
651 return value;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700652}
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669bool
670AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
671{
Dianne Hackborne6b68032011-10-13 16:26:02 -0700672 mParamsChanged = true;
673
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 Vector<String8> parts;
Narayan Kamath788fa412014-01-21 15:32:36 +0000675 AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676
Narayan Kamath788fa412014-01-21 15:32:36 +0000677 String8 mcc, mnc, layoutsize, layoutlong, orient, den;
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700678 String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700679 String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680
Narayan Kamath788fa412014-01-21 15:32:36 +0000681 AaptLocaleValue locale;
682 int numLocaleComponents = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -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 Kamath788fa412014-01-21 15:32:36 +0000723 //printf("not mnc: %s\n", part.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 }
725
Narayan Kamath788fa412014-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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 }
733
Narayan Kamath788fa412014-01-21 15:32:36 +0000734 part = parts[index];
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700735 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
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700747 if (getSmallestScreenWidthDpName(part.string())) {
748 smallestwidthdp = part;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700749
750 index++;
751 if (index == N) {
752 goto success;
753 }
754 part = parts[index];
755 } else {
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700756 //printf("not smallest screen width dp: %s\n", part.string());
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700757 }
758
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700759 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
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700783 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 // 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
Tobias Haamel27b28b32010-02-09 23:09:17 +0100820 // 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 // 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
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700898 // 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 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 Kamath788fa412014-01-21 15:32:36 +0000953 this->locale = locale;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700954 this->screenLayoutSize = layoutsize;
955 this->screenLayoutLong = layoutlong;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700956 this->smallestScreenWidthDp = smallestwidthdp;
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700957 this->screenWidthDp = widthdp;
958 this->screenHeightDp = heightdp;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 this->orientation = orient;
Tobias Haamel27b28b32010-02-09 23:09:17 +0100960 this->uiModeType = uiModeType;
961 this->uiModeNight = uiModeNight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 this->density = den;
963 this->touchscreen = touch;
964 this->keysHidden = keysHidden;
965 this->keyboard = key;
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700966 this->navHidden = navHidden;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 this->navigation = nav;
968 this->screenSize = size;
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700969 this->layoutDirection = layoutDir;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 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 Kamath788fa412014-01-21 15:32:36 +0000985 s += locale.toDirName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 s += ",";
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700987 s += layoutDirection;
988 s += ",";
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700989 s += smallestScreenWidthDp;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700990 s += ",";
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700991 s += screenWidthDp;
992 s += ",";
993 s += screenHeightDp;
994 s += ",";
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700995 s += screenLayoutSize;
996 s += ",";
997 s += screenLayoutLong;
998 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 s += this->orientation;
1000 s += ",";
Tobias Haamel27b28b32010-02-09 23:09:17 +01001001 s += uiModeType;
1002 s += ",";
1003 s += uiModeNight;
1004 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001005 s += density;
1006 s += ",";
1007 s += touchscreen;
1008 s += ",";
1009 s += keysHidden;
1010 s += ",";
1011 s += keyboard;
1012 s += ",";
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001013 s += navHidden;
1014 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 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 != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001028 if (s.length() > 0) {
1029 s += "-";
1030 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 s += mcc;
1032 }
1033 if (this->mnc != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001034 if (s.length() > 0) {
1035 s += "-";
1036 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 s += mnc;
1038 }
Narayan Kamath788fa412014-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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 }
Narayan Kamath788fa412014-01-21 15:32:36 +00001047
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001048 if (this->layoutDirection != "") {
1049 if (s.length() > 0) {
1050 s += "-";
1051 }
1052 s += layoutDirection;
1053 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001054 if (this->smallestScreenWidthDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001055 if (s.length() > 0) {
1056 s += "-";
1057 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001058 s += smallestScreenWidthDp;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001059 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001060 if (this->screenWidthDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001061 if (s.length() > 0) {
1062 s += "-";
1063 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001064 s += screenWidthDp;
1065 }
1066 if (this->screenHeightDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001067 if (s.length() > 0) {
1068 s += "-";
1069 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001070 s += screenHeightDp;
1071 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001072 if (this->screenLayoutSize != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001073 if (s.length() > 0) {
1074 s += "-";
1075 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001076 s += screenLayoutSize;
1077 }
1078 if (this->screenLayoutLong != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001079 if (s.length() > 0) {
1080 s += "-";
1081 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001082 s += screenLayoutLong;
1083 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 if (this->orientation != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001085 if (s.length() > 0) {
1086 s += "-";
1087 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 s += orientation;
1089 }
Tobias Haamel27b28b32010-02-09 23:09:17 +01001090 if (this->uiModeType != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001091 if (s.length() > 0) {
1092 s += "-";
1093 }
Tobias Haamel27b28b32010-02-09 23:09:17 +01001094 s += uiModeType;
1095 }
1096 if (this->uiModeNight != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001097 if (s.length() > 0) {
1098 s += "-";
1099 }
Tobias Haamel27b28b32010-02-09 23:09:17 +01001100 s += uiModeNight;
1101 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 if (this->density != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001103 if (s.length() > 0) {
1104 s += "-";
1105 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 s += density;
1107 }
1108 if (this->touchscreen != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001109 if (s.length() > 0) {
1110 s += "-";
1111 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112 s += touchscreen;
1113 }
1114 if (this->keysHidden != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001115 if (s.length() > 0) {
1116 s += "-";
1117 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 s += keysHidden;
1119 }
1120 if (this->keyboard != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001121 if (s.length() > 0) {
1122 s += "-";
1123 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 s += keyboard;
1125 }
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001126 if (this->navHidden != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001127 if (s.length() > 0) {
1128 s += "-";
1129 }
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001130 s += navHidden;
1131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 if (this->navigation != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001133 if (s.length() > 0) {
1134 s += "-";
1135 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001136 s += navigation;
1137 }
1138 if (this->screenSize != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001139 if (s.length() > 0) {
1140 s += "-";
1141 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 s += screenSize;
1143 }
1144 if (this->version != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001145 if (s.length() > 0) {
1146 s += "-";
1147 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 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
Johan Redestig5ef0b9d2010-11-09 14:13:31 +01001209 if (out) {
1210 out->mnc = atoi(val);
Mattias Petersson1d766b52011-10-07 09:33:52 +02001211 if (out->mnc == 0) {
1212 out->mnc = ACONFIGURATION_MNC_ZERO;
1213 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 }
1215
Johan Redestig5ef0b9d2010-11-09 14:13:31 +01001216 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217}
1218
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001219bool 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;
Fabrice Di Meglio8a802db2012-09-05 13:12:02 -07001226 } else if (strcmp(name, "ldltr") == 0) {
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001227 if (out) out->screenLayout =
1228 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1229 | ResTable_config::LAYOUTDIR_LTR;
1230 return true;
Fabrice Di Meglio8a802db2012-09-05 13:12:02 -07001231 } else if (strcmp(name, "ldrtl") == 0) {
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001232 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
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001241bool 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;
Dianne Hackborn14cee9f2010-04-23 17:51:26 -07001264 } 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;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001269 }
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001297bool 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
Tobias Haamel27b28b32010-02-09 23:09:17 +01001317bool 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)
Dianne Hackborn7299c412010-03-04 18:41:49 -08001323 | 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;
Tobias Haamel27b28b32010-02-09 23:09:17 +01001329 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;
Dianne Hackborne360bb62011-05-20 16:11:04 -07001335 } 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;
Joe Onorato44fcb832011-12-14 20:59:30 -08001340 } 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;
Tobias Haamel27b28b32010-02-09 23:09:17 +01001345 }
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373bool AaptGroupEntry::getDensityName(const char* name,
1374 ResTable_config* out)
1375{
1376 if (strcmp(name, kWildcardName) == 0) {
Dianne Hackborna53b8282009-07-17 11:13:48 -07001377 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001378 return true;
1379 }
Dianne Hackborna53b8282009-07-17 11:13:48 -07001380
1381 if (strcmp(name, "nodpi") == 0) {
1382 if (out) out->density = ResTable_config::DENSITY_NONE;
1383 return true;
1384 }
1385
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001386 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
Dianne Hackbornb96cbbd2011-05-27 13:40:26 -07001396 if (strcmp(name, "tvdpi") == 0) {
1397 if (out) out->density = ResTable_config::DENSITY_TV;
1398 return true;
1399 }
1400
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001401 if (strcmp(name, "hdpi") == 0) {
1402 if (out) out->density = ResTable_config::DENSITY_HIGH;
1403 return true;
1404 }
Dianne Hackbornd96e3df2012-01-25 15:12:23 -08001405
Dianne Hackborn588feee2010-06-04 14:36:39 -07001406 if (strcmp(name, "xhdpi") == 0) {
Dianne Hackbornd96e3df2012-01-25 15:12:23 -08001407 if (out) out->density = ResTable_config::DENSITY_XHIGH;
Dianne Hackborn588feee2010-06-04 14:36:39 -07001408 return true;
1409 }
Dianne Hackbornd96e3df2012-01-25 15:12:23 -08001410
1411 if (strcmp(name, "xxhdpi") == 0) {
1412 if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1413 return true;
1414 }
1415
Dianne Hackborn56a23012013-02-12 15:41:49 -08001416 if (strcmp(name, "xxxhdpi") == 0) {
1417 if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
1418 return true;
1419 }
1420
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001421 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) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001476 mask = ResTable_config::MASK_KEYSHIDDEN;
1477 value = ResTable_config::KEYSHIDDEN_ANY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 } else if (strcmp(name, "keysexposed") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001479 mask = ResTable_config::MASK_KEYSHIDDEN;
1480 value = ResTable_config::KEYSHIDDEN_NO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001481 } else if (strcmp(name, "keyshidden") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001482 mask = ResTable_config::MASK_KEYSHIDDEN;
1483 value = ResTable_config::KEYSHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484 } else if (strcmp(name, "keyssoft") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001485 mask = ResTable_config::MASK_KEYSHIDDEN;
1486 value = ResTable_config::KEYSHIDDEN_SOFT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487 }
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
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001517bool 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) {
Kenny Roote599f782010-02-19 12:45:48 -08001523 mask = ResTable_config::MASK_NAVHIDDEN;
1524 value = ResTable_config::NAVHIDDEN_ANY;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001525 } else if (strcmp(name, "navexposed") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001526 mask = ResTable_config::MASK_NAVHIDDEN;
1527 value = ResTable_config::NAVHIDDEN_NO;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001528 } else if (strcmp(name, "navhidden") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001529 mask = ResTable_config::MASK_NAVHIDDEN;
1530 value = ResTable_config::NAVHIDDEN_YES;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001531 }
1532
1533 if (mask != 0) {
1534 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1535 return true;
1536 }
1537
1538 return false;
1539}
1540
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001541bool 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
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001564bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001565{
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
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001599bool 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
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001624bool 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)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001671{
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);
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001703 if (v == 0) v = layoutDirection.compare(o.layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001704 if (v == 0) v = vendor.compare(o.vendor);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001705 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001706 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1707 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001708 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1709 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001710 if (v == 0) v = orientation.compare(o.orientation);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001711 if (v == 0) v = uiModeType.compare(o.uiModeType);
1712 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 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);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001717 if (v == 0) v = navHidden.compare(o.navHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001718 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 Kamath788fa412014-01-21 15:32:36 +00001724const ResTable_config AaptGroupEntry::toParams() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001725{
Dianne Hackborne6b68032011-10-13 16:26:02 -07001726 if (!mParamsChanged) {
1727 return mParams;
1728 }
1729
1730 mParamsChanged = false;
Narayan Kamath788fa412014-01-21 15:32:36 +00001731 ResTable_config& params = mParams;
1732 memset(&params, 0, sizeof(ResTable_config));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 getMccName(mcc.string(), &params);
1734 getMncName(mnc.string(), &params);
Narayan Kamath788fa412014-01-21 15:32:36 +00001735 locale.writeTo(&params);
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001736 getLayoutDirectionName(layoutDirection.string(), &params);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001737 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001738 getScreenWidthDpName(screenWidthDp.string(), &params);
1739 getScreenHeightDpName(screenHeightDp.string(), &params);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001740 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1741 getScreenLayoutLongName(screenLayoutLong.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001742 getOrientationName(orientation.string(), &params);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001743 getUiModeTypeName(uiModeType.string(), &params);
1744 getUiModeNightName(uiModeNight.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001745 getDensityName(density.string(), &params);
1746 getTouchscreenName(touchscreen.string(), &params);
1747 getKeysHiddenName(keysHidden.string(), &params);
1748 getKeyboardName(keyboard.string(), &params);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001749 getNavHiddenName(navHidden.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 getNavigationName(navigation.string(), &params);
1751 getScreenSizeName(screenSize.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 getVersionName(version.string(), &params);
Dianne Hackbornef05e072010-03-01 17:43:39 -08001753
1754 // Fix up version number based on specified parameters.
1755 int minSdk = 0;
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001756 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1757 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001758 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001759 minSdk = SDK_HONEYCOMB_MR2;
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001760 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
Dianne Hackbornef05e072010-03-01 17:43:39 -08001761 != 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001777 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
1801void* AaptFile::editData(size_t* outSize)
1802{
1803 if (outSize) {
1804 *outSize = mDataSize;
1805 }
1806 return mData;
1807}
1808
1809void* AaptFile::padData(size_t wordSize)
1810{
1811 const size_t extra = mDataSize%wordSize;
1812 if (extra == 0) {
1813 return mData;
1814 }
1815
1816 size_t initial = mDataSize;
1817 void* data = editData(initial+(wordSize-extra));
1818 if (data != NULL) {
1819 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1820 }
1821 return data;
1822}
1823
1824status_t AaptFile::writeData(const void* data, size_t size)
1825{
1826 size_t end = mDataSize;
1827 size_t total = size + end;
1828 void* buf = editData(total);
1829 if (buf == NULL) {
1830 return UNKNOWN_ERROR;
1831 }
1832 memcpy(((char*)buf)+end, data, size);
1833 return NO_ERROR;
1834}
1835
1836void AaptFile::clearData()
1837{
1838 if (mData != NULL) free(mData);
1839 mData = NULL;
1840 mDataSize = 0;
1841 mBufferSize = 0;
1842}
1843
1844String8 AaptFile::getPrintableSource() const
1845{
1846 if (hasData()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001847 String8 name(mGroupEntry.toDirName(String8()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 name.appendPath(mPath);
1849 name.append(" #generated");
1850 return name;
1851 }
1852 return mSourceFile;
1853}
1854
1855// =========================================================================
1856// =========================================================================
1857// =========================================================================
1858
1859status_t AaptGroup::addFile(const sp<AaptFile>& file)
1860{
1861 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1862 file->mPath = mPath;
1863 mFiles.add(file->getGroupEntry(), file);
1864 return NO_ERROR;
1865 }
1866
Dianne Hackborne6b68032011-10-13 16:26:02 -07001867#if 0
1868 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1869 file->getSourceFile().string(),
1870 file->getGroupEntry().toDirName(String8()).string(),
1871 mLeaf.string(), mPath.string());
1872#endif
1873
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001874 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1875 getPrintableSource().string());
1876 return UNKNOWN_ERROR;
1877}
1878
1879void AaptGroup::removeFile(size_t index)
1880{
1881 mFiles.removeItemsAt(index);
1882}
1883
Dianne Hackborne6b68032011-10-13 16:26:02 -07001884void AaptGroup::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885{
Dianne Hackborne6b68032011-10-13 16:26:02 -07001886 printf("%s%s\n", prefix.string(), getPath().string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001887 const size_t N=mFiles.size();
1888 size_t i;
1889 for (i=0; i<N; i++) {
1890 sp<AaptFile> file = mFiles.valueAt(i);
1891 const AaptGroupEntry& e = file->getGroupEntry();
1892 if (file->hasData()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001893 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001894 (int)file->getSize());
1895 } else {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001896 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1897 file->getPrintableSource().string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001898 }
Dianne Hackborne6b68032011-10-13 16:26:02 -07001899 //printf("%s File Group Entry: %s\n", prefix.string(),
1900 // file->getGroupEntry().toDirName(String8()).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001901 }
1902}
1903
1904String8 AaptGroup::getPrintableSource() const
1905{
1906 if (mFiles.size() > 0) {
1907 // Arbitrarily pull the first source file out of the list.
1908 return mFiles.valueAt(0)->getPrintableSource();
1909 }
1910
1911 // Should never hit this case, but to be safe...
1912 return getPath();
1913
1914}
1915
1916// =========================================================================
1917// =========================================================================
1918// =========================================================================
1919
1920status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1921{
1922 if (mFiles.indexOfKey(name) >= 0) {
1923 return ALREADY_EXISTS;
1924 }
1925 mFiles.add(name, file);
1926 return NO_ERROR;
1927}
1928
1929status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1930{
1931 if (mDirs.indexOfKey(name) >= 0) {
1932 return ALREADY_EXISTS;
1933 }
1934 mDirs.add(name, dir);
1935 return NO_ERROR;
1936}
1937
1938sp<AaptDir> AaptDir::makeDir(const String8& path)
1939{
1940 String8 name;
1941 String8 remain = path;
1942
1943 sp<AaptDir> subdir = this;
1944 while (name = remain.walkPath(&remain), remain != "") {
1945 subdir = subdir->makeDir(name);
1946 }
1947
1948 ssize_t i = subdir->mDirs.indexOfKey(name);
1949 if (i >= 0) {
1950 return subdir->mDirs.valueAt(i);
1951 }
1952 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1953 subdir->mDirs.add(name, dir);
1954 return dir;
1955}
1956
1957void AaptDir::removeFile(const String8& name)
1958{
1959 mFiles.removeItem(name);
1960}
1961
1962void AaptDir::removeDir(const String8& name)
1963{
1964 mDirs.removeItem(name);
1965}
1966
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001967status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1968{
1969 sp<AaptGroup> group;
1970 if (mFiles.indexOfKey(leafName) >= 0) {
1971 group = mFiles.valueFor(leafName);
1972 } else {
1973 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1974 mFiles.add(leafName, group);
1975 }
1976
1977 return group->addFile(file);
1978}
1979
1980ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001981 const AaptGroupEntry& kind, const String8& resType,
1982 sp<FilePathStore>& fullResPaths)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001983{
1984 Vector<String8> fileNames;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001985 {
1986 DIR* dir = NULL;
1987
1988 dir = opendir(srcDir.string());
1989 if (dir == NULL) {
1990 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1991 return UNKNOWN_ERROR;
1992 }
1993
1994 /*
1995 * Slurp the filenames out of the directory.
1996 */
1997 while (1) {
1998 struct dirent* entry;
1999
2000 entry = readdir(dir);
2001 if (entry == NULL)
2002 break;
2003
2004 if (isHidden(srcDir.string(), entry->d_name))
2005 continue;
2006
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002007 String8 name(entry->d_name);
2008 fileNames.add(name);
2009 // Add fully qualified path for dependency purposes
2010 // if we're collecting them
2011 if (fullResPaths != NULL) {
2012 fullResPaths->add(srcDir.appendPathCopy(name));
2013 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002014 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002015 closedir(dir);
2016 }
2017
2018 ssize_t count = 0;
2019
2020 /*
2021 * Stash away the files and recursively descend into subdirectories.
2022 */
2023 const size_t N = fileNames.size();
2024 size_t i;
2025 for (i = 0; i < N; i++) {
2026 String8 pathName(srcDir);
2027 FileType type;
2028
2029 pathName.appendPath(fileNames[i].string());
2030 type = getFileType(pathName.string());
2031 if (type == kFileTypeDirectory) {
2032 sp<AaptDir> subdir;
2033 bool notAdded = false;
2034 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
2035 subdir = mDirs.valueFor(fileNames[i]);
2036 } else {
2037 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
2038 notAdded = true;
2039 }
2040 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002041 resType, fullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002042 if (res < NO_ERROR) {
2043 return res;
2044 }
2045 if (res > 0 && notAdded) {
2046 mDirs.add(fileNames[i], subdir);
2047 }
2048 count += res;
2049 } else if (type == kFileTypeRegular) {
2050 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
2051 status_t err = addLeafFile(fileNames[i], file);
2052 if (err != NO_ERROR) {
2053 return err;
2054 }
2055
2056 count++;
2057
2058 } else {
2059 if (bundle->getVerbose())
2060 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
2061 }
2062 }
2063
2064 return count;
2065}
2066
2067status_t AaptDir::validate() const
2068{
2069 const size_t NF = mFiles.size();
2070 const size_t ND = mDirs.size();
2071 size_t i;
2072 for (i = 0; i < NF; i++) {
2073 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
2074 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2075 "Invalid filename. Unable to add.");
2076 return UNKNOWN_ERROR;
2077 }
2078
2079 size_t j;
2080 for (j = i+1; j < NF; j++) {
2081 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2082 mFiles.valueAt(j)->getLeaf().string()) == 0) {
2083 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2084 "File is case-insensitive equivalent to: %s",
2085 mFiles.valueAt(j)->getPrintableSource().string());
2086 return UNKNOWN_ERROR;
2087 }
2088
2089 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
2090 // (this is mostly caught by the "marked" stuff, below)
2091 }
2092
2093 for (j = 0; j < ND; j++) {
2094 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2095 mDirs.valueAt(j)->getLeaf().string()) == 0) {
2096 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2097 "File conflicts with dir from: %s",
2098 mDirs.valueAt(j)->getPrintableSource().string());
2099 return UNKNOWN_ERROR;
2100 }
2101 }
2102 }
2103
2104 for (i = 0; i < ND; i++) {
2105 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
2106 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2107 "Invalid directory name, unable to add.");
2108 return UNKNOWN_ERROR;
2109 }
2110
2111 size_t j;
2112 for (j = i+1; j < ND; j++) {
2113 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
2114 mDirs.valueAt(j)->getLeaf().string()) == 0) {
2115 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2116 "Directory is case-insensitive equivalent to: %s",
2117 mDirs.valueAt(j)->getPrintableSource().string());
2118 return UNKNOWN_ERROR;
2119 }
2120 }
2121
2122 status_t err = mDirs.valueAt(i)->validate();
2123 if (err != NO_ERROR) {
2124 return err;
2125 }
2126 }
2127
2128 return NO_ERROR;
2129}
2130
Dianne Hackborne6b68032011-10-13 16:26:02 -07002131void AaptDir::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002132{
2133 const size_t ND=getDirs().size();
2134 size_t i;
2135 for (i=0; i<ND; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002136 getDirs().valueAt(i)->print(prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002137 }
2138
2139 const size_t NF=getFiles().size();
2140 for (i=0; i<NF; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002141 getFiles().valueAt(i)->print(prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002142 }
2143}
2144
2145String8 AaptDir::getPrintableSource() const
2146{
2147 if (mFiles.size() > 0) {
2148 // Arbitrarily pull the first file out of the list as the source dir.
2149 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
2150 }
2151 if (mDirs.size() > 0) {
2152 // Or arbitrarily pull the first dir out of the list as the source dir.
2153 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
2154 }
2155
2156 // Should never hit this case, but to be safe...
2157 return mPath;
2158
2159}
2160
2161// =========================================================================
2162// =========================================================================
2163// =========================================================================
2164
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08002165status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
2166{
2167 status_t err = NO_ERROR;
2168 size_t N = javaSymbols->mSymbols.size();
2169 for (size_t i=0; i<N; i++) {
2170 const String8& name = javaSymbols->mSymbols.keyAt(i);
2171 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
2172 ssize_t pos = mSymbols.indexOfKey(name);
2173 if (pos < 0) {
2174 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
2175 err = UNKNOWN_ERROR;
2176 continue;
2177 }
2178 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
2179 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
2180 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
2181 }
2182
2183 N = javaSymbols->mNestedSymbols.size();
2184 for (size_t i=0; i<N; i++) {
2185 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
2186 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
2187 ssize_t pos = mNestedSymbols.indexOfKey(name);
2188 if (pos < 0) {
2189 SourcePos pos;
2190 pos.error("Java symbol dir %s not defined\n", name.string());
2191 err = UNKNOWN_ERROR;
2192 continue;
2193 }
2194 //printf("**** applying java symbols in dir %s\n", name.string());
2195 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2196 if (myerr != NO_ERROR) {
2197 err = myerr;
2198 }
2199 }
2200
2201 return err;
2202}
2203
2204// =========================================================================
2205// =========================================================================
2206// =========================================================================
2207
Dianne Hackborne6b68032011-10-13 16:26:02 -07002208AaptAssets::AaptAssets()
2209 : AaptDir(String8(), String8()),
Narayan Kamath788fa412014-01-21 15:32:36 +00002210 mHavePrivateSymbols(false),
2211 mChanged(false), mHaveIncludedAssets(false),
2212 mRes(NULL)
Dianne Hackborne6b68032011-10-13 16:26:02 -07002213{
2214}
2215
2216const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
2217 if (mChanged) {
2218 }
2219 return mGroupEntries;
2220}
2221
2222status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
2223{
2224 mChanged = true;
2225 return AaptDir::addFile(name, file);
2226}
2227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002228sp<AaptFile> AaptAssets::addFile(
2229 const String8& filePath, const AaptGroupEntry& entry,
2230 const String8& srcDir, sp<AaptGroup>* outGroup,
2231 const String8& resType)
2232{
2233 sp<AaptDir> dir = this;
2234 sp<AaptGroup> group;
2235 sp<AaptFile> file;
2236 String8 root, remain(filePath), partialPath;
2237 while (remain.length() > 0) {
2238 root = remain.walkPath(&remain);
2239 partialPath.appendPath(root);
2240
2241 const String8 rootStr(root);
2242
2243 if (remain.length() == 0) {
2244 ssize_t i = dir->getFiles().indexOfKey(rootStr);
2245 if (i >= 0) {
2246 group = dir->getFiles().valueAt(i);
2247 } else {
2248 group = new AaptGroup(rootStr, filePath);
2249 status_t res = dir->addFile(rootStr, group);
2250 if (res != NO_ERROR) {
2251 return NULL;
2252 }
2253 }
2254 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2255 status_t res = group->addFile(file);
2256 if (res != NO_ERROR) {
2257 return NULL;
2258 }
2259 break;
2260
2261 } else {
2262 ssize_t i = dir->getDirs().indexOfKey(rootStr);
2263 if (i >= 0) {
2264 dir = dir->getDirs().valueAt(i);
2265 } else {
2266 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2267 status_t res = dir->addDir(rootStr, subdir);
2268 if (res != NO_ERROR) {
2269 return NULL;
2270 }
2271 dir = subdir;
2272 }
2273 }
2274 }
2275
2276 mGroupEntries.add(entry);
2277 if (outGroup) *outGroup = group;
2278 return file;
2279}
2280
2281void AaptAssets::addResource(const String8& leafName, const String8& path,
2282 const sp<AaptFile>& file, const String8& resType)
2283{
2284 sp<AaptDir> res = AaptDir::makeDir(kResString);
2285 String8 dirname = file->getGroupEntry().toDirName(resType);
2286 sp<AaptDir> subdir = res->makeDir(dirname);
2287 sp<AaptGroup> grr = new AaptGroup(leafName, path);
2288 grr->addFile(file);
2289
2290 subdir->addFile(leafName, grr);
2291}
2292
2293
2294ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2295{
2296 int count;
2297 int totalCount = 0;
2298 FileType type;
2299 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2300 const size_t dirCount =resDirs.size();
2301 sp<AaptAssets> current = this;
2302
2303 const int N = bundle->getFileSpecCount();
2304
2305 /*
2306 * If a package manifest was specified, include that first.
2307 */
2308 if (bundle->getAndroidManifestFile() != NULL) {
2309 // place at root of zip.
2310 String8 srcFile(bundle->getAndroidManifestFile());
2311 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2312 NULL, String8());
2313 totalCount++;
2314 }
2315
2316 /*
2317 * If a directory of custom assets was supplied, slurp 'em up.
2318 */
2319 if (bundle->getAssetSourceDir()) {
2320 const char* assetDir = bundle->getAssetSourceDir();
2321
2322 FileType type = getFileType(assetDir);
2323 if (type == kFileTypeNonexistent) {
2324 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
2325 return UNKNOWN_ERROR;
2326 }
2327 if (type != kFileTypeDirectory) {
2328 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2329 return UNKNOWN_ERROR;
2330 }
2331
2332 String8 assetRoot(assetDir);
2333 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2334 AaptGroupEntry group;
2335 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002336 String8(), mFullAssetPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002337 if (count < 0) {
2338 totalCount = count;
2339 goto bail;
2340 }
2341 if (count > 0) {
2342 mGroupEntries.add(group);
2343 }
2344 totalCount += count;
2345
2346 if (bundle->getVerbose())
2347 printf("Found %d custom asset file%s in %s\n",
2348 count, (count==1) ? "" : "s", assetDir);
2349 }
2350
2351 /*
2352 * If a directory of resource-specific assets was supplied, slurp 'em up.
2353 */
2354 for (size_t i=0; i<dirCount; i++) {
2355 const char *res = resDirs[i];
2356 if (res) {
2357 type = getFileType(res);
2358 if (type == kFileTypeNonexistent) {
2359 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2360 return UNKNOWN_ERROR;
2361 }
2362 if (type == kFileTypeDirectory) {
2363 if (i>0) {
2364 sp<AaptAssets> nextOverlay = new AaptAssets();
2365 current->setOverlay(nextOverlay);
2366 current = nextOverlay;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002367 current->setFullResPaths(mFullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002368 }
2369 count = current->slurpResourceTree(bundle, String8(res));
2370
2371 if (count < 0) {
2372 totalCount = count;
2373 goto bail;
2374 }
2375 totalCount += count;
2376 }
2377 else {
2378 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2379 return UNKNOWN_ERROR;
2380 }
2381 }
2382
2383 }
2384 /*
2385 * Now do any additional raw files.
2386 */
2387 for (int arg=0; arg<N; arg++) {
2388 const char* assetDir = bundle->getFileSpecEntry(arg);
2389
2390 FileType type = getFileType(assetDir);
2391 if (type == kFileTypeNonexistent) {
2392 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2393 return UNKNOWN_ERROR;
2394 }
2395 if (type != kFileTypeDirectory) {
2396 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2397 return UNKNOWN_ERROR;
2398 }
2399
2400 String8 assetRoot(assetDir);
2401
2402 if (bundle->getVerbose())
2403 printf("Processing raw dir '%s'\n", (const char*) assetDir);
2404
2405 /*
2406 * Do a recursive traversal of subdir tree. We don't make any
2407 * guarantees about ordering, so we're okay with an inorder search
2408 * using whatever order the OS happens to hand back to us.
2409 */
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002410 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002411 if (count < 0) {
2412 /* failure; report error and remove archive */
2413 totalCount = count;
2414 goto bail;
2415 }
2416 totalCount += count;
2417
2418 if (bundle->getVerbose())
2419 printf("Found %d asset file%s in %s\n",
2420 count, (count==1) ? "" : "s", assetDir);
2421 }
2422
2423 count = validate();
2424 if (count != NO_ERROR) {
2425 totalCount = count;
2426 goto bail;
2427 }
2428
Dianne Hackborne6b68032011-10-13 16:26:02 -07002429 count = filter(bundle);
2430 if (count != NO_ERROR) {
2431 totalCount = count;
2432 goto bail;
2433 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002434
2435bail:
2436 return totalCount;
2437}
2438
2439ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2440 const AaptGroupEntry& kind,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002441 const String8& resType,
2442 sp<FilePathStore>& fullResPaths)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002443{
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002444 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002445 if (res > 0) {
2446 mGroupEntries.add(kind);
2447 }
2448
2449 return res;
2450}
2451
2452ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2453{
2454 ssize_t err = 0;
2455
2456 DIR* dir = opendir(srcDir.string());
2457 if (dir == NULL) {
2458 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2459 return UNKNOWN_ERROR;
2460 }
2461
2462 status_t count = 0;
2463
2464 /*
2465 * Run through the directory, looking for dirs that match the
2466 * expected pattern.
2467 */
2468 while (1) {
2469 struct dirent* entry = readdir(dir);
2470 if (entry == NULL) {
2471 break;
2472 }
2473
2474 if (isHidden(srcDir.string(), entry->d_name)) {
2475 continue;
2476 }
2477
2478 String8 subdirName(srcDir);
2479 subdirName.appendPath(entry->d_name);
2480
2481 AaptGroupEntry group;
2482 String8 resType;
2483 bool b = group.initFromDirName(entry->d_name, &resType);
2484 if (!b) {
2485 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2486 entry->d_name);
2487 err = -1;
2488 continue;
2489 }
2490
Dianne Hackborne6b68032011-10-13 16:26:02 -07002491 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07002492 int maxResInt = atoi(bundle->getMaxResVersion());
Dianne Hackborne6b68032011-10-13 16:26:02 -07002493 const char *verString = group.getVersionString().string();
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07002494 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2495 if (dirVersionInt > maxResInt) {
2496 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2497 continue;
2498 }
2499 }
2500
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002501 FileType type = getFileType(subdirName.string());
2502
2503 if (type == kFileTypeDirectory) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002504 sp<AaptDir> dir = makeDir(resType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002505 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002506 resType, mFullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002507 if (res < 0) {
2508 count = res;
2509 goto bail;
2510 }
2511 if (res > 0) {
2512 mGroupEntries.add(group);
2513 count += res;
2514 }
2515
Dianne Hackborne6b68032011-10-13 16:26:02 -07002516 // Only add this directory if we don't already have a resource dir
2517 // for the current type. This ensures that we only add the dir once
2518 // for all configs.
2519 sp<AaptDir> rdir = resDir(resType);
2520 if (rdir == NULL) {
2521 mResDirs.add(dir);
2522 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002523 } else {
2524 if (bundle->getVerbose()) {
2525 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
2526 }
2527 }
2528 }
2529
2530bail:
2531 closedir(dir);
2532 dir = NULL;
2533
2534 if (err != 0) {
2535 return err;
2536 }
2537 return count;
2538}
2539
2540ssize_t
2541AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2542{
2543 int count = 0;
2544 SortedVector<AaptGroupEntry> entries;
2545
2546 ZipFile* zip = new ZipFile;
2547 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2548 if (err != NO_ERROR) {
2549 fprintf(stderr, "error opening zip file %s\n", filename);
2550 count = err;
2551 delete zip;
2552 return -1;
2553 }
2554
2555 const int N = zip->getNumEntries();
2556 for (int i=0; i<N; i++) {
2557 ZipEntry* entry = zip->getEntryByIndex(i);
2558 if (entry->getDeleted()) {
2559 continue;
2560 }
2561
2562 String8 entryName(entry->getFileName());
2563
2564 String8 dirName = entryName.getPathDir();
2565 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2566
2567 String8 resType;
2568 AaptGroupEntry kind;
2569
2570 String8 remain;
2571 if (entryName.walkPath(&remain) == kResourceDir) {
2572 // these are the resources, pull their type out of the directory name
2573 kind.initFromDirName(remain.walkPath().string(), &resType);
2574 } else {
2575 // these are untyped and don't have an AaptGroupEntry
2576 }
2577 if (entries.indexOf(kind) < 0) {
2578 entries.add(kind);
2579 mGroupEntries.add(kind);
2580 }
2581
2582 // use the one from the zip file if they both exist.
2583 dir->removeFile(entryName.getPathLeaf());
2584
2585 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2586 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2587 if (err != NO_ERROR) {
2588 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2589 count = err;
2590 goto bail;
2591 }
2592 file->setCompressionMethod(entry->getCompressionMethod());
2593
2594#if 0
2595 if (entryName == "AndroidManifest.xml") {
2596 printf("AndroidManifest.xml\n");
2597 }
2598 printf("\n\nfile: %s\n", entryName.string());
2599#endif
2600
2601 size_t len = entry->getUncompressedLen();
2602 void* data = zip->uncompress(entry);
2603 void* buf = file->editData(len);
2604 memcpy(buf, data, len);
2605
2606#if 0
2607 const int OFF = 0;
2608 const unsigned char* p = (unsigned char*)data;
2609 const unsigned char* end = p+len;
2610 p += OFF;
2611 for (int i=0; i<32 && p < end; i++) {
2612 printf("0x%03x ", i*0x10 + OFF);
2613 for (int j=0; j<0x10 && p < end; j++) {
2614 printf(" %02x", *p);
2615 p++;
2616 }
2617 printf("\n");
2618 }
2619#endif
2620
2621 free(data);
2622
2623 count++;
2624 }
2625
2626bail:
2627 delete zip;
2628 return count;
2629}
2630
Dianne Hackborne6b68032011-10-13 16:26:02 -07002631status_t AaptAssets::filter(Bundle* bundle)
2632{
2633 ResourceFilter reqFilter;
2634 status_t err = reqFilter.parse(bundle->getConfigurations());
2635 if (err != NO_ERROR) {
2636 return err;
2637 }
2638
2639 ResourceFilter prefFilter;
2640 err = prefFilter.parse(bundle->getPreferredConfigurations());
2641 if (err != NO_ERROR) {
2642 return err;
2643 }
2644
2645 if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2646 return NO_ERROR;
2647 }
2648
Dianne Hackbornbd9d2bc2011-10-16 14:17:07 -07002649 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002650 if (!reqFilter.isEmpty()) {
2651 printf("Applying required filter: %s\n",
2652 bundle->getConfigurations());
2653 }
2654 if (!prefFilter.isEmpty()) {
2655 printf("Applying preferred filter: %s\n",
2656 bundle->getPreferredConfigurations());
2657 }
2658 }
2659
2660 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2661 const size_t ND = resdirs.size();
2662 for (size_t i=0; i<ND; i++) {
2663 const sp<AaptDir>& dir = resdirs.itemAt(i);
2664 if (dir->getLeaf() == kValuesDir) {
2665 // The "value" dir is special since a single file defines
2666 // multiple resources, so we can not do filtering on the
2667 // files themselves.
2668 continue;
2669 }
2670 if (dir->getLeaf() == kMipmapDir) {
2671 // We also skip the "mipmap" directory, since the point of this
2672 // is to include all densities without stripping. If you put
2673 // other configurations in here as well they won't be stripped
2674 // either... So don't do that. Seriously. What is wrong with you?
2675 continue;
2676 }
2677
2678 const size_t NG = dir->getFiles().size();
2679 for (size_t j=0; j<NG; j++) {
2680 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2681
2682 // First remove any configurations we know we don't need.
2683 for (size_t k=0; k<grp->getFiles().size(); k++) {
2684 sp<AaptFile> file = grp->getFiles().valueAt(k);
2685 if (k == 0 && grp->getFiles().size() == 1) {
2686 // If this is the only file left, we need to keep it.
2687 // Otherwise the resource IDs we are using will be inconsistent
2688 // with what we get when not stripping. Sucky, but at least
2689 // for now we can rely on the back-end doing another filtering
2690 // pass to take this out and leave us with this resource name
2691 // containing no entries.
2692 continue;
2693 }
2694 if (file->getPath().getPathExtension() == ".xml") {
2695 // We can't remove .xml files at this point, because when
2696 // we parse them they may add identifier resources, so
2697 // removing them can cause our resource identifiers to
2698 // become inconsistent.
2699 continue;
2700 }
2701 const ResTable_config& config(file->getGroupEntry().toParams());
2702 if (!reqFilter.match(config)) {
2703 if (bundle->getVerbose()) {
2704 printf("Pruning unneeded resource: %s\n",
2705 file->getPrintableSource().string());
2706 }
2707 grp->removeFile(k);
2708 k--;
2709 }
2710 }
2711
2712 // Quick check: no preferred filters, nothing more to do.
2713 if (prefFilter.isEmpty()) {
2714 continue;
2715 }
2716
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002717 // Get the preferred density if there is one. We do not match exactly for density.
2718 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2719 // pick xhdpi.
2720 uint32_t preferredDensity = 0;
Narayan Kamath788fa412014-01-21 15:32:36 +00002721 const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002722 if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
Narayan Kamath788fa412014-01-21 15:32:36 +00002723 preferredDensity = (*preferredConfigs)[0].intValue;
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002724 }
2725
Dianne Hackborne6b68032011-10-13 16:26:02 -07002726 // Now deal with preferred configurations.
2727 for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2728 for (size_t k=0; k<grp->getFiles().size(); k++) {
2729 sp<AaptFile> file = grp->getFiles().valueAt(k);
2730 if (k == 0 && grp->getFiles().size() == 1) {
2731 // If this is the only file left, we need to keep it.
2732 // Otherwise the resource IDs we are using will be inconsistent
2733 // with what we get when not stripping. Sucky, but at least
2734 // for now we can rely on the back-end doing another filtering
2735 // pass to take this out and leave us with this resource name
2736 // containing no entries.
2737 continue;
2738 }
2739 if (file->getPath().getPathExtension() == ".xml") {
2740 // We can't remove .xml files at this point, because when
2741 // we parse them they may add identifier resources, so
2742 // removing them can cause our resource identifiers to
2743 // become inconsistent.
2744 continue;
2745 }
2746 const ResTable_config& config(file->getGroupEntry().toParams());
2747 if (!prefFilter.match(axis, config)) {
2748 // This is a resource we would prefer not to have. Check
2749 // to see if have a similar variation that we would like
2750 // to have and, if so, we can drop it.
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002751
2752 uint32_t bestDensity = config.density;
2753
Dianne Hackborne6b68032011-10-13 16:26:02 -07002754 for (size_t m=0; m<grp->getFiles().size(); m++) {
2755 if (m == k) continue;
2756 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2757 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2758 if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002759 if (axis == AXIS_DENSITY && preferredDensity > 0) {
2760 // See if there is a better density resource
2761 if (mconfig.density < bestDensity &&
2762 mconfig.density > preferredDensity &&
2763 bestDensity > preferredDensity) {
2764 // This density is between our best density and
2765 // the preferred density, therefore it is better.
2766 bestDensity = mconfig.density;
2767 } else if (mconfig.density > bestDensity &&
2768 bestDensity < preferredDensity) {
2769 // This density is better than our best density and
2770 // our best density was smaller than our preferred
2771 // density, so it is better.
2772 bestDensity = mconfig.density;
2773 }
2774 } else if (prefFilter.match(axis, mconfig)) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002775 if (bundle->getVerbose()) {
2776 printf("Pruning unneeded resource: %s\n",
2777 file->getPrintableSource().string());
2778 }
2779 grp->removeFile(k);
2780 k--;
2781 break;
2782 }
2783 }
2784 }
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002785
2786 if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2787 bestDensity != config.density) {
2788 if (bundle->getVerbose()) {
2789 printf("Pruning unneeded resource: %s\n",
2790 file->getPrintableSource().string());
2791 }
2792 grp->removeFile(k);
2793 k--;
2794 }
Dianne Hackborne6b68032011-10-13 16:26:02 -07002795 }
2796 }
2797 }
2798 }
2799 }
2800
2801 return NO_ERROR;
2802}
2803
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002804sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2805{
2806 sp<AaptSymbols> sym = mSymbols.valueFor(name);
2807 if (sym == NULL) {
2808 sym = new AaptSymbols();
2809 mSymbols.add(name, sym);
2810 }
2811 return sym;
2812}
2813
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08002814sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2815{
2816 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2817 if (sym == NULL) {
2818 sym = new AaptSymbols();
2819 mJavaSymbols.add(name, sym);
2820 }
2821 return sym;
2822}
2823
2824status_t AaptAssets::applyJavaSymbols()
2825{
2826 size_t N = mJavaSymbols.size();
2827 for (size_t i=0; i<N; i++) {
2828 const String8& name = mJavaSymbols.keyAt(i);
2829 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2830 ssize_t pos = mSymbols.indexOfKey(name);
2831 if (pos < 0) {
2832 SourcePos pos;
2833 pos.error("Java symbol dir %s not defined\n", name.string());
2834 return UNKNOWN_ERROR;
2835 }
2836 //printf("**** applying java symbols in dir %s\n", name.string());
2837 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2838 if (err != NO_ERROR) {
2839 return err;
2840 }
2841 }
2842
2843 return NO_ERROR;
2844}
2845
2846bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2847 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2848 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2849 // sym.isJavaSymbol ? 1 : 0);
2850 if (!mHavePrivateSymbols) return true;
2851 if (sym.isPublic) return true;
2852 if (includePrivate && sym.isJavaSymbol) return true;
2853 return false;
2854}
2855
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002856status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2857{
2858 if (!mHaveIncludedAssets) {
2859 // Add in all includes.
2860 const Vector<const char*>& incl = bundle->getPackageIncludes();
2861 const size_t N=incl.size();
2862 for (size_t i=0; i<N; i++) {
2863 if (bundle->getVerbose())
2864 printf("Including resources from package: %s\n", incl[i]);
2865 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2866 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2867 incl[i]);
2868 return UNKNOWN_ERROR;
2869 }
2870 }
2871 mHaveIncludedAssets = true;
2872 }
2873
2874 return NO_ERROR;
2875}
2876
2877status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2878{
2879 const ResTable& res = getIncludedResources();
2880 // XXX dirty!
Narayan Kamath7c4887f2014-01-27 17:32:37 +00002881 return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002882}
2883
2884const ResTable& AaptAssets::getIncludedResources() const
2885{
2886 return mIncludedAssets.getResources(false);
2887}
2888
Dianne Hackborne6b68032011-10-13 16:26:02 -07002889void AaptAssets::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002890{
Dianne Hackborne6b68032011-10-13 16:26:02 -07002891 String8 innerPrefix(prefix);
2892 innerPrefix.append(" ");
2893 String8 innerInnerPrefix(innerPrefix);
2894 innerInnerPrefix.append(" ");
2895 printf("%sConfigurations:\n", prefix.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002896 const size_t N=mGroupEntries.size();
2897 for (size_t i=0; i<N; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002898 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2899 printf("%s %s\n", prefix.string(),
2900 cname != "" ? cname.string() : "(default)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002901 }
2902
Dianne Hackborne6b68032011-10-13 16:26:02 -07002903 printf("\n%sFiles:\n", prefix.string());
2904 AaptDir::print(innerPrefix);
2905
2906 printf("\n%sResource Dirs:\n", prefix.string());
2907 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2908 const size_t NR = resdirs.size();
2909 for (size_t i=0; i<NR; i++) {
2910 const sp<AaptDir>& d = resdirs.itemAt(i);
2911 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
2912 d->print(innerInnerPrefix);
2913 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002914}
2915
Dianne Hackborne6b68032011-10-13 16:26:02 -07002916sp<AaptDir> AaptAssets::resDir(const String8& name) const
Joe Onorato1553c822009-08-30 13:36:22 -07002917{
Dianne Hackborne6b68032011-10-13 16:26:02 -07002918 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2919 const size_t N = resdirs.size();
Joe Onorato1553c822009-08-30 13:36:22 -07002920 for (size_t i=0; i<N; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002921 const sp<AaptDir>& d = resdirs.itemAt(i);
Joe Onorato1553c822009-08-30 13:36:22 -07002922 if (d->getLeaf() == name) {
2923 return d;
2924 }
2925 }
2926 return NULL;
2927}
2928
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002929bool
2930valid_symbol_name(const String8& symbol)
2931{
2932 static char const * const KEYWORDS[] = {
2933 "abstract", "assert", "boolean", "break",
2934 "byte", "case", "catch", "char", "class", "const", "continue",
2935 "default", "do", "double", "else", "enum", "extends", "final",
2936 "finally", "float", "for", "goto", "if", "implements", "import",
2937 "instanceof", "int", "interface", "long", "native", "new", "package",
2938 "private", "protected", "public", "return", "short", "static",
2939 "strictfp", "super", "switch", "synchronized", "this", "throw",
2940 "throws", "transient", "try", "void", "volatile", "while",
2941 "true", "false", "null",
2942 NULL
2943 };
2944 const char*const* k = KEYWORDS;
2945 const char*const s = symbol.string();
2946 while (*k) {
2947 if (0 == strcmp(s, *k)) {
2948 return false;
2949 }
2950 k++;
2951 }
2952 return true;
2953}