]> git.saurik.com Git - android/aapt.git/blame - AaptAssets.cpp
am fb34490f: Merge "Fix writing text version of styleable IDs."
[android/aapt.git] / AaptAssets.cpp
CommitLineData
a534180c
TAOSP
1//
2// Copyright 2006 The Android Open Source Project
3//
4
5#include "AaptAssets.h"
e29f4ada 6#include "ResourceFilter.h"
a534180c
TAOSP
7#include "Main.h"
8
9#include <utils/misc.h>
10#include <utils/SortedVector.h>
11
12#include <ctype.h>
13#include <dirent.h>
14#include <errno.h>
15
16static const char* kDefaultLocale = "default";
17static const char* kWildcardName = "any";
18static const char* kAssetDir = "assets";
19static const char* kResourceDir = "res";
e29f4ada
DH
20static const char* kValuesDir = "values";
21static const char* kMipmapDir = "mipmap";
a534180c
TAOSP
22static const char* kInvalidChars = "/\\:";
23static const size_t kMaxAssetFileName = 100;
24
25static const String8 kResString(kResourceDir);
26
27/*
28 * Names of asset files must meet the following criteria:
29 *
30 * - the filename length must be less than kMaxAssetFileName bytes long
31 * (and can't be empty)
32 * - all characters must be 7-bit printable ASCII
33 * - none of { '/' '\\' ':' }
34 *
35 * Pass in just the filename, not the full path.
36 */
37static bool validateFileName(const char* fileName)
38{
39 const char* cp = fileName;
40 size_t len = 0;
41
42 while (*cp != '\0') {
43 if ((*cp & 0x80) != 0)
44 return false; // reject high ASCII
45 if (*cp < 0x20 || *cp >= 0x7f)
46 return false; // reject control chars and 0x7f
47 if (strchr(kInvalidChars, *cp) != NULL)
48 return false; // reject path sep chars
49 cp++;
50 len++;
51 }
52
53 if (len < 1 || len > kMaxAssetFileName)
54 return false; // reject empty or too long
55
56 return true;
57}
58
d3535722
RM
59// The default to use if no other ignore pattern is defined.
60const char * const gDefaultIgnoreAssets =
f77a54f9 61 "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
d3535722
RM
62// The ignore pattern that can be passed via --ignore-assets in Main.cpp
63const char * gUserIgnoreAssets = NULL;
64
a534180c
TAOSP
65static bool isHidden(const char *root, const char *path)
66{
d3535722
RM
67 // Patterns syntax:
68 // - Delimiter is :
69 // - Entry can start with the flag ! to avoid printing a warning
70 // about the file being ignored.
71 // - Entry can have the flag "<dir>" to match only directories
72 // or <file> to match only files. Default is to match both.
73 // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
74 // where prefix/suffix must have at least 1 character (so that
75 // we don't match a '*' catch-all pattern.)
76 // - The special filenames "." and ".." are always ignored.
77 // - Otherwise the full string is matched.
78 // - match is not case-sensitive.
79
80 if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
81 return true;
82 }
83
84 const char *delim = ":";
85 const char *p = gUserIgnoreAssets;
86 if (!p || !p[0]) {
87 p = getenv("ANDROID_AAPT_IGNORE");
88 }
89 if (!p || !p[0]) {
90 p = gDefaultIgnoreAssets;
91 }
92 char *patterns = strdup(p);
93
94 bool ignore = false;
95 bool chatty = true;
96 char *matchedPattern = NULL;
97
98 String8 fullPath(root);
99 fullPath.appendPath(path);
100 FileType type = getFileType(fullPath);
101
102 int plen = strlen(path);
103
104 // Note: we don't have strtok_r under mingw.
105 for(char *token = strtok(patterns, delim);
106 !ignore && token != NULL;
107 token = strtok(NULL, delim)) {
108 chatty = token[0] != '!';
109 if (!chatty) token++; // skip !
110 if (strncasecmp(token, "<dir>" , 5) == 0) {
111 if (type != kFileTypeDirectory) continue;
112 token += 5;
a534180c 113 }
d3535722
RM
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++;
33e92afe 125 n--;
d3535722
RM
126 if (n <= plen) {
127 ignore = strncasecmp(token, path + plen - n, n) == 0;
128 }
129 } else if (n > 1 && token[n - 1] == '*') {
130 // Match prefix*
131 ignore = strncasecmp(token, path, n - 1) == 0;
132 } else {
133 ignore = strcasecmp(token, path) == 0;
a534180c 134 }
a534180c
TAOSP
135 }
136
d3535722
RM
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 }
a534180c 143
d3535722
RM
144 free(patterns);
145 return ignore;
a534180c
TAOSP
146}
147
148// =========================================================================
149// =========================================================================
150// =========================================================================
151
152status_t
153AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
154{
155 ResTable_config config;
156
157 // IMSI - MCC
158 if (getMccName(part.string(), &config)) {
159 *axis = AXIS_MCC;
160 *value = config.mcc;
161 return 0;
162 }
163
164 // IMSI - MNC
165 if (getMncName(part.string(), &config)) {
166 *axis = AXIS_MNC;
167 *value = config.mnc;
168 return 0;
169 }
170
171 // locale - language
172 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
173 *axis = AXIS_LANGUAGE;
174 *value = part[1] << 8 | part[0];
175 return 0;
176 }
177
178 // locale - language_REGION
179 if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
180 && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
181 *axis = AXIS_LANGUAGE;
182 *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
183 return 0;
184 }
185
f1f3915b
DH
186 // smallest screen dp width
187 if (getSmallestScreenWidthDpName(part.string(), &config)) {
188 *axis = AXIS_SMALLESTSCREENWIDTHDP;
189 *value = config.smallestScreenWidthDp;
784c6915
DH
190 return 0;
191 }
192
4ee37df2
DH
193 // screen dp width
194 if (getScreenWidthDpName(part.string(), &config)) {
195 *axis = AXIS_SCREENWIDTHDP;
196 *value = config.screenWidthDp;
197 return 0;
198 }
199
200 // screen dp height
201 if (getScreenHeightDpName(part.string(), &config)) {
202 *axis = AXIS_SCREENHEIGHTDP;
203 *value = config.screenHeightDp;
204 return 0;
205 }
206
f1f3915b
DH
207 // screen layout size
208 if (getScreenLayoutSizeName(part.string(), &config)) {
209 *axis = AXIS_SCREENLAYOUTSIZE;
210 *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
211 return 0;
212 }
213
214 // screen layout long
215 if (getScreenLayoutLongName(part.string(), &config)) {
216 *axis = AXIS_SCREENLAYOUTLONG;
217 *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
218 return 0;
219 }
220
a534180c
TAOSP
221 // orientation
222 if (getOrientationName(part.string(), &config)) {
223 *axis = AXIS_ORIENTATION;
224 *value = config.orientation;
225 return 0;
226 }
227
2ca01a37
TH
228 // ui mode type
229 if (getUiModeTypeName(part.string(), &config)) {
230 *axis = AXIS_UIMODETYPE;
231 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
232 return 0;
233 }
234
235 // ui mode night
236 if (getUiModeNightName(part.string(), &config)) {
237 *axis = AXIS_UIMODENIGHT;
238 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
239 return 0;
240 }
241
a534180c
TAOSP
242 // density
243 if (getDensityName(part.string(), &config)) {
244 *axis = AXIS_DENSITY;
245 *value = config.density;
246 return 0;
247 }
248
249 // touchscreen
250 if (getTouchscreenName(part.string(), &config)) {
251 *axis = AXIS_TOUCHSCREEN;
252 *value = config.touchscreen;
253 return 0;
254 }
255
256 // keyboard hidden
257 if (getKeysHiddenName(part.string(), &config)) {
258 *axis = AXIS_KEYSHIDDEN;
259 *value = config.inputFlags;
260 return 0;
261 }
262
263 // keyboard
264 if (getKeyboardName(part.string(), &config)) {
265 *axis = AXIS_KEYBOARD;
266 *value = config.keyboard;
267 return 0;
268 }
269
0096feb5
DH
270 // navigation hidden
271 if (getNavHiddenName(part.string(), &config)) {
272 *axis = AXIS_NAVHIDDEN;
273 *value = config.inputFlags;
274 return 0;
275 }
276
a534180c
TAOSP
277 // navigation
278 if (getNavigationName(part.string(), &config)) {
279 *axis = AXIS_NAVIGATION;
280 *value = config.navigation;
281 return 0;
282 }
283
284 // screen size
285 if (getScreenSizeName(part.string(), &config)) {
286 *axis = AXIS_SCREENSIZE;
287 *value = config.screenSize;
288 return 0;
289 }
290
291 // version
292 if (getVersionName(part.string(), &config)) {
293 *axis = AXIS_VERSION;
294 *value = config.version;
295 return 0;
296 }
297
298 return 1;
299}
300
e29f4ada
DH
301uint32_t
302AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
303{
304 switch (axis) {
305 case AXIS_MCC:
306 return config.mcc;
307 case AXIS_MNC:
308 return config.mnc;
309 case AXIS_LANGUAGE:
310 return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
311 | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
312 case AXIS_SCREENLAYOUTSIZE:
313 return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
314 case AXIS_ORIENTATION:
315 return config.orientation;
316 case AXIS_UIMODETYPE:
317 return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
318 case AXIS_UIMODENIGHT:
319 return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
320 case AXIS_DENSITY:
321 return config.density;
322 case AXIS_TOUCHSCREEN:
323 return config.touchscreen;
324 case AXIS_KEYSHIDDEN:
325 return config.inputFlags;
326 case AXIS_KEYBOARD:
327 return config.keyboard;
328 case AXIS_NAVIGATION:
329 return config.navigation;
330 case AXIS_SCREENSIZE:
331 return config.screenSize;
332 case AXIS_SMALLESTSCREENWIDTHDP:
333 return config.smallestScreenWidthDp;
334 case AXIS_SCREENWIDTHDP:
335 return config.screenWidthDp;
336 case AXIS_SCREENHEIGHTDP:
337 return config.screenHeightDp;
338 case AXIS_VERSION:
339 return config.version;
340 }
341 return 0;
342}
343
344bool
345AaptGroupEntry::configSameExcept(const ResTable_config& config,
346 const ResTable_config& otherConfig, int axis)
347{
348 for (int i=AXIS_START; i<=AXIS_END; i++) {
349 if (i == axis) {
350 continue;
351 }
352 if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
353 return false;
354 }
355 }
356 return true;
357}
358
a534180c
TAOSP
359bool
360AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
361{
e29f4ada
DH
362 mParamsChanged = true;
363
a534180c
TAOSP
364 Vector<String8> parts;
365
784c6915 366 String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
0096feb5 367 String8 touch, key, keysHidden, nav, navHidden, size, vers;
f1f3915b 368 String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
a534180c
TAOSP
369
370 const char *p = dir;
371 const char *q;
372 while (NULL != (q = strchr(p, '-'))) {
373 String8 val(p, q-p);
374 val.toLower();
375 parts.add(val);
376 //printf("part: %s\n", parts[parts.size()-1].string());
377 p = q+1;
378 }
379 String8 val(p);
380 val.toLower();
381 parts.add(val);
382 //printf("part: %s\n", parts[parts.size()-1].string());
383
384 const int N = parts.size();
385 int index = 0;
386 String8 part = parts[index];
387
388 // resource type
389 if (!isValidResourceType(part)) {
390 return false;
391 }
392 *resType = part;
393
394 index++;
395 if (index == N) {
396 goto success;
397 }
398 part = parts[index];
399
400 // imsi - mcc
401 if (getMccName(part.string())) {
402 mcc = part;
403
404 index++;
405 if (index == N) {
406 goto success;
407 }
408 part = parts[index];
409 } else {
410 //printf("not mcc: %s\n", part.string());
411 }
412
413 // imsi - mnc
414 if (getMncName(part.string())) {
415 mnc = part;
416
417 index++;
418 if (index == N) {
419 goto success;
420 }
421 part = parts[index];
422 } else {
423 //printf("not mcc: %s\n", part.string());
424 }
425
426 // locale - language
427 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
428 loc = part;
429
430 index++;
431 if (index == N) {
432 goto success;
433 }
434 part = parts[index];
435 } else {
436 //printf("not language: %s\n", part.string());
437 }
438
439 // locale - region
440 if (loc.length() > 0
441 && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
442 loc += "-";
443 part.toUpper();
444 loc += part.string() + 1;
445
446 index++;
447 if (index == N) {
448 goto success;
449 }
450 part = parts[index];
451 } else {
452 //printf("not region: %s\n", part.string());
453 }
454
f1f3915b
DH
455 if (getSmallestScreenWidthDpName(part.string())) {
456 smallestwidthdp = part;
784c6915
DH
457
458 index++;
459 if (index == N) {
460 goto success;
461 }
462 part = parts[index];
463 } else {
f1f3915b 464 //printf("not smallest screen width dp: %s\n", part.string());
784c6915
DH
465 }
466
f1f3915b
DH
467 if (getScreenWidthDpName(part.string())) {
468 widthdp = part;
784c6915
DH
469
470 index++;
471 if (index == N) {
472 goto success;
473 }
474 part = parts[index];
475 } else {
f1f3915b 476 //printf("not screen width dp: %s\n", part.string());
784c6915
DH
477 }
478
f1f3915b
DH
479 if (getScreenHeightDpName(part.string())) {
480 heightdp = part;
4ee37df2
DH
481
482 index++;
483 if (index == N) {
484 goto success;
485 }
486 part = parts[index];
487 } else {
f1f3915b 488 //printf("not screen height dp: %s\n", part.string());
4ee37df2
DH
489 }
490
f1f3915b
DH
491 if (getScreenLayoutSizeName(part.string())) {
492 layoutsize = part;
4ee37df2
DH
493
494 index++;
495 if (index == N) {
496 goto success;
497 }
498 part = parts[index];
499 } else {
f1f3915b
DH
500 //printf("not screen layout size: %s\n", part.string());
501 }
502
503 if (getScreenLayoutLongName(part.string())) {
504 layoutlong = part;
505
506 index++;
507 if (index == N) {
508 goto success;
509 }
510 part = parts[index];
511 } else {
512 //printf("not screen layout long: %s\n", part.string());
4ee37df2
DH
513 }
514
a534180c
TAOSP
515 // orientation
516 if (getOrientationName(part.string())) {
517 orient = part;
518
519 index++;
520 if (index == N) {
521 goto success;
522 }
523 part = parts[index];
524 } else {
525 //printf("not orientation: %s\n", part.string());
526 }
527
2ca01a37
TH
528 // ui mode type
529 if (getUiModeTypeName(part.string())) {
530 uiModeType = part;
531
532 index++;
533 if (index == N) {
534 goto success;
535 }
536 part = parts[index];
537 } else {
538 //printf("not ui mode type: %s\n", part.string());
539 }
540
541 // ui mode night
542 if (getUiModeNightName(part.string())) {
543 uiModeNight = part;
544
545 index++;
546 if (index == N) {
547 goto success;
548 }
549 part = parts[index];
550 } else {
551 //printf("not ui mode night: %s\n", part.string());
552 }
553
a534180c
TAOSP
554 // density
555 if (getDensityName(part.string())) {
556 den = part;
557
558 index++;
559 if (index == N) {
560 goto success;
561 }
562 part = parts[index];
563 } else {
564 //printf("not density: %s\n", part.string());
565 }
566
567 // touchscreen
568 if (getTouchscreenName(part.string())) {
569 touch = part;
570
571 index++;
572 if (index == N) {
573 goto success;
574 }
575 part = parts[index];
576 } else {
577 //printf("not touchscreen: %s\n", part.string());
578 }
579
580 // keyboard hidden
581 if (getKeysHiddenName(part.string())) {
582 keysHidden = part;
583
584 index++;
585 if (index == N) {
586 goto success;
587 }
588 part = parts[index];
589 } else {
590 //printf("not keysHidden: %s\n", part.string());
591 }
592
593 // keyboard
594 if (getKeyboardName(part.string())) {
595 key = part;
596
597 index++;
598 if (index == N) {
599 goto success;
600 }
601 part = parts[index];
602 } else {
603 //printf("not keyboard: %s\n", part.string());
604 }
605
0096feb5
DH
606 // navigation hidden
607 if (getNavHiddenName(part.string())) {
608 navHidden = part;
609
610 index++;
611 if (index == N) {
612 goto success;
613 }
614 part = parts[index];
615 } else {
616 //printf("not navHidden: %s\n", part.string());
617 }
618
a534180c
TAOSP
619 if (getNavigationName(part.string())) {
620 nav = part;
621
622 index++;
623 if (index == N) {
624 goto success;
625 }
626 part = parts[index];
627 } else {
628 //printf("not navigation: %s\n", part.string());
629 }
630
631 if (getScreenSizeName(part.string())) {
632 size = part;
633
634 index++;
635 if (index == N) {
636 goto success;
637 }
638 part = parts[index];
639 } else {
640 //printf("not screen size: %s\n", part.string());
641 }
642
643 if (getVersionName(part.string())) {
644 vers = part;
645
646 index++;
647 if (index == N) {
648 goto success;
649 }
650 part = parts[index];
651 } else {
652 //printf("not version: %s\n", part.string());
653 }
654
655 // if there are extra parts, it doesn't match
656 return false;
657
658success:
659 this->mcc = mcc;
660 this->mnc = mnc;
661 this->locale = loc;
784c6915
DH
662 this->screenLayoutSize = layoutsize;
663 this->screenLayoutLong = layoutlong;
f1f3915b 664 this->smallestScreenWidthDp = smallestwidthdp;
4ee37df2
DH
665 this->screenWidthDp = widthdp;
666 this->screenHeightDp = heightdp;
a534180c 667 this->orientation = orient;
2ca01a37
TH
668 this->uiModeType = uiModeType;
669 this->uiModeNight = uiModeNight;
a534180c
TAOSP
670 this->density = den;
671 this->touchscreen = touch;
672 this->keysHidden = keysHidden;
673 this->keyboard = key;
0096feb5 674 this->navHidden = navHidden;
a534180c
TAOSP
675 this->navigation = nav;
676 this->screenSize = size;
677 this->version = vers;
678
679 // what is this anyway?
680 this->vendor = "";
681
682 return true;
683}
684
685String8
686AaptGroupEntry::toString() const
687{
688 String8 s = this->mcc;
689 s += ",";
690 s += this->mnc;
691 s += ",";
692 s += this->locale;
693 s += ",";
f1f3915b 694 s += smallestScreenWidthDp;
784c6915 695 s += ",";
4ee37df2
DH
696 s += screenWidthDp;
697 s += ",";
698 s += screenHeightDp;
699 s += ",";
f1f3915b
DH
700 s += screenLayoutSize;
701 s += ",";
702 s += screenLayoutLong;
703 s += ",";
a534180c
TAOSP
704 s += this->orientation;
705 s += ",";
2ca01a37
TH
706 s += uiModeType;
707 s += ",";
708 s += uiModeNight;
709 s += ",";
a534180c
TAOSP
710 s += density;
711 s += ",";
712 s += touchscreen;
713 s += ",";
714 s += keysHidden;
715 s += ",";
716 s += keyboard;
717 s += ",";
0096feb5
DH
718 s += navHidden;
719 s += ",";
a534180c
TAOSP
720 s += navigation;
721 s += ",";
722 s += screenSize;
723 s += ",";
724 s += version;
725 return s;
726}
727
728String8
729AaptGroupEntry::toDirName(const String8& resType) const
730{
731 String8 s = resType;
732 if (this->mcc != "") {
e29f4ada
DH
733 if (s.length() > 0) {
734 s += "-";
735 }
a534180c
TAOSP
736 s += mcc;
737 }
738 if (this->mnc != "") {
e29f4ada
DH
739 if (s.length() > 0) {
740 s += "-";
741 }
a534180c
TAOSP
742 s += mnc;
743 }
744 if (this->locale != "") {
e29f4ada
DH
745 if (s.length() > 0) {
746 s += "-";
747 }
a534180c
TAOSP
748 s += locale;
749 }
f1f3915b 750 if (this->smallestScreenWidthDp != "") {
e29f4ada
DH
751 if (s.length() > 0) {
752 s += "-";
753 }
f1f3915b 754 s += smallestScreenWidthDp;
784c6915 755 }
4ee37df2 756 if (this->screenWidthDp != "") {
e29f4ada
DH
757 if (s.length() > 0) {
758 s += "-";
759 }
4ee37df2
DH
760 s += screenWidthDp;
761 }
762 if (this->screenHeightDp != "") {
e29f4ada
DH
763 if (s.length() > 0) {
764 s += "-";
765 }
4ee37df2
DH
766 s += screenHeightDp;
767 }
f1f3915b 768 if (this->screenLayoutSize != "") {
e29f4ada
DH
769 if (s.length() > 0) {
770 s += "-";
771 }
f1f3915b
DH
772 s += screenLayoutSize;
773 }
774 if (this->screenLayoutLong != "") {
e29f4ada
DH
775 if (s.length() > 0) {
776 s += "-";
777 }
f1f3915b
DH
778 s += screenLayoutLong;
779 }
a534180c 780 if (this->orientation != "") {
e29f4ada
DH
781 if (s.length() > 0) {
782 s += "-";
783 }
a534180c
TAOSP
784 s += orientation;
785 }
2ca01a37 786 if (this->uiModeType != "") {
e29f4ada
DH
787 if (s.length() > 0) {
788 s += "-";
789 }
2ca01a37
TH
790 s += uiModeType;
791 }
792 if (this->uiModeNight != "") {
e29f4ada
DH
793 if (s.length() > 0) {
794 s += "-";
795 }
2ca01a37
TH
796 s += uiModeNight;
797 }
a534180c 798 if (this->density != "") {
e29f4ada
DH
799 if (s.length() > 0) {
800 s += "-";
801 }
a534180c
TAOSP
802 s += density;
803 }
804 if (this->touchscreen != "") {
e29f4ada
DH
805 if (s.length() > 0) {
806 s += "-";
807 }
a534180c
TAOSP
808 s += touchscreen;
809 }
810 if (this->keysHidden != "") {
e29f4ada
DH
811 if (s.length() > 0) {
812 s += "-";
813 }
a534180c
TAOSP
814 s += keysHidden;
815 }
816 if (this->keyboard != "") {
e29f4ada
DH
817 if (s.length() > 0) {
818 s += "-";
819 }
a534180c
TAOSP
820 s += keyboard;
821 }
0096feb5 822 if (this->navHidden != "") {
e29f4ada
DH
823 if (s.length() > 0) {
824 s += "-";
825 }
0096feb5
DH
826 s += navHidden;
827 }
a534180c 828 if (this->navigation != "") {
e29f4ada
DH
829 if (s.length() > 0) {
830 s += "-";
831 }
a534180c
TAOSP
832 s += navigation;
833 }
834 if (this->screenSize != "") {
e29f4ada
DH
835 if (s.length() > 0) {
836 s += "-";
837 }
a534180c
TAOSP
838 s += screenSize;
839 }
840 if (this->version != "") {
e29f4ada
DH
841 if (s.length() > 0) {
842 s += "-";
843 }
a534180c
TAOSP
844 s += version;
845 }
846
847 return s;
848}
849
850bool AaptGroupEntry::getMccName(const char* name,
851 ResTable_config* out)
852{
853 if (strcmp(name, kWildcardName) == 0) {
854 if (out) out->mcc = 0;
855 return true;
856 }
857 const char* c = name;
858 if (tolower(*c) != 'm') return false;
859 c++;
860 if (tolower(*c) != 'c') return false;
861 c++;
862 if (tolower(*c) != 'c') return false;
863 c++;
864
865 const char* val = c;
866
867 while (*c >= '0' && *c <= '9') {
868 c++;
869 }
870 if (*c != 0) return false;
871 if (c-val != 3) return false;
872
873 int d = atoi(val);
874 if (d != 0) {
875 if (out) out->mcc = d;
876 return true;
877 }
878
879 return false;
880}
881
882bool AaptGroupEntry::getMncName(const char* name,
883 ResTable_config* out)
884{
885 if (strcmp(name, kWildcardName) == 0) {
886 if (out) out->mcc = 0;
887 return true;
888 }
889 const char* c = name;
890 if (tolower(*c) != 'm') return false;
891 c++;
892 if (tolower(*c) != 'n') return false;
893 c++;
894 if (tolower(*c) != 'c') return false;
895 c++;
896
897 const char* val = c;
898
899 while (*c >= '0' && *c <= '9') {
900 c++;
901 }
902 if (*c != 0) return false;
903 if (c-val == 0 || c-val > 3) return false;
904
a4424abe
JR
905 if (out) {
906 out->mnc = atoi(val);
a534180c
TAOSP
907 }
908
a4424abe 909 return true;
a534180c
TAOSP
910}
911
912/*
913 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
914 * "default")?
915 *
916 * TODO: Should insist that the first two letters are lower case, and the
917 * second two are upper.
918 */
919bool AaptGroupEntry::getLocaleName(const char* fileName,
920 ResTable_config* out)
921{
922 if (strcmp(fileName, kWildcardName) == 0
923 || strcmp(fileName, kDefaultLocale) == 0) {
924 if (out) {
925 out->language[0] = 0;
926 out->language[1] = 0;
927 out->country[0] = 0;
928 out->country[1] = 0;
929 }
930 return true;
931 }
932
933 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
934 if (out) {
935 out->language[0] = fileName[0];
936 out->language[1] = fileName[1];
937 out->country[0] = 0;
938 out->country[1] = 0;
939 }
940 return true;
941 }
942
943 if (strlen(fileName) == 5 &&
944 isalpha(fileName[0]) &&
945 isalpha(fileName[1]) &&
946 fileName[2] == '-' &&
947 isalpha(fileName[3]) &&
948 isalpha(fileName[4])) {
949 if (out) {
950 out->language[0] = fileName[0];
951 out->language[1] = fileName[1];
952 out->country[0] = fileName[3];
953 out->country[1] = fileName[4];
954 }
955 return true;
956 }
957
958 return false;
959}
960
784c6915
DH
961bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
962 ResTable_config* out)
963{
964 if (strcmp(name, kWildcardName) == 0) {
965 if (out) out->screenLayout =
966 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
967 | ResTable_config::SCREENSIZE_ANY;
968 return true;
969 } else if (strcmp(name, "small") == 0) {
970 if (out) out->screenLayout =
971 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
972 | ResTable_config::SCREENSIZE_SMALL;
973 return true;
974 } else if (strcmp(name, "normal") == 0) {
975 if (out) out->screenLayout =
976 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
977 | ResTable_config::SCREENSIZE_NORMAL;
978 return true;
979 } else if (strcmp(name, "large") == 0) {
980 if (out) out->screenLayout =
981 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
982 | ResTable_config::SCREENSIZE_LARGE;
983 return true;
69e2fb69
DH
984 } else if (strcmp(name, "xlarge") == 0) {
985 if (out) out->screenLayout =
986 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
987 | ResTable_config::SCREENSIZE_XLARGE;
988 return true;
784c6915
DH
989 }
990
991 return false;
992}
993
994bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
995 ResTable_config* out)
996{
997 if (strcmp(name, kWildcardName) == 0) {
998 if (out) out->screenLayout =
999 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1000 | ResTable_config::SCREENLONG_ANY;
1001 return true;
1002 } else if (strcmp(name, "long") == 0) {
1003 if (out) out->screenLayout =
1004 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1005 | ResTable_config::SCREENLONG_YES;
1006 return true;
1007 } else if (strcmp(name, "notlong") == 0) {
1008 if (out) out->screenLayout =
1009 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1010 | ResTable_config::SCREENLONG_NO;
1011 return true;
1012 }
1013
1014 return false;
1015}
1016
a534180c
TAOSP
1017bool AaptGroupEntry::getOrientationName(const char* name,
1018 ResTable_config* out)
1019{
1020 if (strcmp(name, kWildcardName) == 0) {
1021 if (out) out->orientation = out->ORIENTATION_ANY;
1022 return true;
1023 } else if (strcmp(name, "port") == 0) {
1024 if (out) out->orientation = out->ORIENTATION_PORT;
1025 return true;
1026 } else if (strcmp(name, "land") == 0) {
1027 if (out) out->orientation = out->ORIENTATION_LAND;
1028 return true;
1029 } else if (strcmp(name, "square") == 0) {
1030 if (out) out->orientation = out->ORIENTATION_SQUARE;
1031 return true;
1032 }
1033
1034 return false;
1035}
1036
2ca01a37
TH
1037bool AaptGroupEntry::getUiModeTypeName(const char* name,
1038 ResTable_config* out)
1039{
1040 if (strcmp(name, kWildcardName) == 0) {
1041 if (out) out->uiMode =
1042 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
e3c6837d
DH
1043 | ResTable_config::UI_MODE_TYPE_ANY;
1044 return true;
1045 } else if (strcmp(name, "desk") == 0) {
1046 if (out) out->uiMode =
1047 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1048 | ResTable_config::UI_MODE_TYPE_DESK;
2ca01a37
TH
1049 return true;
1050 } else if (strcmp(name, "car") == 0) {
1051 if (out) out->uiMode =
1052 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1053 | ResTable_config::UI_MODE_TYPE_CAR;
1054 return true;
718f0a85
DH
1055 } else if (strcmp(name, "television") == 0) {
1056 if (out) out->uiMode =
1057 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1058 | ResTable_config::UI_MODE_TYPE_TELEVISION;
1059 return true;
2cf07fe6
JO
1060 } else if (strcmp(name, "appliance") == 0) {
1061 if (out) out->uiMode =
1062 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1063 | ResTable_config::UI_MODE_TYPE_APPLIANCE;
1064 return true;
2ca01a37
TH
1065 }
1066
1067 return false;
1068}
1069
1070bool AaptGroupEntry::getUiModeNightName(const char* name,
1071 ResTable_config* out)
1072{
1073 if (strcmp(name, kWildcardName) == 0) {
1074 if (out) out->uiMode =
1075 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1076 | ResTable_config::UI_MODE_NIGHT_ANY;
1077 return true;
1078 } else if (strcmp(name, "night") == 0) {
1079 if (out) out->uiMode =
1080 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1081 | ResTable_config::UI_MODE_NIGHT_YES;
1082 return true;
1083 } else if (strcmp(name, "notnight") == 0) {
1084 if (out) out->uiMode =
1085 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1086 | ResTable_config::UI_MODE_NIGHT_NO;
1087 return true;
1088 }
1089
1090 return false;
1091}
1092
a534180c
TAOSP
1093bool AaptGroupEntry::getDensityName(const char* name,
1094 ResTable_config* out)
1095{
1096 if (strcmp(name, kWildcardName) == 0) {
41ff305d 1097 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
a534180c
TAOSP
1098 return true;
1099 }
41ff305d
DH
1100
1101 if (strcmp(name, "nodpi") == 0) {
1102 if (out) out->density = ResTable_config::DENSITY_NONE;
1103 return true;
1104 }
1105
784c6915
DH
1106 if (strcmp(name, "ldpi") == 0) {
1107 if (out) out->density = ResTable_config::DENSITY_LOW;
1108 return true;
1109 }
1110
1111 if (strcmp(name, "mdpi") == 0) {
1112 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1113 return true;
1114 }
1115
465c2ef5
DH
1116 if (strcmp(name, "tvdpi") == 0) {
1117 if (out) out->density = ResTable_config::DENSITY_TV;
1118 return true;
1119 }
1120
784c6915
DH
1121 if (strcmp(name, "hdpi") == 0) {
1122 if (out) out->density = ResTable_config::DENSITY_HIGH;
1123 return true;
1124 }
e2380bf2 1125
bd8c05dd 1126 if (strcmp(name, "xhdpi") == 0) {
e2380bf2 1127 if (out) out->density = ResTable_config::DENSITY_XHIGH;
bd8c05dd
DH
1128 return true;
1129 }
e2380bf2
DH
1130
1131 if (strcmp(name, "xxhdpi") == 0) {
1132 if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1133 return true;
1134 }
1135
a534180c
TAOSP
1136 char* c = (char*)name;
1137 while (*c >= '0' && *c <= '9') {
1138 c++;
1139 }
1140
1141 // check that we have 'dpi' after the last digit.
1142 if (toupper(c[0]) != 'D' ||
1143 toupper(c[1]) != 'P' ||
1144 toupper(c[2]) != 'I' ||
1145 c[3] != 0) {
1146 return false;
1147 }
1148
1149 // temporarily replace the first letter with \0 to
1150 // use atoi.
1151 char tmp = c[0];
1152 c[0] = '\0';
1153
1154 int d = atoi(name);
1155 c[0] = tmp;
1156
1157 if (d != 0) {
1158 if (out) out->density = d;
1159 return true;
1160 }
1161
1162 return false;
1163}
1164
1165bool AaptGroupEntry::getTouchscreenName(const char* name,
1166 ResTable_config* out)
1167{
1168 if (strcmp(name, kWildcardName) == 0) {
1169 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1170 return true;
1171 } else if (strcmp(name, "notouch") == 0) {
1172 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1173 return true;
1174 } else if (strcmp(name, "stylus") == 0) {
1175 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1176 return true;
1177 } else if (strcmp(name, "finger") == 0) {
1178 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1179 return true;
1180 }
1181
1182 return false;
1183}
1184
1185bool AaptGroupEntry::getKeysHiddenName(const char* name,
1186 ResTable_config* out)
1187{
1188 uint8_t mask = 0;
1189 uint8_t value = 0;
1190 if (strcmp(name, kWildcardName) == 0) {
18a923f6
KR
1191 mask = ResTable_config::MASK_KEYSHIDDEN;
1192 value = ResTable_config::KEYSHIDDEN_ANY;
a534180c 1193 } else if (strcmp(name, "keysexposed") == 0) {
18a923f6
KR
1194 mask = ResTable_config::MASK_KEYSHIDDEN;
1195 value = ResTable_config::KEYSHIDDEN_NO;
a534180c 1196 } else if (strcmp(name, "keyshidden") == 0) {
18a923f6
KR
1197 mask = ResTable_config::MASK_KEYSHIDDEN;
1198 value = ResTable_config::KEYSHIDDEN_YES;
a534180c 1199 } else if (strcmp(name, "keyssoft") == 0) {
18a923f6
KR
1200 mask = ResTable_config::MASK_KEYSHIDDEN;
1201 value = ResTable_config::KEYSHIDDEN_SOFT;
a534180c
TAOSP
1202 }
1203
1204 if (mask != 0) {
1205 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1206 return true;
1207 }
1208
1209 return false;
1210}
1211
1212bool AaptGroupEntry::getKeyboardName(const char* name,
1213 ResTable_config* out)
1214{
1215 if (strcmp(name, kWildcardName) == 0) {
1216 if (out) out->keyboard = out->KEYBOARD_ANY;
1217 return true;
1218 } else if (strcmp(name, "nokeys") == 0) {
1219 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1220 return true;
1221 } else if (strcmp(name, "qwerty") == 0) {
1222 if (out) out->keyboard = out->KEYBOARD_QWERTY;
1223 return true;
1224 } else if (strcmp(name, "12key") == 0) {
1225 if (out) out->keyboard = out->KEYBOARD_12KEY;
1226 return true;
1227 }
1228
1229 return false;
1230}
1231
0096feb5
DH
1232bool AaptGroupEntry::getNavHiddenName(const char* name,
1233 ResTable_config* out)
1234{
1235 uint8_t mask = 0;
1236 uint8_t value = 0;
1237 if (strcmp(name, kWildcardName) == 0) {
491d9ef0
KR
1238 mask = ResTable_config::MASK_NAVHIDDEN;
1239 value = ResTable_config::NAVHIDDEN_ANY;
0096feb5 1240 } else if (strcmp(name, "navexposed") == 0) {
491d9ef0
KR
1241 mask = ResTable_config::MASK_NAVHIDDEN;
1242 value = ResTable_config::NAVHIDDEN_NO;
0096feb5 1243 } else if (strcmp(name, "navhidden") == 0) {
491d9ef0
KR
1244 mask = ResTable_config::MASK_NAVHIDDEN;
1245 value = ResTable_config::NAVHIDDEN_YES;
0096feb5
DH
1246 }
1247
1248 if (mask != 0) {
1249 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1250 return true;
1251 }
1252
1253 return false;
1254}
1255
a534180c
TAOSP
1256bool AaptGroupEntry::getNavigationName(const char* name,
1257 ResTable_config* out)
1258{
1259 if (strcmp(name, kWildcardName) == 0) {
1260 if (out) out->navigation = out->NAVIGATION_ANY;
1261 return true;
1262 } else if (strcmp(name, "nonav") == 0) {
1263 if (out) out->navigation = out->NAVIGATION_NONAV;
1264 return true;
1265 } else if (strcmp(name, "dpad") == 0) {
1266 if (out) out->navigation = out->NAVIGATION_DPAD;
1267 return true;
1268 } else if (strcmp(name, "trackball") == 0) {
1269 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1270 return true;
1271 } else if (strcmp(name, "wheel") == 0) {
1272 if (out) out->navigation = out->NAVIGATION_WHEEL;
1273 return true;
1274 }
1275
1276 return false;
1277}
1278
4ee37df2 1279bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
a534180c
TAOSP
1280{
1281 if (strcmp(name, kWildcardName) == 0) {
1282 if (out) {
1283 out->screenWidth = out->SCREENWIDTH_ANY;
1284 out->screenHeight = out->SCREENHEIGHT_ANY;
1285 }
1286 return true;
1287 }
1288
1289 const char* x = name;
1290 while (*x >= '0' && *x <= '9') x++;
1291 if (x == name || *x != 'x') return false;
1292 String8 xName(name, x-name);
1293 x++;
1294
1295 const char* y = x;
1296 while (*y >= '0' && *y <= '9') y++;
1297 if (y == name || *y != 0) return false;
1298 String8 yName(x, y-x);
1299
1300 uint16_t w = (uint16_t)atoi(xName.string());
1301 uint16_t h = (uint16_t)atoi(yName.string());
1302 if (w < h) {
1303 return false;
1304 }
1305
1306 if (out) {
1307 out->screenWidth = w;
1308 out->screenHeight = h;
1309 }
1310
1311 return true;
1312}
1313
f1f3915b
DH
1314bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1315{
1316 if (strcmp(name, kWildcardName) == 0) {
1317 if (out) {
1318 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1319 }
1320 return true;
1321 }
1322
1323 if (*name != 's') return false;
1324 name++;
1325 if (*name != 'w') return false;
1326 name++;
1327 const char* x = name;
1328 while (*x >= '0' && *x <= '9') x++;
1329 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1330 String8 xName(name, x-name);
1331
1332 if (out) {
1333 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1334 }
1335
1336 return true;
1337}
1338
4ee37df2
DH
1339bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1340{
1341 if (strcmp(name, kWildcardName) == 0) {
1342 if (out) {
1343 out->screenWidthDp = out->SCREENWIDTH_ANY;
1344 }
1345 return true;
1346 }
1347
1348 if (*name != 'w') return false;
1349 name++;
1350 const char* x = name;
1351 while (*x >= '0' && *x <= '9') x++;
1352 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1353 String8 xName(name, x-name);
1354
1355 if (out) {
1356 out->screenWidthDp = (uint16_t)atoi(xName.string());
1357 }
1358
1359 return true;
1360}
1361
1362bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1363{
1364 if (strcmp(name, kWildcardName) == 0) {
1365 if (out) {
1366 out->screenHeightDp = out->SCREENWIDTH_ANY;
1367 }
1368 return true;
1369 }
1370
1371 if (*name != 'h') return false;
1372 name++;
1373 const char* x = name;
1374 while (*x >= '0' && *x <= '9') x++;
1375 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1376 String8 xName(name, x-name);
1377
1378 if (out) {
1379 out->screenHeightDp = (uint16_t)atoi(xName.string());
1380 }
1381
1382 return true;
1383}
1384
1385bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
a534180c
TAOSP
1386{
1387 if (strcmp(name, kWildcardName) == 0) {
1388 if (out) {
1389 out->sdkVersion = out->SDKVERSION_ANY;
1390 out->minorVersion = out->MINORVERSION_ANY;
1391 }
1392 return true;
1393 }
1394
1395 if (*name != 'v') {
1396 return false;
1397 }
1398
1399 name++;
1400 const char* s = name;
1401 while (*s >= '0' && *s <= '9') s++;
1402 if (s == name || *s != 0) return false;
1403 String8 sdkName(name, s-name);
1404
1405 if (out) {
1406 out->sdkVersion = (uint16_t)atoi(sdkName.string());
1407 out->minorVersion = 0;
1408 }
1409
1410 return true;
1411}
1412
1413int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1414{
1415 int v = mcc.compare(o.mcc);
1416 if (v == 0) v = mnc.compare(o.mnc);
1417 if (v == 0) v = locale.compare(o.locale);
1418 if (v == 0) v = vendor.compare(o.vendor);
f1f3915b 1419 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
4ee37df2
DH
1420 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1421 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
f1f3915b
DH
1422 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1423 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
a534180c 1424 if (v == 0) v = orientation.compare(o.orientation);
2ca01a37
TH
1425 if (v == 0) v = uiModeType.compare(o.uiModeType);
1426 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
a534180c
TAOSP
1427 if (v == 0) v = density.compare(o.density);
1428 if (v == 0) v = touchscreen.compare(o.touchscreen);
1429 if (v == 0) v = keysHidden.compare(o.keysHidden);
1430 if (v == 0) v = keyboard.compare(o.keyboard);
0096feb5 1431 if (v == 0) v = navHidden.compare(o.navHidden);
a534180c
TAOSP
1432 if (v == 0) v = navigation.compare(o.navigation);
1433 if (v == 0) v = screenSize.compare(o.screenSize);
1434 if (v == 0) v = version.compare(o.version);
1435 return v;
1436}
1437
e29f4ada 1438const ResTable_config& AaptGroupEntry::toParams() const
a534180c 1439{
e29f4ada
DH
1440 if (!mParamsChanged) {
1441 return mParams;
1442 }
1443
1444 mParamsChanged = false;
1445 ResTable_config& params(mParams);
a534180c
TAOSP
1446 memset(&params, 0, sizeof(params));
1447 getMccName(mcc.string(), &params);
1448 getMncName(mnc.string(), &params);
1449 getLocaleName(locale.string(), &params);
f1f3915b 1450 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
4ee37df2
DH
1451 getScreenWidthDpName(screenWidthDp.string(), &params);
1452 getScreenHeightDpName(screenHeightDp.string(), &params);
f1f3915b
DH
1453 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1454 getScreenLayoutLongName(screenLayoutLong.string(), &params);
a534180c 1455 getOrientationName(orientation.string(), &params);
2ca01a37
TH
1456 getUiModeTypeName(uiModeType.string(), &params);
1457 getUiModeNightName(uiModeNight.string(), &params);
a534180c
TAOSP
1458 getDensityName(density.string(), &params);
1459 getTouchscreenName(touchscreen.string(), &params);
1460 getKeysHiddenName(keysHidden.string(), &params);
1461 getKeyboardName(keyboard.string(), &params);
0096feb5 1462 getNavHiddenName(navHidden.string(), &params);
a534180c
TAOSP
1463 getNavigationName(navigation.string(), &params);
1464 getScreenSizeName(screenSize.string(), &params);
1465 getVersionName(version.string(), &params);
af945cf3
DH
1466
1467 // Fix up version number based on specified parameters.
1468 int minSdk = 0;
f1f3915b
DH
1469 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1470 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
4ee37df2 1471 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
f1f3915b 1472 minSdk = SDK_HONEYCOMB_MR2;
4ee37df2 1473 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
af945cf3
DH
1474 != ResTable_config::UI_MODE_TYPE_ANY
1475 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1476 != ResTable_config::UI_MODE_NIGHT_ANY) {
1477 minSdk = SDK_FROYO;
1478 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1479 != ResTable_config::SCREENSIZE_ANY
1480 || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1481 != ResTable_config::SCREENLONG_ANY
1482 || params.density != ResTable_config::DENSITY_DEFAULT) {
1483 minSdk = SDK_DONUT;
1484 }
1485
1486 if (minSdk > params.sdkVersion) {
1487 params.sdkVersion = minSdk;
1488 }
1489
a534180c
TAOSP
1490 return params;
1491}
1492
1493// =========================================================================
1494// =========================================================================
1495// =========================================================================
1496
1497void* AaptFile::editData(size_t size)
1498{
1499 if (size <= mBufferSize) {
1500 mDataSize = size;
1501 return mData;
1502 }
1503 size_t allocSize = (size*3)/2;
1504 void* buf = realloc(mData, allocSize);
1505 if (buf == NULL) {
1506 return NULL;
1507 }
1508 mData = buf;
1509 mDataSize = size;
1510 mBufferSize = allocSize;
1511 return buf;
1512}
1513
1514void* AaptFile::editData(size_t* outSize)
1515{
1516 if (outSize) {
1517 *outSize = mDataSize;
1518 }
1519 return mData;
1520}
1521
1522void* AaptFile::padData(size_t wordSize)
1523{
1524 const size_t extra = mDataSize%wordSize;
1525 if (extra == 0) {
1526 return mData;
1527 }
1528
1529 size_t initial = mDataSize;
1530 void* data = editData(initial+(wordSize-extra));
1531 if (data != NULL) {
1532 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1533 }
1534 return data;
1535}
1536
1537status_t AaptFile::writeData(const void* data, size_t size)
1538{
1539 size_t end = mDataSize;
1540 size_t total = size + end;
1541 void* buf = editData(total);
1542 if (buf == NULL) {
1543 return UNKNOWN_ERROR;
1544 }
1545 memcpy(((char*)buf)+end, data, size);
1546 return NO_ERROR;
1547}
1548
1549void AaptFile::clearData()
1550{
1551 if (mData != NULL) free(mData);
1552 mData = NULL;
1553 mDataSize = 0;
1554 mBufferSize = 0;
1555}
1556
1557String8 AaptFile::getPrintableSource() const
1558{
1559 if (hasData()) {
e29f4ada 1560 String8 name(mGroupEntry.toDirName(String8()));
a534180c
TAOSP
1561 name.appendPath(mPath);
1562 name.append(" #generated");
1563 return name;
1564 }
1565 return mSourceFile;
1566}
1567
1568// =========================================================================
1569// =========================================================================
1570// =========================================================================
1571
1572status_t AaptGroup::addFile(const sp<AaptFile>& file)
1573{
1574 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1575 file->mPath = mPath;
1576 mFiles.add(file->getGroupEntry(), file);
1577 return NO_ERROR;
1578 }
1579
e29f4ada
DH
1580#if 0
1581 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1582 file->getSourceFile().string(),
1583 file->getGroupEntry().toDirName(String8()).string(),
1584 mLeaf.string(), mPath.string());
1585#endif
1586
a534180c
TAOSP
1587 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1588 getPrintableSource().string());
1589 return UNKNOWN_ERROR;
1590}
1591
1592void AaptGroup::removeFile(size_t index)
1593{
1594 mFiles.removeItemsAt(index);
1595}
1596
e29f4ada 1597void AaptGroup::print(const String8& prefix) const
a534180c 1598{
e29f4ada 1599 printf("%s%s\n", prefix.string(), getPath().string());
a534180c
TAOSP
1600 const size_t N=mFiles.size();
1601 size_t i;
1602 for (i=0; i<N; i++) {
1603 sp<AaptFile> file = mFiles.valueAt(i);
1604 const AaptGroupEntry& e = file->getGroupEntry();
1605 if (file->hasData()) {
e29f4ada 1606 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
a534180c
TAOSP
1607 (int)file->getSize());
1608 } else {
e29f4ada
DH
1609 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1610 file->getPrintableSource().string());
a534180c 1611 }
e29f4ada
DH
1612 //printf("%s File Group Entry: %s\n", prefix.string(),
1613 // file->getGroupEntry().toDirName(String8()).string());
a534180c
TAOSP
1614 }
1615}
1616
1617String8 AaptGroup::getPrintableSource() const
1618{
1619 if (mFiles.size() > 0) {
1620 // Arbitrarily pull the first source file out of the list.
1621 return mFiles.valueAt(0)->getPrintableSource();
1622 }
1623
1624 // Should never hit this case, but to be safe...
1625 return getPath();
1626
1627}
1628
1629// =========================================================================
1630// =========================================================================
1631// =========================================================================
1632
1633status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1634{
1635 if (mFiles.indexOfKey(name) >= 0) {
1636 return ALREADY_EXISTS;
1637 }
1638 mFiles.add(name, file);
1639 return NO_ERROR;
1640}
1641
1642status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1643{
1644 if (mDirs.indexOfKey(name) >= 0) {
1645 return ALREADY_EXISTS;
1646 }
1647 mDirs.add(name, dir);
1648 return NO_ERROR;
1649}
1650
1651sp<AaptDir> AaptDir::makeDir(const String8& path)
1652{
1653 String8 name;
1654 String8 remain = path;
1655
1656 sp<AaptDir> subdir = this;
1657 while (name = remain.walkPath(&remain), remain != "") {
1658 subdir = subdir->makeDir(name);
1659 }
1660
1661 ssize_t i = subdir->mDirs.indexOfKey(name);
1662 if (i >= 0) {
1663 return subdir->mDirs.valueAt(i);
1664 }
1665 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1666 subdir->mDirs.add(name, dir);
1667 return dir;
1668}
1669
1670void AaptDir::removeFile(const String8& name)
1671{
1672 mFiles.removeItem(name);
1673}
1674
1675void AaptDir::removeDir(const String8& name)
1676{
1677 mDirs.removeItem(name);
1678}
1679
a534180c
TAOSP
1680status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1681{
1682 sp<AaptGroup> group;
1683 if (mFiles.indexOfKey(leafName) >= 0) {
1684 group = mFiles.valueFor(leafName);
1685 } else {
1686 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1687 mFiles.add(leafName, group);
1688 }
1689
1690 return group->addFile(file);
1691}
1692
1693ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
52ffc169
JG
1694 const AaptGroupEntry& kind, const String8& resType,
1695 sp<FilePathStore>& fullResPaths)
a534180c
TAOSP
1696{
1697 Vector<String8> fileNames;
a534180c
TAOSP
1698 {
1699 DIR* dir = NULL;
1700
1701 dir = opendir(srcDir.string());
1702 if (dir == NULL) {
1703 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1704 return UNKNOWN_ERROR;
1705 }
1706
1707 /*
1708 * Slurp the filenames out of the directory.
1709 */
1710 while (1) {
1711 struct dirent* entry;
1712
1713 entry = readdir(dir);
1714 if (entry == NULL)
1715 break;
1716
1717 if (isHidden(srcDir.string(), entry->d_name))
1718 continue;
1719
52ffc169
JG
1720 String8 name(entry->d_name);
1721 fileNames.add(name);
1722 // Add fully qualified path for dependency purposes
1723 // if we're collecting them
1724 if (fullResPaths != NULL) {
1725 fullResPaths->add(srcDir.appendPathCopy(name));
1726 }
a534180c 1727 }
a534180c
TAOSP
1728 closedir(dir);
1729 }
1730
1731 ssize_t count = 0;
1732
1733 /*
1734 * Stash away the files and recursively descend into subdirectories.
1735 */
1736 const size_t N = fileNames.size();
1737 size_t i;
1738 for (i = 0; i < N; i++) {
1739 String8 pathName(srcDir);
1740 FileType type;
1741
1742 pathName.appendPath(fileNames[i].string());
1743 type = getFileType(pathName.string());
1744 if (type == kFileTypeDirectory) {
1745 sp<AaptDir> subdir;
1746 bool notAdded = false;
1747 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1748 subdir = mDirs.valueFor(fileNames[i]);
1749 } else {
1750 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1751 notAdded = true;
1752 }
1753 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
52ffc169 1754 resType, fullResPaths);
a534180c
TAOSP
1755 if (res < NO_ERROR) {
1756 return res;
1757 }
1758 if (res > 0 && notAdded) {
1759 mDirs.add(fileNames[i], subdir);
1760 }
1761 count += res;
1762 } else if (type == kFileTypeRegular) {
1763 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1764 status_t err = addLeafFile(fileNames[i], file);
1765 if (err != NO_ERROR) {
1766 return err;
1767 }
1768
1769 count++;
1770
1771 } else {
1772 if (bundle->getVerbose())
1773 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
1774 }
1775 }
1776
1777 return count;
1778}
1779
1780status_t AaptDir::validate() const
1781{
1782 const size_t NF = mFiles.size();
1783 const size_t ND = mDirs.size();
1784 size_t i;
1785 for (i = 0; i < NF; i++) {
1786 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1787 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1788 "Invalid filename. Unable to add.");
1789 return UNKNOWN_ERROR;
1790 }
1791
1792 size_t j;
1793 for (j = i+1; j < NF; j++) {
1794 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1795 mFiles.valueAt(j)->getLeaf().string()) == 0) {
1796 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1797 "File is case-insensitive equivalent to: %s",
1798 mFiles.valueAt(j)->getPrintableSource().string());
1799 return UNKNOWN_ERROR;
1800 }
1801
1802 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1803 // (this is mostly caught by the "marked" stuff, below)
1804 }
1805
1806 for (j = 0; j < ND; j++) {
1807 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1808 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1809 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1810 "File conflicts with dir from: %s",
1811 mDirs.valueAt(j)->getPrintableSource().string());
1812 return UNKNOWN_ERROR;
1813 }
1814 }
1815 }
1816
1817 for (i = 0; i < ND; i++) {
1818 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1819 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1820 "Invalid directory name, unable to add.");
1821 return UNKNOWN_ERROR;
1822 }
1823
1824 size_t j;
1825 for (j = i+1; j < ND; j++) {
1826 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1827 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1828 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1829 "Directory is case-insensitive equivalent to: %s",
1830 mDirs.valueAt(j)->getPrintableSource().string());
1831 return UNKNOWN_ERROR;
1832 }
1833 }
1834
1835 status_t err = mDirs.valueAt(i)->validate();
1836 if (err != NO_ERROR) {
1837 return err;
1838 }
1839 }
1840
1841 return NO_ERROR;
1842}
1843
e29f4ada 1844void AaptDir::print(const String8& prefix) const
a534180c
TAOSP
1845{
1846 const size_t ND=getDirs().size();
1847 size_t i;
1848 for (i=0; i<ND; i++) {
e29f4ada 1849 getDirs().valueAt(i)->print(prefix);
a534180c
TAOSP
1850 }
1851
1852 const size_t NF=getFiles().size();
1853 for (i=0; i<NF; i++) {
e29f4ada 1854 getFiles().valueAt(i)->print(prefix);
a534180c
TAOSP
1855 }
1856}
1857
1858String8 AaptDir::getPrintableSource() const
1859{
1860 if (mFiles.size() > 0) {
1861 // Arbitrarily pull the first file out of the list as the source dir.
1862 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1863 }
1864 if (mDirs.size() > 0) {
1865 // Or arbitrarily pull the first dir out of the list as the source dir.
1866 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1867 }
1868
1869 // Should never hit this case, but to be safe...
1870 return mPath;
1871
1872}
1873
1874// =========================================================================
1875// =========================================================================
1876// =========================================================================
1877
6415576e
DH
1878status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
1879{
1880 status_t err = NO_ERROR;
1881 size_t N = javaSymbols->mSymbols.size();
1882 for (size_t i=0; i<N; i++) {
1883 const String8& name = javaSymbols->mSymbols.keyAt(i);
1884 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
1885 ssize_t pos = mSymbols.indexOfKey(name);
1886 if (pos < 0) {
1887 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
1888 err = UNKNOWN_ERROR;
1889 continue;
1890 }
1891 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
1892 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
1893 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
1894 }
1895
1896 N = javaSymbols->mNestedSymbols.size();
1897 for (size_t i=0; i<N; i++) {
1898 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
1899 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
1900 ssize_t pos = mNestedSymbols.indexOfKey(name);
1901 if (pos < 0) {
1902 SourcePos pos;
1903 pos.error("Java symbol dir %s not defined\n", name.string());
1904 err = UNKNOWN_ERROR;
1905 continue;
1906 }
1907 //printf("**** applying java symbols in dir %s\n", name.string());
1908 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1909 if (myerr != NO_ERROR) {
1910 err = myerr;
1911 }
1912 }
1913
1914 return err;
1915}
1916
1917// =========================================================================
1918// =========================================================================
1919// =========================================================================
1920
e29f4ada
DH
1921AaptAssets::AaptAssets()
1922 : AaptDir(String8(), String8()),
1923 mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
1924{
1925}
1926
1927const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
1928 if (mChanged) {
1929 }
1930 return mGroupEntries;
1931}
1932
1933status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
1934{
1935 mChanged = true;
1936 return AaptDir::addFile(name, file);
1937}
1938
a534180c
TAOSP
1939sp<AaptFile> AaptAssets::addFile(
1940 const String8& filePath, const AaptGroupEntry& entry,
1941 const String8& srcDir, sp<AaptGroup>* outGroup,
1942 const String8& resType)
1943{
1944 sp<AaptDir> dir = this;
1945 sp<AaptGroup> group;
1946 sp<AaptFile> file;
1947 String8 root, remain(filePath), partialPath;
1948 while (remain.length() > 0) {
1949 root = remain.walkPath(&remain);
1950 partialPath.appendPath(root);
1951
1952 const String8 rootStr(root);
1953
1954 if (remain.length() == 0) {
1955 ssize_t i = dir->getFiles().indexOfKey(rootStr);
1956 if (i >= 0) {
1957 group = dir->getFiles().valueAt(i);
1958 } else {
1959 group = new AaptGroup(rootStr, filePath);
1960 status_t res = dir->addFile(rootStr, group);
1961 if (res != NO_ERROR) {
1962 return NULL;
1963 }
1964 }
1965 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1966 status_t res = group->addFile(file);
1967 if (res != NO_ERROR) {
1968 return NULL;
1969 }
1970 break;
1971
1972 } else {
1973 ssize_t i = dir->getDirs().indexOfKey(rootStr);
1974 if (i >= 0) {
1975 dir = dir->getDirs().valueAt(i);
1976 } else {
1977 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1978 status_t res = dir->addDir(rootStr, subdir);
1979 if (res != NO_ERROR) {
1980 return NULL;
1981 }
1982 dir = subdir;
1983 }
1984 }
1985 }
1986
1987 mGroupEntries.add(entry);
1988 if (outGroup) *outGroup = group;
1989 return file;
1990}
1991
1992void AaptAssets::addResource(const String8& leafName, const String8& path,
1993 const sp<AaptFile>& file, const String8& resType)
1994{
1995 sp<AaptDir> res = AaptDir::makeDir(kResString);
1996 String8 dirname = file->getGroupEntry().toDirName(resType);
1997 sp<AaptDir> subdir = res->makeDir(dirname);
1998 sp<AaptGroup> grr = new AaptGroup(leafName, path);
1999 grr->addFile(file);
2000
2001 subdir->addFile(leafName, grr);
2002}
2003
2004
2005ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2006{
2007 int count;
2008 int totalCount = 0;
2009 FileType type;
2010 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2011 const size_t dirCount =resDirs.size();
2012 sp<AaptAssets> current = this;
2013
2014 const int N = bundle->getFileSpecCount();
2015
2016 /*
2017 * If a package manifest was specified, include that first.
2018 */
2019 if (bundle->getAndroidManifestFile() != NULL) {
2020 // place at root of zip.
2021 String8 srcFile(bundle->getAndroidManifestFile());
2022 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2023 NULL, String8());
2024 totalCount++;
2025 }
2026
2027 /*
2028 * If a directory of custom assets was supplied, slurp 'em up.
2029 */
2030 if (bundle->getAssetSourceDir()) {
2031 const char* assetDir = bundle->getAssetSourceDir();
2032
2033 FileType type = getFileType(assetDir);
2034 if (type == kFileTypeNonexistent) {
2035 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
2036 return UNKNOWN_ERROR;
2037 }
2038 if (type != kFileTypeDirectory) {
2039 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2040 return UNKNOWN_ERROR;
2041 }
2042
2043 String8 assetRoot(assetDir);
2044 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2045 AaptGroupEntry group;
2046 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
b5a473da 2047 String8(), mFullAssetPaths);
a534180c
TAOSP
2048 if (count < 0) {
2049 totalCount = count;
2050 goto bail;
2051 }
2052 if (count > 0) {
2053 mGroupEntries.add(group);
2054 }
2055 totalCount += count;
2056
2057 if (bundle->getVerbose())
2058 printf("Found %d custom asset file%s in %s\n",
2059 count, (count==1) ? "" : "s", assetDir);
2060 }
2061
2062 /*
2063 * If a directory of resource-specific assets was supplied, slurp 'em up.
2064 */
2065 for (size_t i=0; i<dirCount; i++) {
2066 const char *res = resDirs[i];
2067 if (res) {
2068 type = getFileType(res);
2069 if (type == kFileTypeNonexistent) {
2070 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2071 return UNKNOWN_ERROR;
2072 }
2073 if (type == kFileTypeDirectory) {
2074 if (i>0) {
2075 sp<AaptAssets> nextOverlay = new AaptAssets();
2076 current->setOverlay(nextOverlay);
2077 current = nextOverlay;
52ffc169 2078 current->setFullResPaths(mFullResPaths);
a534180c
TAOSP
2079 }
2080 count = current->slurpResourceTree(bundle, String8(res));
2081
2082 if (count < 0) {
2083 totalCount = count;
2084 goto bail;
2085 }
2086 totalCount += count;
2087 }
2088 else {
2089 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2090 return UNKNOWN_ERROR;
2091 }
2092 }
2093
2094 }
2095 /*
2096 * Now do any additional raw files.
2097 */
2098 for (int arg=0; arg<N; arg++) {
2099 const char* assetDir = bundle->getFileSpecEntry(arg);
2100
2101 FileType type = getFileType(assetDir);
2102 if (type == kFileTypeNonexistent) {
2103 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2104 return UNKNOWN_ERROR;
2105 }
2106 if (type != kFileTypeDirectory) {
2107 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2108 return UNKNOWN_ERROR;
2109 }
2110
2111 String8 assetRoot(assetDir);
2112
2113 if (bundle->getVerbose())
2114 printf("Processing raw dir '%s'\n", (const char*) assetDir);
2115
2116 /*
2117 * Do a recursive traversal of subdir tree. We don't make any
2118 * guarantees about ordering, so we're okay with an inorder search
2119 * using whatever order the OS happens to hand back to us.
2120 */
b5a473da 2121 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
a534180c
TAOSP
2122 if (count < 0) {
2123 /* failure; report error and remove archive */
2124 totalCount = count;
2125 goto bail;
2126 }
2127 totalCount += count;
2128
2129 if (bundle->getVerbose())
2130 printf("Found %d asset file%s in %s\n",
2131 count, (count==1) ? "" : "s", assetDir);
2132 }
2133
2134 count = validate();
2135 if (count != NO_ERROR) {
2136 totalCount = count;
2137 goto bail;
2138 }
2139
e29f4ada
DH
2140 count = filter(bundle);
2141 if (count != NO_ERROR) {
2142 totalCount = count;
2143 goto bail;
2144 }
a534180c
TAOSP
2145
2146bail:
2147 return totalCount;
2148}
2149
2150ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2151 const AaptGroupEntry& kind,
52ffc169
JG
2152 const String8& resType,
2153 sp<FilePathStore>& fullResPaths)
a534180c 2154{
52ffc169 2155 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
a534180c
TAOSP
2156 if (res > 0) {
2157 mGroupEntries.add(kind);
2158 }
2159
2160 return res;
2161}
2162
2163ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2164{
2165 ssize_t err = 0;
2166
2167 DIR* dir = opendir(srcDir.string());
2168 if (dir == NULL) {
2169 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2170 return UNKNOWN_ERROR;
2171 }
2172
2173 status_t count = 0;
2174
2175 /*
2176 * Run through the directory, looking for dirs that match the
2177 * expected pattern.
2178 */
2179 while (1) {
2180 struct dirent* entry = readdir(dir);
2181 if (entry == NULL) {
2182 break;
2183 }
2184
2185 if (isHidden(srcDir.string(), entry->d_name)) {
2186 continue;
2187 }
2188
2189 String8 subdirName(srcDir);
2190 subdirName.appendPath(entry->d_name);
2191
2192 AaptGroupEntry group;
2193 String8 resType;
2194 bool b = group.initFromDirName(entry->d_name, &resType);
2195 if (!b) {
2196 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2197 entry->d_name);
2198 err = -1;
2199 continue;
2200 }
2201
e29f4ada 2202 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
73412e58 2203 int maxResInt = atoi(bundle->getMaxResVersion());
e29f4ada 2204 const char *verString = group.getVersionString().string();
73412e58
FK
2205 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2206 if (dirVersionInt > maxResInt) {
2207 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2208 continue;
2209 }
2210 }
2211
a534180c
TAOSP
2212 FileType type = getFileType(subdirName.string());
2213
2214 if (type == kFileTypeDirectory) {
e29f4ada 2215 sp<AaptDir> dir = makeDir(resType);
a534180c 2216 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
52ffc169 2217 resType, mFullResPaths);
a534180c
TAOSP
2218 if (res < 0) {
2219 count = res;
2220 goto bail;
2221 }
2222 if (res > 0) {
2223 mGroupEntries.add(group);
2224 count += res;
2225 }
2226
e29f4ada
DH
2227 // Only add this directory if we don't already have a resource dir
2228 // for the current type. This ensures that we only add the dir once
2229 // for all configs.
2230 sp<AaptDir> rdir = resDir(resType);
2231 if (rdir == NULL) {
2232 mResDirs.add(dir);
2233 }
a534180c
TAOSP
2234 } else {
2235 if (bundle->getVerbose()) {
2236 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
2237 }
2238 }
2239 }
2240
2241bail:
2242 closedir(dir);
2243 dir = NULL;
2244
2245 if (err != 0) {
2246 return err;
2247 }
2248 return count;
2249}
2250
2251ssize_t
2252AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2253{
2254 int count = 0;
2255 SortedVector<AaptGroupEntry> entries;
2256
2257 ZipFile* zip = new ZipFile;
2258 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2259 if (err != NO_ERROR) {
2260 fprintf(stderr, "error opening zip file %s\n", filename);
2261 count = err;
2262 delete zip;
2263 return -1;
2264 }
2265
2266 const int N = zip->getNumEntries();
2267 for (int i=0; i<N; i++) {
2268 ZipEntry* entry = zip->getEntryByIndex(i);
2269 if (entry->getDeleted()) {
2270 continue;
2271 }
2272
2273 String8 entryName(entry->getFileName());
2274
2275 String8 dirName = entryName.getPathDir();
2276 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2277
2278 String8 resType;
2279 AaptGroupEntry kind;
2280
2281 String8 remain;
2282 if (entryName.walkPath(&remain) == kResourceDir) {
2283 // these are the resources, pull their type out of the directory name
2284 kind.initFromDirName(remain.walkPath().string(), &resType);
2285 } else {
2286 // these are untyped and don't have an AaptGroupEntry
2287 }
2288 if (entries.indexOf(kind) < 0) {
2289 entries.add(kind);
2290 mGroupEntries.add(kind);
2291 }
2292
2293 // use the one from the zip file if they both exist.
2294 dir->removeFile(entryName.getPathLeaf());
2295
2296 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2297 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2298 if (err != NO_ERROR) {
2299 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2300 count = err;
2301 goto bail;
2302 }
2303 file->setCompressionMethod(entry->getCompressionMethod());
2304
2305#if 0
2306 if (entryName == "AndroidManifest.xml") {
2307 printf("AndroidManifest.xml\n");
2308 }
2309 printf("\n\nfile: %s\n", entryName.string());
2310#endif
2311
2312 size_t len = entry->getUncompressedLen();
2313 void* data = zip->uncompress(entry);
2314 void* buf = file->editData(len);
2315 memcpy(buf, data, len);
2316
2317#if 0
2318 const int OFF = 0;
2319 const unsigned char* p = (unsigned char*)data;
2320 const unsigned char* end = p+len;
2321 p += OFF;
2322 for (int i=0; i<32 && p < end; i++) {
2323 printf("0x%03x ", i*0x10 + OFF);
2324 for (int j=0; j<0x10 && p < end; j++) {
2325 printf(" %02x", *p);
2326 p++;
2327 }
2328 printf("\n");
2329 }
2330#endif
2331
2332 free(data);
2333
2334 count++;
2335 }
2336
2337bail:
2338 delete zip;
2339 return count;
2340}
2341
e29f4ada
DH
2342status_t AaptAssets::filter(Bundle* bundle)
2343{
2344 ResourceFilter reqFilter;
2345 status_t err = reqFilter.parse(bundle->getConfigurations());
2346 if (err != NO_ERROR) {
2347 return err;
2348 }
2349
2350 ResourceFilter prefFilter;
2351 err = prefFilter.parse(bundle->getPreferredConfigurations());
2352 if (err != NO_ERROR) {
2353 return err;
2354 }
2355
2356 if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2357 return NO_ERROR;
2358 }
2359
9fbf0ad5 2360 if (bundle->getVerbose()) {
e29f4ada
DH
2361 if (!reqFilter.isEmpty()) {
2362 printf("Applying required filter: %s\n",
2363 bundle->getConfigurations());
2364 }
2365 if (!prefFilter.isEmpty()) {
2366 printf("Applying preferred filter: %s\n",
2367 bundle->getPreferredConfigurations());
2368 }
2369 }
2370
2371 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2372 const size_t ND = resdirs.size();
2373 for (size_t i=0; i<ND; i++) {
2374 const sp<AaptDir>& dir = resdirs.itemAt(i);
2375 if (dir->getLeaf() == kValuesDir) {
2376 // The "value" dir is special since a single file defines
2377 // multiple resources, so we can not do filtering on the
2378 // files themselves.
2379 continue;
2380 }
2381 if (dir->getLeaf() == kMipmapDir) {
2382 // We also skip the "mipmap" directory, since the point of this
2383 // is to include all densities without stripping. If you put
2384 // other configurations in here as well they won't be stripped
2385 // either... So don't do that. Seriously. What is wrong with you?
2386 continue;
2387 }
2388
2389 const size_t NG = dir->getFiles().size();
2390 for (size_t j=0; j<NG; j++) {
2391 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2392
2393 // First remove any configurations we know we don't need.
2394 for (size_t k=0; k<grp->getFiles().size(); k++) {
2395 sp<AaptFile> file = grp->getFiles().valueAt(k);
2396 if (k == 0 && grp->getFiles().size() == 1) {
2397 // If this is the only file left, we need to keep it.
2398 // Otherwise the resource IDs we are using will be inconsistent
2399 // with what we get when not stripping. Sucky, but at least
2400 // for now we can rely on the back-end doing another filtering
2401 // pass to take this out and leave us with this resource name
2402 // containing no entries.
2403 continue;
2404 }
2405 if (file->getPath().getPathExtension() == ".xml") {
2406 // We can't remove .xml files at this point, because when
2407 // we parse them they may add identifier resources, so
2408 // removing them can cause our resource identifiers to
2409 // become inconsistent.
2410 continue;
2411 }
2412 const ResTable_config& config(file->getGroupEntry().toParams());
2413 if (!reqFilter.match(config)) {
2414 if (bundle->getVerbose()) {
2415 printf("Pruning unneeded resource: %s\n",
2416 file->getPrintableSource().string());
2417 }
2418 grp->removeFile(k);
2419 k--;
2420 }
2421 }
2422
2423 // Quick check: no preferred filters, nothing more to do.
2424 if (prefFilter.isEmpty()) {
2425 continue;
2426 }
2427
2428 // Now deal with preferred configurations.
2429 for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2430 for (size_t k=0; k<grp->getFiles().size(); k++) {
2431 sp<AaptFile> file = grp->getFiles().valueAt(k);
2432 if (k == 0 && grp->getFiles().size() == 1) {
2433 // If this is the only file left, we need to keep it.
2434 // Otherwise the resource IDs we are using will be inconsistent
2435 // with what we get when not stripping. Sucky, but at least
2436 // for now we can rely on the back-end doing another filtering
2437 // pass to take this out and leave us with this resource name
2438 // containing no entries.
2439 continue;
2440 }
2441 if (file->getPath().getPathExtension() == ".xml") {
2442 // We can't remove .xml files at this point, because when
2443 // we parse them they may add identifier resources, so
2444 // removing them can cause our resource identifiers to
2445 // become inconsistent.
2446 continue;
2447 }
2448 const ResTable_config& config(file->getGroupEntry().toParams());
2449 if (!prefFilter.match(axis, config)) {
2450 // This is a resource we would prefer not to have. Check
2451 // to see if have a similar variation that we would like
2452 // to have and, if so, we can drop it.
2453 for (size_t m=0; m<grp->getFiles().size(); m++) {
2454 if (m == k) continue;
2455 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2456 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2457 if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2458 if (prefFilter.match(axis, mconfig)) {
2459 if (bundle->getVerbose()) {
2460 printf("Pruning unneeded resource: %s\n",
2461 file->getPrintableSource().string());
2462 }
2463 grp->removeFile(k);
2464 k--;
2465 break;
2466 }
2467 }
2468 }
2469 }
2470 }
2471 }
2472 }
2473 }
2474
2475 return NO_ERROR;
2476}
2477
a534180c
TAOSP
2478sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2479{
2480 sp<AaptSymbols> sym = mSymbols.valueFor(name);
2481 if (sym == NULL) {
2482 sym = new AaptSymbols();
2483 mSymbols.add(name, sym);
2484 }
2485 return sym;
2486}
2487
6415576e
DH
2488sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2489{
2490 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2491 if (sym == NULL) {
2492 sym = new AaptSymbols();
2493 mJavaSymbols.add(name, sym);
2494 }
2495 return sym;
2496}
2497
2498status_t AaptAssets::applyJavaSymbols()
2499{
2500 size_t N = mJavaSymbols.size();
2501 for (size_t i=0; i<N; i++) {
2502 const String8& name = mJavaSymbols.keyAt(i);
2503 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2504 ssize_t pos = mSymbols.indexOfKey(name);
2505 if (pos < 0) {
2506 SourcePos pos;
2507 pos.error("Java symbol dir %s not defined\n", name.string());
2508 return UNKNOWN_ERROR;
2509 }
2510 //printf("**** applying java symbols in dir %s\n", name.string());
2511 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2512 if (err != NO_ERROR) {
2513 return err;
2514 }
2515 }
2516
2517 return NO_ERROR;
2518}
2519
2520bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2521 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2522 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2523 // sym.isJavaSymbol ? 1 : 0);
2524 if (!mHavePrivateSymbols) return true;
2525 if (sym.isPublic) return true;
2526 if (includePrivate && sym.isJavaSymbol) return true;
2527 return false;
2528}
2529
a534180c
TAOSP
2530status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2531{
2532 if (!mHaveIncludedAssets) {
2533 // Add in all includes.
2534 const Vector<const char*>& incl = bundle->getPackageIncludes();
2535 const size_t N=incl.size();
2536 for (size_t i=0; i<N; i++) {
2537 if (bundle->getVerbose())
2538 printf("Including resources from package: %s\n", incl[i]);
2539 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2540 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2541 incl[i]);
2542 return UNKNOWN_ERROR;
2543 }
2544 }
2545 mHaveIncludedAssets = true;
2546 }
2547
2548 return NO_ERROR;
2549}
2550
2551status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2552{
2553 const ResTable& res = getIncludedResources();
2554 // XXX dirty!
2555 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2556}
2557
2558const ResTable& AaptAssets::getIncludedResources() const
2559{
2560 return mIncludedAssets.getResources(false);
2561}
2562
e29f4ada 2563void AaptAssets::print(const String8& prefix) const
a534180c 2564{
e29f4ada
DH
2565 String8 innerPrefix(prefix);
2566 innerPrefix.append(" ");
2567 String8 innerInnerPrefix(innerPrefix);
2568 innerInnerPrefix.append(" ");
2569 printf("%sConfigurations:\n", prefix.string());
a534180c
TAOSP
2570 const size_t N=mGroupEntries.size();
2571 for (size_t i=0; i<N; i++) {
e29f4ada
DH
2572 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2573 printf("%s %s\n", prefix.string(),
2574 cname != "" ? cname.string() : "(default)");
a534180c
TAOSP
2575 }
2576
e29f4ada
DH
2577 printf("\n%sFiles:\n", prefix.string());
2578 AaptDir::print(innerPrefix);
2579
2580 printf("\n%sResource Dirs:\n", prefix.string());
2581 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2582 const size_t NR = resdirs.size();
2583 for (size_t i=0; i<NR; i++) {
2584 const sp<AaptDir>& d = resdirs.itemAt(i);
2585 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
2586 d->print(innerInnerPrefix);
2587 }
a534180c
TAOSP
2588}
2589
e29f4ada 2590sp<AaptDir> AaptAssets::resDir(const String8& name) const
6648ff78 2591{
e29f4ada
DH
2592 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2593 const size_t N = resdirs.size();
6648ff78 2594 for (size_t i=0; i<N; i++) {
e29f4ada 2595 const sp<AaptDir>& d = resdirs.itemAt(i);
6648ff78
JO
2596 if (d->getLeaf() == name) {
2597 return d;
2598 }
2599 }
2600 return NULL;
2601}
2602
a534180c
TAOSP
2603bool
2604valid_symbol_name(const String8& symbol)
2605{
2606 static char const * const KEYWORDS[] = {
2607 "abstract", "assert", "boolean", "break",
2608 "byte", "case", "catch", "char", "class", "const", "continue",
2609 "default", "do", "double", "else", "enum", "extends", "final",
2610 "finally", "float", "for", "goto", "if", "implements", "import",
2611 "instanceof", "int", "interface", "long", "native", "new", "package",
2612 "private", "protected", "public", "return", "short", "static",
2613 "strictfp", "super", "switch", "synchronized", "this", "throw",
2614 "throws", "transient", "try", "void", "volatile", "while",
2615 "true", "false", "null",
2616 NULL
2617 };
2618 const char*const* k = KEYWORDS;
2619 const char*const s = symbol.string();
2620 while (*k) {
2621 if (0 == strcmp(s, *k)) {
2622 return false;
2623 }
2624 k++;
2625 }
2626 return true;
2627}