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