blob: 729a048be56c1162a086d23c886c9e662cf7d6e4 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001//
2// Copyright 2006 The Android Open Source Project
3//
4
5#include "AaptAssets.h"
Dianne Hackborne6b68032011-10-13 16:26:02 -07006#include "ResourceFilter.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007#include "Main.h"
8
9#include <utils/misc.h>
10#include <utils/SortedVector.h>
11
12#include <ctype.h>
13#include <dirent.h>
14#include <errno.h>
15
16static const char* kDefaultLocale = "default";
17static const char* kWildcardName = "any";
18static const char* kAssetDir = "assets";
19static const char* kResourceDir = "res";
Dianne Hackborne6b68032011-10-13 16:26:02 -070020static const char* kValuesDir = "values";
21static const char* kMipmapDir = "mipmap";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022static const char* kInvalidChars = "/\\:";
23static const size_t kMaxAssetFileName = 100;
24
25static const String8 kResString(kResourceDir);
26
27/*
28 * Names of asset files must meet the following criteria:
29 *
30 * - the filename length must be less than kMaxAssetFileName bytes long
31 * (and can't be empty)
32 * - all characters must be 7-bit printable ASCII
33 * - none of { '/' '\\' ':' }
34 *
35 * Pass in just the filename, not the full path.
36 */
37static bool validateFileName(const char* fileName)
38{
39 const char* cp = fileName;
40 size_t len = 0;
41
42 while (*cp != '\0') {
43 if ((*cp & 0x80) != 0)
44 return false; // reject high ASCII
45 if (*cp < 0x20 || *cp >= 0x7f)
46 return false; // reject control chars and 0x7f
47 if (strchr(kInvalidChars, *cp) != NULL)
48 return false; // reject path sep chars
49 cp++;
50 len++;
51 }
52
53 if (len < 1 || len > kMaxAssetFileName)
54 return false; // reject empty or too long
55
56 return true;
57}
58
Raphael Moll6c255a32012-05-07 16:16:46 -070059// The default to use if no other ignore pattern is defined.
60const char * const gDefaultIgnoreAssets =
Tor Norbyee0219c82012-06-04 10:38:13 -070061 "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
Raphael Moll6c255a32012-05-07 16:16:46 -070062// The ignore pattern that can be passed via --ignore-assets in Main.cpp
63const char * gUserIgnoreAssets = NULL;
64
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065static bool isHidden(const char *root, const char *path)
66{
Raphael Moll6c255a32012-05-07 16:16:46 -070067 // Patterns syntax:
68 // - Delimiter is :
69 // - Entry can start with the flag ! to avoid printing a warning
70 // about the file being ignored.
71 // - Entry can have the flag "<dir>" to match only directories
72 // or <file> to match only files. Default is to match both.
73 // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
74 // where prefix/suffix must have at least 1 character (so that
75 // we don't match a '*' catch-all pattern.)
76 // - The special filenames "." and ".." are always ignored.
77 // - Otherwise the full string is matched.
78 // - match is not case-sensitive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
Raphael Moll6c255a32012-05-07 16:16:46 -070080 if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 }
83
Raphael Moll6c255a32012-05-07 16:16:46 -070084 const char *delim = ":";
85 const char *p = gUserIgnoreAssets;
86 if (!p || !p[0]) {
87 p = getenv("ANDROID_AAPT_IGNORE");
88 }
89 if (!p || !p[0]) {
90 p = gDefaultIgnoreAssets;
91 }
92 char *patterns = strdup(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093
Raphael Moll6c255a32012-05-07 16:16:46 -070094 bool ignore = false;
95 bool chatty = true;
96 char *matchedPattern = NULL;
97
98 String8 fullPath(root);
99 fullPath.appendPath(path);
100 FileType type = getFileType(fullPath);
101
102 int plen = strlen(path);
103
104 // Note: we don't have strtok_r under mingw.
105 for(char *token = strtok(patterns, delim);
106 !ignore && token != NULL;
107 token = strtok(NULL, delim)) {
108 chatty = token[0] != '!';
109 if (!chatty) token++; // skip !
110 if (strncasecmp(token, "<dir>" , 5) == 0) {
111 if (type != kFileTypeDirectory) continue;
112 token += 5;
113 }
114 if (strncasecmp(token, "<file>", 6) == 0) {
115 if (type != kFileTypeRegular) continue;
116 token += 6;
117 }
118
119 matchedPattern = token;
120 int n = strlen(token);
121
122 if (token[0] == '*') {
123 // Match *suffix
124 token++;
Ying Wang996b0732012-05-22 11:24:22 -0700125 n--;
Raphael Moll6c255a32012-05-07 16:16:46 -0700126 if (n <= plen) {
127 ignore = strncasecmp(token, path + plen - n, n) == 0;
128 }
129 } else if (n > 1 && token[n - 1] == '*') {
130 // Match prefix*
131 ignore = strncasecmp(token, path, n - 1) == 0;
132 } else {
133 ignore = strcasecmp(token, path) == 0;
134 }
135 }
136
137 if (ignore && chatty) {
138 fprintf(stderr, " (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
139 type == kFileTypeDirectory ? "dir" : "file",
140 path,
141 matchedPattern ? matchedPattern : "");
142 }
143
144 free(patterns);
145 return ignore;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146}
147
148// =========================================================================
149// =========================================================================
150// =========================================================================
151
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
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700186 // layout direction
187 if (getLayoutDirectionName(part.string(), &config)) {
188 *axis = AXIS_LAYOUTDIR;
189 *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
190 return 0;
191 }
192
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700193 // smallest screen dp width
194 if (getSmallestScreenWidthDpName(part.string(), &config)) {
195 *axis = AXIS_SMALLESTSCREENWIDTHDP;
196 *value = config.smallestScreenWidthDp;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700197 return 0;
198 }
199
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700200 // screen dp width
201 if (getScreenWidthDpName(part.string(), &config)) {
202 *axis = AXIS_SCREENWIDTHDP;
203 *value = config.screenWidthDp;
204 return 0;
205 }
206
207 // screen dp height
208 if (getScreenHeightDpName(part.string(), &config)) {
209 *axis = AXIS_SCREENHEIGHTDP;
210 *value = config.screenHeightDp;
211 return 0;
212 }
213
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700214 // screen layout size
215 if (getScreenLayoutSizeName(part.string(), &config)) {
216 *axis = AXIS_SCREENLAYOUTSIZE;
217 *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
218 return 0;
219 }
220
221 // screen layout long
222 if (getScreenLayoutLongName(part.string(), &config)) {
223 *axis = AXIS_SCREENLAYOUTLONG;
224 *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
225 return 0;
226 }
227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 // orientation
229 if (getOrientationName(part.string(), &config)) {
230 *axis = AXIS_ORIENTATION;
231 *value = config.orientation;
232 return 0;
233 }
234
Tobias Haamel27b28b32010-02-09 23:09:17 +0100235 // ui mode type
236 if (getUiModeTypeName(part.string(), &config)) {
237 *axis = AXIS_UIMODETYPE;
238 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
239 return 0;
240 }
241
242 // ui mode night
243 if (getUiModeNightName(part.string(), &config)) {
244 *axis = AXIS_UIMODENIGHT;
245 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
246 return 0;
247 }
248
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 // density
250 if (getDensityName(part.string(), &config)) {
251 *axis = AXIS_DENSITY;
252 *value = config.density;
253 return 0;
254 }
255
256 // touchscreen
257 if (getTouchscreenName(part.string(), &config)) {
258 *axis = AXIS_TOUCHSCREEN;
259 *value = config.touchscreen;
260 return 0;
261 }
262
263 // keyboard hidden
264 if (getKeysHiddenName(part.string(), &config)) {
265 *axis = AXIS_KEYSHIDDEN;
266 *value = config.inputFlags;
267 return 0;
268 }
269
270 // keyboard
271 if (getKeyboardName(part.string(), &config)) {
272 *axis = AXIS_KEYBOARD;
273 *value = config.keyboard;
274 return 0;
275 }
276
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700277 // navigation hidden
278 if (getNavHiddenName(part.string(), &config)) {
279 *axis = AXIS_NAVHIDDEN;
280 *value = config.inputFlags;
281 return 0;
282 }
283
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 // navigation
285 if (getNavigationName(part.string(), &config)) {
286 *axis = AXIS_NAVIGATION;
287 *value = config.navigation;
288 return 0;
289 }
290
291 // screen size
292 if (getScreenSizeName(part.string(), &config)) {
293 *axis = AXIS_SCREENSIZE;
294 *value = config.screenSize;
295 return 0;
296 }
297
298 // version
299 if (getVersionName(part.string(), &config)) {
300 *axis = AXIS_VERSION;
301 *value = config.version;
302 return 0;
303 }
304
305 return 1;
306}
307
Dianne Hackborne6b68032011-10-13 16:26:02 -0700308uint32_t
309AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
310{
311 switch (axis) {
312 case AXIS_MCC:
313 return config.mcc;
314 case AXIS_MNC:
315 return config.mnc;
316 case AXIS_LANGUAGE:
317 return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
318 | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700319 case AXIS_LAYOUTDIR:
320 return config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700321 case AXIS_SCREENLAYOUTSIZE:
322 return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
323 case AXIS_ORIENTATION:
324 return config.orientation;
325 case AXIS_UIMODETYPE:
326 return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
327 case AXIS_UIMODENIGHT:
328 return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
329 case AXIS_DENSITY:
330 return config.density;
331 case AXIS_TOUCHSCREEN:
332 return config.touchscreen;
333 case AXIS_KEYSHIDDEN:
334 return config.inputFlags;
335 case AXIS_KEYBOARD:
336 return config.keyboard;
337 case AXIS_NAVIGATION:
338 return config.navigation;
339 case AXIS_SCREENSIZE:
340 return config.screenSize;
341 case AXIS_SMALLESTSCREENWIDTHDP:
342 return config.smallestScreenWidthDp;
343 case AXIS_SCREENWIDTHDP:
344 return config.screenWidthDp;
345 case AXIS_SCREENHEIGHTDP:
346 return config.screenHeightDp;
347 case AXIS_VERSION:
348 return config.version;
349 }
350 return 0;
351}
352
353bool
354AaptGroupEntry::configSameExcept(const ResTable_config& config,
355 const ResTable_config& otherConfig, int axis)
356{
357 for (int i=AXIS_START; i<=AXIS_END; i++) {
358 if (i == axis) {
359 continue;
360 }
361 if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
362 return false;
363 }
364 }
365 return true;
366}
367
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368bool
369AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
370{
Dianne Hackborne6b68032011-10-13 16:26:02 -0700371 mParamsChanged = true;
372
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 Vector<String8> parts;
374
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700375 String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700376 String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700377 String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378
379 const char *p = dir;
380 const char *q;
381 while (NULL != (q = strchr(p, '-'))) {
382 String8 val(p, q-p);
383 val.toLower();
384 parts.add(val);
385 //printf("part: %s\n", parts[parts.size()-1].string());
386 p = q+1;
387 }
388 String8 val(p);
389 val.toLower();
390 parts.add(val);
391 //printf("part: %s\n", parts[parts.size()-1].string());
392
393 const int N = parts.size();
394 int index = 0;
395 String8 part = parts[index];
396
397 // resource type
398 if (!isValidResourceType(part)) {
399 return false;
400 }
401 *resType = part;
402
403 index++;
404 if (index == N) {
405 goto success;
406 }
407 part = parts[index];
408
409 // imsi - mcc
410 if (getMccName(part.string())) {
411 mcc = part;
412
413 index++;
414 if (index == N) {
415 goto success;
416 }
417 part = parts[index];
418 } else {
419 //printf("not mcc: %s\n", part.string());
420 }
421
422 // imsi - mnc
423 if (getMncName(part.string())) {
424 mnc = part;
425
426 index++;
427 if (index == N) {
428 goto success;
429 }
430 part = parts[index];
431 } else {
432 //printf("not mcc: %s\n", part.string());
433 }
434
435 // locale - language
436 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
437 loc = part;
438
439 index++;
440 if (index == N) {
441 goto success;
442 }
443 part = parts[index];
444 } else {
445 //printf("not language: %s\n", part.string());
446 }
447
448 // locale - region
449 if (loc.length() > 0
450 && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
451 loc += "-";
452 part.toUpper();
453 loc += part.string() + 1;
454
455 index++;
456 if (index == N) {
457 goto success;
458 }
459 part = parts[index];
460 } else {
461 //printf("not region: %s\n", part.string());
462 }
463
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700464 if (getLayoutDirectionName(part.string())) {
465 layoutDir = part;
466
467 index++;
468 if (index == N) {
469 goto success;
470 }
471 part = parts[index];
472 } else {
473 //printf("not layout direction: %s\n", part.string());
474 }
475
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700476 if (getSmallestScreenWidthDpName(part.string())) {
477 smallestwidthdp = part;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700478
479 index++;
480 if (index == N) {
481 goto success;
482 }
483 part = parts[index];
484 } else {
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700485 //printf("not smallest screen width dp: %s\n", part.string());
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700486 }
487
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700488 if (getScreenWidthDpName(part.string())) {
489 widthdp = part;
490
491 index++;
492 if (index == N) {
493 goto success;
494 }
495 part = parts[index];
496 } else {
497 //printf("not screen width dp: %s\n", part.string());
498 }
499
500 if (getScreenHeightDpName(part.string())) {
501 heightdp = part;
502
503 index++;
504 if (index == N) {
505 goto success;
506 }
507 part = parts[index];
508 } else {
509 //printf("not screen height dp: %s\n", part.string());
510 }
511
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700512 if (getScreenLayoutSizeName(part.string())) {
513 layoutsize = part;
514
515 index++;
516 if (index == N) {
517 goto success;
518 }
519 part = parts[index];
520 } else {
521 //printf("not screen layout size: %s\n", part.string());
522 }
523
524 if (getScreenLayoutLongName(part.string())) {
525 layoutlong = part;
526
527 index++;
528 if (index == N) {
529 goto success;
530 }
531 part = parts[index];
532 } else {
533 //printf("not screen layout long: %s\n", part.string());
534 }
535
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 // orientation
537 if (getOrientationName(part.string())) {
538 orient = part;
539
540 index++;
541 if (index == N) {
542 goto success;
543 }
544 part = parts[index];
545 } else {
546 //printf("not orientation: %s\n", part.string());
547 }
548
Tobias Haamel27b28b32010-02-09 23:09:17 +0100549 // ui mode type
550 if (getUiModeTypeName(part.string())) {
551 uiModeType = part;
552
553 index++;
554 if (index == N) {
555 goto success;
556 }
557 part = parts[index];
558 } else {
559 //printf("not ui mode type: %s\n", part.string());
560 }
561
562 // ui mode night
563 if (getUiModeNightName(part.string())) {
564 uiModeNight = part;
565
566 index++;
567 if (index == N) {
568 goto success;
569 }
570 part = parts[index];
571 } else {
572 //printf("not ui mode night: %s\n", part.string());
573 }
574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 // density
576 if (getDensityName(part.string())) {
577 den = part;
578
579 index++;
580 if (index == N) {
581 goto success;
582 }
583 part = parts[index];
584 } else {
585 //printf("not density: %s\n", part.string());
586 }
587
588 // touchscreen
589 if (getTouchscreenName(part.string())) {
590 touch = part;
591
592 index++;
593 if (index == N) {
594 goto success;
595 }
596 part = parts[index];
597 } else {
598 //printf("not touchscreen: %s\n", part.string());
599 }
600
601 // keyboard hidden
602 if (getKeysHiddenName(part.string())) {
603 keysHidden = part;
604
605 index++;
606 if (index == N) {
607 goto success;
608 }
609 part = parts[index];
610 } else {
611 //printf("not keysHidden: %s\n", part.string());
612 }
613
614 // keyboard
615 if (getKeyboardName(part.string())) {
616 key = part;
617
618 index++;
619 if (index == N) {
620 goto success;
621 }
622 part = parts[index];
623 } else {
624 //printf("not keyboard: %s\n", part.string());
625 }
626
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700627 // navigation hidden
628 if (getNavHiddenName(part.string())) {
629 navHidden = part;
630
631 index++;
632 if (index == N) {
633 goto success;
634 }
635 part = parts[index];
636 } else {
637 //printf("not navHidden: %s\n", part.string());
638 }
639
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 if (getNavigationName(part.string())) {
641 nav = part;
642
643 index++;
644 if (index == N) {
645 goto success;
646 }
647 part = parts[index];
648 } else {
649 //printf("not navigation: %s\n", part.string());
650 }
651
652 if (getScreenSizeName(part.string())) {
653 size = part;
654
655 index++;
656 if (index == N) {
657 goto success;
658 }
659 part = parts[index];
660 } else {
661 //printf("not screen size: %s\n", part.string());
662 }
663
664 if (getVersionName(part.string())) {
665 vers = part;
666
667 index++;
668 if (index == N) {
669 goto success;
670 }
671 part = parts[index];
672 } else {
673 //printf("not version: %s\n", part.string());
674 }
675
676 // if there are extra parts, it doesn't match
677 return false;
678
679success:
680 this->mcc = mcc;
681 this->mnc = mnc;
682 this->locale = loc;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700683 this->screenLayoutSize = layoutsize;
684 this->screenLayoutLong = layoutlong;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700685 this->smallestScreenWidthDp = smallestwidthdp;
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700686 this->screenWidthDp = widthdp;
687 this->screenHeightDp = heightdp;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 this->orientation = orient;
Tobias Haamel27b28b32010-02-09 23:09:17 +0100689 this->uiModeType = uiModeType;
690 this->uiModeNight = uiModeNight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 this->density = den;
692 this->touchscreen = touch;
693 this->keysHidden = keysHidden;
694 this->keyboard = key;
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700695 this->navHidden = navHidden;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 this->navigation = nav;
697 this->screenSize = size;
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700698 this->layoutDirection = layoutDir;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 this->version = vers;
700
701 // what is this anyway?
702 this->vendor = "";
703
704 return true;
705}
706
707String8
708AaptGroupEntry::toString() const
709{
710 String8 s = this->mcc;
711 s += ",";
712 s += this->mnc;
713 s += ",";
714 s += this->locale;
715 s += ",";
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700716 s += layoutDirection;
717 s += ",";
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700718 s += smallestScreenWidthDp;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700719 s += ",";
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700720 s += screenWidthDp;
721 s += ",";
722 s += screenHeightDp;
723 s += ",";
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700724 s += screenLayoutSize;
725 s += ",";
726 s += screenLayoutLong;
727 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 s += this->orientation;
729 s += ",";
Tobias Haamel27b28b32010-02-09 23:09:17 +0100730 s += uiModeType;
731 s += ",";
732 s += uiModeNight;
733 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 s += density;
735 s += ",";
736 s += touchscreen;
737 s += ",";
738 s += keysHidden;
739 s += ",";
740 s += keyboard;
741 s += ",";
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700742 s += navHidden;
743 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 s += navigation;
745 s += ",";
746 s += screenSize;
747 s += ",";
748 s += version;
749 return s;
750}
751
752String8
753AaptGroupEntry::toDirName(const String8& resType) const
754{
755 String8 s = resType;
756 if (this->mcc != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700757 if (s.length() > 0) {
758 s += "-";
759 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 s += mcc;
761 }
762 if (this->mnc != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700763 if (s.length() > 0) {
764 s += "-";
765 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 s += mnc;
767 }
768 if (this->locale != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700769 if (s.length() > 0) {
770 s += "-";
771 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 s += locale;
773 }
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700774 if (this->layoutDirection != "") {
775 if (s.length() > 0) {
776 s += "-";
777 }
778 s += layoutDirection;
779 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700780 if (this->smallestScreenWidthDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700781 if (s.length() > 0) {
782 s += "-";
783 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700784 s += smallestScreenWidthDp;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700785 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700786 if (this->screenWidthDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700787 if (s.length() > 0) {
788 s += "-";
789 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700790 s += screenWidthDp;
791 }
792 if (this->screenHeightDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700793 if (s.length() > 0) {
794 s += "-";
795 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700796 s += screenHeightDp;
797 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700798 if (this->screenLayoutSize != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700799 if (s.length() > 0) {
800 s += "-";
801 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700802 s += screenLayoutSize;
803 }
804 if (this->screenLayoutLong != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700805 if (s.length() > 0) {
806 s += "-";
807 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700808 s += screenLayoutLong;
809 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 if (this->orientation != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700811 if (s.length() > 0) {
812 s += "-";
813 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 s += orientation;
815 }
Tobias Haamel27b28b32010-02-09 23:09:17 +0100816 if (this->uiModeType != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700817 if (s.length() > 0) {
818 s += "-";
819 }
Tobias Haamel27b28b32010-02-09 23:09:17 +0100820 s += uiModeType;
821 }
822 if (this->uiModeNight != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700823 if (s.length() > 0) {
824 s += "-";
825 }
Tobias Haamel27b28b32010-02-09 23:09:17 +0100826 s += uiModeNight;
827 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 if (this->density != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700829 if (s.length() > 0) {
830 s += "-";
831 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 s += density;
833 }
834 if (this->touchscreen != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700835 if (s.length() > 0) {
836 s += "-";
837 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 s += touchscreen;
839 }
840 if (this->keysHidden != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700841 if (s.length() > 0) {
842 s += "-";
843 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 s += keysHidden;
845 }
846 if (this->keyboard != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700847 if (s.length() > 0) {
848 s += "-";
849 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 s += keyboard;
851 }
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700852 if (this->navHidden != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700853 if (s.length() > 0) {
854 s += "-";
855 }
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700856 s += navHidden;
857 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800858 if (this->navigation != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700859 if (s.length() > 0) {
860 s += "-";
861 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 s += navigation;
863 }
864 if (this->screenSize != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700865 if (s.length() > 0) {
866 s += "-";
867 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 s += screenSize;
869 }
870 if (this->version != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700871 if (s.length() > 0) {
872 s += "-";
873 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 s += version;
875 }
876
877 return s;
878}
879
880bool AaptGroupEntry::getMccName(const char* name,
881 ResTable_config* out)
882{
883 if (strcmp(name, kWildcardName) == 0) {
884 if (out) out->mcc = 0;
885 return true;
886 }
887 const char* c = name;
888 if (tolower(*c) != 'm') return false;
889 c++;
890 if (tolower(*c) != 'c') return false;
891 c++;
892 if (tolower(*c) != 'c') return false;
893 c++;
894
895 const char* val = c;
896
897 while (*c >= '0' && *c <= '9') {
898 c++;
899 }
900 if (*c != 0) return false;
901 if (c-val != 3) return false;
902
903 int d = atoi(val);
904 if (d != 0) {
905 if (out) out->mcc = d;
906 return true;
907 }
908
909 return false;
910}
911
912bool AaptGroupEntry::getMncName(const char* name,
913 ResTable_config* out)
914{
915 if (strcmp(name, kWildcardName) == 0) {
916 if (out) out->mcc = 0;
917 return true;
918 }
919 const char* c = name;
920 if (tolower(*c) != 'm') return false;
921 c++;
922 if (tolower(*c) != 'n') return false;
923 c++;
924 if (tolower(*c) != 'c') return false;
925 c++;
926
927 const char* val = c;
928
929 while (*c >= '0' && *c <= '9') {
930 c++;
931 }
932 if (*c != 0) return false;
933 if (c-val == 0 || c-val > 3) return false;
934
Johan Redestig5ef0b9d2010-11-09 14:13:31 +0100935 if (out) {
936 out->mnc = atoi(val);
Mattias Petersson1d766b52011-10-07 09:33:52 +0200937 if (out->mnc == 0) {
938 out->mnc = ACONFIGURATION_MNC_ZERO;
939 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 }
941
Johan Redestig5ef0b9d2010-11-09 14:13:31 +0100942 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943}
944
945/*
946 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
947 * "default")?
948 *
949 * TODO: Should insist that the first two letters are lower case, and the
950 * second two are upper.
951 */
952bool AaptGroupEntry::getLocaleName(const char* fileName,
953 ResTable_config* out)
954{
955 if (strcmp(fileName, kWildcardName) == 0
956 || strcmp(fileName, kDefaultLocale) == 0) {
957 if (out) {
958 out->language[0] = 0;
959 out->language[1] = 0;
960 out->country[0] = 0;
961 out->country[1] = 0;
962 }
963 return true;
964 }
965
966 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
967 if (out) {
968 out->language[0] = fileName[0];
969 out->language[1] = fileName[1];
970 out->country[0] = 0;
971 out->country[1] = 0;
972 }
973 return true;
974 }
975
976 if (strlen(fileName) == 5 &&
977 isalpha(fileName[0]) &&
978 isalpha(fileName[1]) &&
979 fileName[2] == '-' &&
980 isalpha(fileName[3]) &&
981 isalpha(fileName[4])) {
982 if (out) {
983 out->language[0] = fileName[0];
984 out->language[1] = fileName[1];
985 out->country[0] = fileName[3];
986 out->country[1] = fileName[4];
987 }
988 return true;
989 }
990
991 return false;
992}
993
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700994bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
995{
996 if (strcmp(name, kWildcardName) == 0) {
997 if (out) out->screenLayout =
998 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
999 | ResTable_config::LAYOUTDIR_ANY;
1000 return true;
Fabrice Di Meglio8a802db2012-09-05 13:12:02 -07001001 } else if (strcmp(name, "ldltr") == 0) {
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001002 if (out) out->screenLayout =
1003 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1004 | ResTable_config::LAYOUTDIR_LTR;
1005 return true;
Fabrice Di Meglio8a802db2012-09-05 13:12:02 -07001006 } else if (strcmp(name, "ldrtl") == 0) {
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001007 if (out) out->screenLayout =
1008 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1009 | ResTable_config::LAYOUTDIR_RTL;
1010 return true;
1011 }
1012
1013 return false;
1014}
1015
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001016bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
1017 ResTable_config* out)
1018{
1019 if (strcmp(name, kWildcardName) == 0) {
1020 if (out) out->screenLayout =
1021 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1022 | ResTable_config::SCREENSIZE_ANY;
1023 return true;
1024 } else if (strcmp(name, "small") == 0) {
1025 if (out) out->screenLayout =
1026 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1027 | ResTable_config::SCREENSIZE_SMALL;
1028 return true;
1029 } else if (strcmp(name, "normal") == 0) {
1030 if (out) out->screenLayout =
1031 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1032 | ResTable_config::SCREENSIZE_NORMAL;
1033 return true;
1034 } else if (strcmp(name, "large") == 0) {
1035 if (out) out->screenLayout =
1036 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1037 | ResTable_config::SCREENSIZE_LARGE;
1038 return true;
Dianne Hackborn14cee9f2010-04-23 17:51:26 -07001039 } else if (strcmp(name, "xlarge") == 0) {
1040 if (out) out->screenLayout =
1041 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1042 | ResTable_config::SCREENSIZE_XLARGE;
1043 return true;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001044 }
1045
1046 return false;
1047}
1048
1049bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
1050 ResTable_config* out)
1051{
1052 if (strcmp(name, kWildcardName) == 0) {
1053 if (out) out->screenLayout =
1054 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1055 | ResTable_config::SCREENLONG_ANY;
1056 return true;
1057 } else if (strcmp(name, "long") == 0) {
1058 if (out) out->screenLayout =
1059 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1060 | ResTable_config::SCREENLONG_YES;
1061 return true;
1062 } else if (strcmp(name, "notlong") == 0) {
1063 if (out) out->screenLayout =
1064 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1065 | ResTable_config::SCREENLONG_NO;
1066 return true;
1067 }
1068
1069 return false;
1070}
1071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072bool AaptGroupEntry::getOrientationName(const char* name,
1073 ResTable_config* out)
1074{
1075 if (strcmp(name, kWildcardName) == 0) {
1076 if (out) out->orientation = out->ORIENTATION_ANY;
1077 return true;
1078 } else if (strcmp(name, "port") == 0) {
1079 if (out) out->orientation = out->ORIENTATION_PORT;
1080 return true;
1081 } else if (strcmp(name, "land") == 0) {
1082 if (out) out->orientation = out->ORIENTATION_LAND;
1083 return true;
1084 } else if (strcmp(name, "square") == 0) {
1085 if (out) out->orientation = out->ORIENTATION_SQUARE;
1086 return true;
1087 }
1088
1089 return false;
1090}
1091
Tobias Haamel27b28b32010-02-09 23:09:17 +01001092bool AaptGroupEntry::getUiModeTypeName(const char* name,
1093 ResTable_config* out)
1094{
1095 if (strcmp(name, kWildcardName) == 0) {
1096 if (out) out->uiMode =
1097 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
Dianne Hackborn7299c412010-03-04 18:41:49 -08001098 | ResTable_config::UI_MODE_TYPE_ANY;
1099 return true;
1100 } else if (strcmp(name, "desk") == 0) {
1101 if (out) out->uiMode =
1102 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1103 | ResTable_config::UI_MODE_TYPE_DESK;
Tobias Haamel27b28b32010-02-09 23:09:17 +01001104 return true;
1105 } else if (strcmp(name, "car") == 0) {
1106 if (out) out->uiMode =
1107 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1108 | ResTable_config::UI_MODE_TYPE_CAR;
1109 return true;
Dianne Hackborne360bb62011-05-20 16:11:04 -07001110 } else if (strcmp(name, "television") == 0) {
1111 if (out) out->uiMode =
1112 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1113 | ResTable_config::UI_MODE_TYPE_TELEVISION;
1114 return true;
Joe Onorato44fcb832011-12-14 20:59:30 -08001115 } else if (strcmp(name, "appliance") == 0) {
1116 if (out) out->uiMode =
1117 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1118 | ResTable_config::UI_MODE_TYPE_APPLIANCE;
1119 return true;
John Spurlock6c191292014-04-03 16:37:27 -04001120 } else if (strcmp(name, "watch") == 0) {
1121 if (out) out->uiMode =
1122 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1123 | ResTable_config::UI_MODE_TYPE_WATCH;
1124 return true;
Tobias Haamel27b28b32010-02-09 23:09:17 +01001125 }
1126
1127 return false;
1128}
1129
1130bool AaptGroupEntry::getUiModeNightName(const char* name,
1131 ResTable_config* out)
1132{
1133 if (strcmp(name, kWildcardName) == 0) {
1134 if (out) out->uiMode =
1135 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1136 | ResTable_config::UI_MODE_NIGHT_ANY;
1137 return true;
1138 } else if (strcmp(name, "night") == 0) {
1139 if (out) out->uiMode =
1140 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1141 | ResTable_config::UI_MODE_NIGHT_YES;
1142 return true;
1143 } else if (strcmp(name, "notnight") == 0) {
1144 if (out) out->uiMode =
1145 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1146 | ResTable_config::UI_MODE_NIGHT_NO;
1147 return true;
1148 }
1149
1150 return false;
1151}
1152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153bool AaptGroupEntry::getDensityName(const char* name,
1154 ResTable_config* out)
1155{
1156 if (strcmp(name, kWildcardName) == 0) {
Dianne Hackborna53b8282009-07-17 11:13:48 -07001157 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 return true;
1159 }
Dianne Hackborna53b8282009-07-17 11:13:48 -07001160
1161 if (strcmp(name, "nodpi") == 0) {
1162 if (out) out->density = ResTable_config::DENSITY_NONE;
1163 return true;
1164 }
1165
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001166 if (strcmp(name, "ldpi") == 0) {
1167 if (out) out->density = ResTable_config::DENSITY_LOW;
1168 return true;
1169 }
1170
1171 if (strcmp(name, "mdpi") == 0) {
1172 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1173 return true;
1174 }
1175
Dianne Hackbornb96cbbd2011-05-27 13:40:26 -07001176 if (strcmp(name, "tvdpi") == 0) {
1177 if (out) out->density = ResTable_config::DENSITY_TV;
1178 return true;
1179 }
1180
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001181 if (strcmp(name, "hdpi") == 0) {
1182 if (out) out->density = ResTable_config::DENSITY_HIGH;
1183 return true;
1184 }
Dianne Hackbornd96e3df2012-01-25 15:12:23 -08001185
Dianne Hackborn588feee2010-06-04 14:36:39 -07001186 if (strcmp(name, "xhdpi") == 0) {
Dianne Hackbornd96e3df2012-01-25 15:12:23 -08001187 if (out) out->density = ResTable_config::DENSITY_XHIGH;
Dianne Hackborn588feee2010-06-04 14:36:39 -07001188 return true;
1189 }
Dianne Hackbornd96e3df2012-01-25 15:12:23 -08001190
1191 if (strcmp(name, "xxhdpi") == 0) {
1192 if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1193 return true;
1194 }
1195
Dianne Hackborn56a23012013-02-12 15:41:49 -08001196 if (strcmp(name, "xxxhdpi") == 0) {
1197 if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
1198 return true;
1199 }
1200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 char* c = (char*)name;
1202 while (*c >= '0' && *c <= '9') {
1203 c++;
1204 }
1205
1206 // check that we have 'dpi' after the last digit.
1207 if (toupper(c[0]) != 'D' ||
1208 toupper(c[1]) != 'P' ||
1209 toupper(c[2]) != 'I' ||
1210 c[3] != 0) {
1211 return false;
1212 }
1213
1214 // temporarily replace the first letter with \0 to
1215 // use atoi.
1216 char tmp = c[0];
1217 c[0] = '\0';
1218
1219 int d = atoi(name);
1220 c[0] = tmp;
1221
1222 if (d != 0) {
1223 if (out) out->density = d;
1224 return true;
1225 }
1226
1227 return false;
1228}
1229
1230bool AaptGroupEntry::getTouchscreenName(const char* name,
1231 ResTable_config* out)
1232{
1233 if (strcmp(name, kWildcardName) == 0) {
1234 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1235 return true;
1236 } else if (strcmp(name, "notouch") == 0) {
1237 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1238 return true;
1239 } else if (strcmp(name, "stylus") == 0) {
1240 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1241 return true;
1242 } else if (strcmp(name, "finger") == 0) {
1243 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1244 return true;
1245 }
1246
1247 return false;
1248}
1249
1250bool AaptGroupEntry::getKeysHiddenName(const char* name,
1251 ResTable_config* out)
1252{
1253 uint8_t mask = 0;
1254 uint8_t value = 0;
1255 if (strcmp(name, kWildcardName) == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001256 mask = ResTable_config::MASK_KEYSHIDDEN;
1257 value = ResTable_config::KEYSHIDDEN_ANY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001258 } else if (strcmp(name, "keysexposed") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001259 mask = ResTable_config::MASK_KEYSHIDDEN;
1260 value = ResTable_config::KEYSHIDDEN_NO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261 } else if (strcmp(name, "keyshidden") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001262 mask = ResTable_config::MASK_KEYSHIDDEN;
1263 value = ResTable_config::KEYSHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001264 } else if (strcmp(name, "keyssoft") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001265 mask = ResTable_config::MASK_KEYSHIDDEN;
1266 value = ResTable_config::KEYSHIDDEN_SOFT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267 }
1268
1269 if (mask != 0) {
1270 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1271 return true;
1272 }
1273
1274 return false;
1275}
1276
1277bool AaptGroupEntry::getKeyboardName(const char* name,
1278 ResTable_config* out)
1279{
1280 if (strcmp(name, kWildcardName) == 0) {
1281 if (out) out->keyboard = out->KEYBOARD_ANY;
1282 return true;
1283 } else if (strcmp(name, "nokeys") == 0) {
1284 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1285 return true;
1286 } else if (strcmp(name, "qwerty") == 0) {
1287 if (out) out->keyboard = out->KEYBOARD_QWERTY;
1288 return true;
1289 } else if (strcmp(name, "12key") == 0) {
1290 if (out) out->keyboard = out->KEYBOARD_12KEY;
1291 return true;
1292 }
1293
1294 return false;
1295}
1296
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001297bool AaptGroupEntry::getNavHiddenName(const char* name,
1298 ResTable_config* out)
1299{
1300 uint8_t mask = 0;
1301 uint8_t value = 0;
1302 if (strcmp(name, kWildcardName) == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001303 mask = ResTable_config::MASK_NAVHIDDEN;
1304 value = ResTable_config::NAVHIDDEN_ANY;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001305 } else if (strcmp(name, "navexposed") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001306 mask = ResTable_config::MASK_NAVHIDDEN;
1307 value = ResTable_config::NAVHIDDEN_NO;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001308 } else if (strcmp(name, "navhidden") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001309 mask = ResTable_config::MASK_NAVHIDDEN;
1310 value = ResTable_config::NAVHIDDEN_YES;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001311 }
1312
1313 if (mask != 0) {
1314 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1315 return true;
1316 }
1317
1318 return false;
1319}
1320
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321bool AaptGroupEntry::getNavigationName(const char* name,
1322 ResTable_config* out)
1323{
1324 if (strcmp(name, kWildcardName) == 0) {
1325 if (out) out->navigation = out->NAVIGATION_ANY;
1326 return true;
1327 } else if (strcmp(name, "nonav") == 0) {
1328 if (out) out->navigation = out->NAVIGATION_NONAV;
1329 return true;
1330 } else if (strcmp(name, "dpad") == 0) {
1331 if (out) out->navigation = out->NAVIGATION_DPAD;
1332 return true;
1333 } else if (strcmp(name, "trackball") == 0) {
1334 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1335 return true;
1336 } else if (strcmp(name, "wheel") == 0) {
1337 if (out) out->navigation = out->NAVIGATION_WHEEL;
1338 return true;
1339 }
1340
1341 return false;
1342}
1343
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001344bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345{
1346 if (strcmp(name, kWildcardName) == 0) {
1347 if (out) {
1348 out->screenWidth = out->SCREENWIDTH_ANY;
1349 out->screenHeight = out->SCREENHEIGHT_ANY;
1350 }
1351 return true;
1352 }
1353
1354 const char* x = name;
1355 while (*x >= '0' && *x <= '9') x++;
1356 if (x == name || *x != 'x') return false;
1357 String8 xName(name, x-name);
1358 x++;
1359
1360 const char* y = x;
1361 while (*y >= '0' && *y <= '9') y++;
1362 if (y == name || *y != 0) return false;
1363 String8 yName(x, y-x);
1364
1365 uint16_t w = (uint16_t)atoi(xName.string());
1366 uint16_t h = (uint16_t)atoi(yName.string());
1367 if (w < h) {
1368 return false;
1369 }
1370
1371 if (out) {
1372 out->screenWidth = w;
1373 out->screenHeight = h;
1374 }
1375
1376 return true;
1377}
1378
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001379bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1380{
1381 if (strcmp(name, kWildcardName) == 0) {
1382 if (out) {
1383 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1384 }
1385 return true;
1386 }
1387
1388 if (*name != 's') return false;
1389 name++;
1390 if (*name != 'w') return false;
1391 name++;
1392 const char* x = name;
1393 while (*x >= '0' && *x <= '9') x++;
1394 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1395 String8 xName(name, x-name);
1396
1397 if (out) {
1398 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1399 }
1400
1401 return true;
1402}
1403
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001404bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1405{
1406 if (strcmp(name, kWildcardName) == 0) {
1407 if (out) {
1408 out->screenWidthDp = out->SCREENWIDTH_ANY;
1409 }
1410 return true;
1411 }
1412
1413 if (*name != 'w') return false;
1414 name++;
1415 const char* x = name;
1416 while (*x >= '0' && *x <= '9') x++;
1417 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1418 String8 xName(name, x-name);
1419
1420 if (out) {
1421 out->screenWidthDp = (uint16_t)atoi(xName.string());
1422 }
1423
1424 return true;
1425}
1426
1427bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1428{
1429 if (strcmp(name, kWildcardName) == 0) {
1430 if (out) {
1431 out->screenHeightDp = out->SCREENWIDTH_ANY;
1432 }
1433 return true;
1434 }
1435
1436 if (*name != 'h') return false;
1437 name++;
1438 const char* x = name;
1439 while (*x >= '0' && *x <= '9') x++;
1440 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1441 String8 xName(name, x-name);
1442
1443 if (out) {
1444 out->screenHeightDp = (uint16_t)atoi(xName.string());
1445 }
1446
1447 return true;
1448}
1449
1450bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451{
1452 if (strcmp(name, kWildcardName) == 0) {
1453 if (out) {
1454 out->sdkVersion = out->SDKVERSION_ANY;
1455 out->minorVersion = out->MINORVERSION_ANY;
1456 }
1457 return true;
1458 }
1459
1460 if (*name != 'v') {
1461 return false;
1462 }
1463
1464 name++;
1465 const char* s = name;
1466 while (*s >= '0' && *s <= '9') s++;
1467 if (s == name || *s != 0) return false;
1468 String8 sdkName(name, s-name);
1469
1470 if (out) {
1471 out->sdkVersion = (uint16_t)atoi(sdkName.string());
1472 out->minorVersion = 0;
1473 }
1474
1475 return true;
1476}
1477
1478int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1479{
1480 int v = mcc.compare(o.mcc);
1481 if (v == 0) v = mnc.compare(o.mnc);
1482 if (v == 0) v = locale.compare(o.locale);
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001483 if (v == 0) v = layoutDirection.compare(o.layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484 if (v == 0) v = vendor.compare(o.vendor);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001485 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001486 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1487 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001488 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1489 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 if (v == 0) v = orientation.compare(o.orientation);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001491 if (v == 0) v = uiModeType.compare(o.uiModeType);
1492 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001493 if (v == 0) v = density.compare(o.density);
1494 if (v == 0) v = touchscreen.compare(o.touchscreen);
1495 if (v == 0) v = keysHidden.compare(o.keysHidden);
1496 if (v == 0) v = keyboard.compare(o.keyboard);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001497 if (v == 0) v = navHidden.compare(o.navHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001498 if (v == 0) v = navigation.compare(o.navigation);
1499 if (v == 0) v = screenSize.compare(o.screenSize);
1500 if (v == 0) v = version.compare(o.version);
1501 return v;
1502}
1503
Dianne Hackborne6b68032011-10-13 16:26:02 -07001504const ResTable_config& AaptGroupEntry::toParams() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001505{
Dianne Hackborne6b68032011-10-13 16:26:02 -07001506 if (!mParamsChanged) {
1507 return mParams;
1508 }
1509
1510 mParamsChanged = false;
1511 ResTable_config& params(mParams);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001512 memset(&params, 0, sizeof(params));
1513 getMccName(mcc.string(), &params);
1514 getMncName(mnc.string(), &params);
1515 getLocaleName(locale.string(), &params);
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001516 getLayoutDirectionName(layoutDirection.string(), &params);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001517 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001518 getScreenWidthDpName(screenWidthDp.string(), &params);
1519 getScreenHeightDpName(screenHeightDp.string(), &params);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001520 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1521 getScreenLayoutLongName(screenLayoutLong.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522 getOrientationName(orientation.string(), &params);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001523 getUiModeTypeName(uiModeType.string(), &params);
1524 getUiModeNightName(uiModeNight.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525 getDensityName(density.string(), &params);
1526 getTouchscreenName(touchscreen.string(), &params);
1527 getKeysHiddenName(keysHidden.string(), &params);
1528 getKeyboardName(keyboard.string(), &params);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001529 getNavHiddenName(navHidden.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 getNavigationName(navigation.string(), &params);
1531 getScreenSizeName(screenSize.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 getVersionName(version.string(), &params);
Dianne Hackbornef05e072010-03-01 17:43:39 -08001533
1534 // Fix up version number based on specified parameters.
1535 int minSdk = 0;
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001536 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1537 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001538 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001539 minSdk = SDK_HONEYCOMB_MR2;
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001540 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
Dianne Hackbornef05e072010-03-01 17:43:39 -08001541 != ResTable_config::UI_MODE_TYPE_ANY
1542 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1543 != ResTable_config::UI_MODE_NIGHT_ANY) {
1544 minSdk = SDK_FROYO;
1545 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1546 != ResTable_config::SCREENSIZE_ANY
1547 || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1548 != ResTable_config::SCREENLONG_ANY
1549 || params.density != ResTable_config::DENSITY_DEFAULT) {
1550 minSdk = SDK_DONUT;
1551 }
1552
1553 if (minSdk > params.sdkVersion) {
1554 params.sdkVersion = minSdk;
1555 }
1556
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 return params;
1558}
1559
1560// =========================================================================
1561// =========================================================================
1562// =========================================================================
1563
1564void* AaptFile::editData(size_t size)
1565{
1566 if (size <= mBufferSize) {
1567 mDataSize = size;
1568 return mData;
1569 }
1570 size_t allocSize = (size*3)/2;
1571 void* buf = realloc(mData, allocSize);
1572 if (buf == NULL) {
1573 return NULL;
1574 }
1575 mData = buf;
1576 mDataSize = size;
1577 mBufferSize = allocSize;
1578 return buf;
1579}
1580
1581void* AaptFile::editData(size_t* outSize)
1582{
1583 if (outSize) {
1584 *outSize = mDataSize;
1585 }
1586 return mData;
1587}
1588
1589void* AaptFile::padData(size_t wordSize)
1590{
1591 const size_t extra = mDataSize%wordSize;
1592 if (extra == 0) {
1593 return mData;
1594 }
1595
1596 size_t initial = mDataSize;
1597 void* data = editData(initial+(wordSize-extra));
1598 if (data != NULL) {
1599 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1600 }
1601 return data;
1602}
1603
1604status_t AaptFile::writeData(const void* data, size_t size)
1605{
1606 size_t end = mDataSize;
1607 size_t total = size + end;
1608 void* buf = editData(total);
1609 if (buf == NULL) {
1610 return UNKNOWN_ERROR;
1611 }
1612 memcpy(((char*)buf)+end, data, size);
1613 return NO_ERROR;
1614}
1615
1616void AaptFile::clearData()
1617{
1618 if (mData != NULL) free(mData);
1619 mData = NULL;
1620 mDataSize = 0;
1621 mBufferSize = 0;
1622}
1623
1624String8 AaptFile::getPrintableSource() const
1625{
1626 if (hasData()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001627 String8 name(mGroupEntry.toDirName(String8()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001628 name.appendPath(mPath);
1629 name.append(" #generated");
1630 return name;
1631 }
1632 return mSourceFile;
1633}
1634
1635// =========================================================================
1636// =========================================================================
1637// =========================================================================
1638
Adam Lesinski09384302014-01-22 16:07:42 -08001639status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001640{
Adam Lesinski09384302014-01-22 16:07:42 -08001641 ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
1642 if (index >= 0 && overwriteDuplicate) {
1643 fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
1644 mFiles[index]->getSourceFile().string(),
1645 file->getSourceFile().string());
1646 removeFile(index);
1647 index = -1;
1648 }
1649
1650 if (index < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001651 file->mPath = mPath;
1652 mFiles.add(file->getGroupEntry(), file);
1653 return NO_ERROR;
1654 }
1655
Dianne Hackborne6b68032011-10-13 16:26:02 -07001656#if 0
1657 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1658 file->getSourceFile().string(),
1659 file->getGroupEntry().toDirName(String8()).string(),
1660 mLeaf.string(), mPath.string());
1661#endif
1662
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001663 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1664 getPrintableSource().string());
1665 return UNKNOWN_ERROR;
1666}
1667
1668void AaptGroup::removeFile(size_t index)
1669{
1670 mFiles.removeItemsAt(index);
1671}
1672
Dianne Hackborne6b68032011-10-13 16:26:02 -07001673void AaptGroup::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001674{
Dianne Hackborne6b68032011-10-13 16:26:02 -07001675 printf("%s%s\n", prefix.string(), getPath().string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 const size_t N=mFiles.size();
1677 size_t i;
1678 for (i=0; i<N; i++) {
1679 sp<AaptFile> file = mFiles.valueAt(i);
1680 const AaptGroupEntry& e = file->getGroupEntry();
1681 if (file->hasData()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001682 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001683 (int)file->getSize());
1684 } else {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001685 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1686 file->getPrintableSource().string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 }
Dianne Hackborne6b68032011-10-13 16:26:02 -07001688 //printf("%s File Group Entry: %s\n", prefix.string(),
1689 // file->getGroupEntry().toDirName(String8()).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001690 }
1691}
1692
1693String8 AaptGroup::getPrintableSource() const
1694{
1695 if (mFiles.size() > 0) {
1696 // Arbitrarily pull the first source file out of the list.
1697 return mFiles.valueAt(0)->getPrintableSource();
1698 }
1699
1700 // Should never hit this case, but to be safe...
1701 return getPath();
1702
1703}
1704
1705// =========================================================================
1706// =========================================================================
1707// =========================================================================
1708
1709status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1710{
1711 if (mFiles.indexOfKey(name) >= 0) {
1712 return ALREADY_EXISTS;
1713 }
1714 mFiles.add(name, file);
1715 return NO_ERROR;
1716}
1717
1718status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1719{
1720 if (mDirs.indexOfKey(name) >= 0) {
1721 return ALREADY_EXISTS;
1722 }
1723 mDirs.add(name, dir);
1724 return NO_ERROR;
1725}
1726
1727sp<AaptDir> AaptDir::makeDir(const String8& path)
1728{
1729 String8 name;
1730 String8 remain = path;
1731
1732 sp<AaptDir> subdir = this;
1733 while (name = remain.walkPath(&remain), remain != "") {
1734 subdir = subdir->makeDir(name);
1735 }
1736
1737 ssize_t i = subdir->mDirs.indexOfKey(name);
1738 if (i >= 0) {
1739 return subdir->mDirs.valueAt(i);
1740 }
1741 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1742 subdir->mDirs.add(name, dir);
1743 return dir;
1744}
1745
1746void AaptDir::removeFile(const String8& name)
1747{
1748 mFiles.removeItem(name);
1749}
1750
1751void AaptDir::removeDir(const String8& name)
1752{
1753 mDirs.removeItem(name);
1754}
1755
Adam Lesinski09384302014-01-22 16:07:42 -08001756status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
1757 const bool overwrite)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001758{
1759 sp<AaptGroup> group;
1760 if (mFiles.indexOfKey(leafName) >= 0) {
1761 group = mFiles.valueFor(leafName);
1762 } else {
1763 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1764 mFiles.add(leafName, group);
1765 }
1766
Adam Lesinski09384302014-01-22 16:07:42 -08001767 return group->addFile(file, overwrite);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001768}
1769
1770ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001771 const AaptGroupEntry& kind, const String8& resType,
Adam Lesinski09384302014-01-22 16:07:42 -08001772 sp<FilePathStore>& fullResPaths, const bool overwrite)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001773{
1774 Vector<String8> fileNames;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001775 {
1776 DIR* dir = NULL;
1777
1778 dir = opendir(srcDir.string());
1779 if (dir == NULL) {
1780 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1781 return UNKNOWN_ERROR;
1782 }
1783
1784 /*
1785 * Slurp the filenames out of the directory.
1786 */
1787 while (1) {
1788 struct dirent* entry;
1789
1790 entry = readdir(dir);
1791 if (entry == NULL)
1792 break;
1793
1794 if (isHidden(srcDir.string(), entry->d_name))
1795 continue;
1796
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001797 String8 name(entry->d_name);
1798 fileNames.add(name);
1799 // Add fully qualified path for dependency purposes
1800 // if we're collecting them
1801 if (fullResPaths != NULL) {
1802 fullResPaths->add(srcDir.appendPathCopy(name));
1803 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 closedir(dir);
1806 }
1807
1808 ssize_t count = 0;
1809
1810 /*
1811 * Stash away the files and recursively descend into subdirectories.
1812 */
1813 const size_t N = fileNames.size();
1814 size_t i;
1815 for (i = 0; i < N; i++) {
1816 String8 pathName(srcDir);
1817 FileType type;
1818
1819 pathName.appendPath(fileNames[i].string());
1820 type = getFileType(pathName.string());
1821 if (type == kFileTypeDirectory) {
1822 sp<AaptDir> subdir;
1823 bool notAdded = false;
1824 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1825 subdir = mDirs.valueFor(fileNames[i]);
1826 } else {
1827 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1828 notAdded = true;
1829 }
1830 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Adam Lesinski09384302014-01-22 16:07:42 -08001831 resType, fullResPaths, overwrite);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 if (res < NO_ERROR) {
1833 return res;
1834 }
1835 if (res > 0 && notAdded) {
1836 mDirs.add(fileNames[i], subdir);
1837 }
1838 count += res;
1839 } else if (type == kFileTypeRegular) {
1840 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
Adam Lesinski09384302014-01-22 16:07:42 -08001841 status_t err = addLeafFile(fileNames[i], file, overwrite);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842 if (err != NO_ERROR) {
1843 return err;
1844 }
1845
1846 count++;
1847
1848 } else {
1849 if (bundle->getVerbose())
1850 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
1851 }
1852 }
1853
1854 return count;
1855}
1856
1857status_t AaptDir::validate() const
1858{
1859 const size_t NF = mFiles.size();
1860 const size_t ND = mDirs.size();
1861 size_t i;
1862 for (i = 0; i < NF; i++) {
1863 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1864 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1865 "Invalid filename. Unable to add.");
1866 return UNKNOWN_ERROR;
1867 }
1868
1869 size_t j;
1870 for (j = i+1; j < NF; j++) {
1871 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1872 mFiles.valueAt(j)->getLeaf().string()) == 0) {
1873 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1874 "File is case-insensitive equivalent to: %s",
1875 mFiles.valueAt(j)->getPrintableSource().string());
1876 return UNKNOWN_ERROR;
1877 }
1878
1879 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1880 // (this is mostly caught by the "marked" stuff, below)
1881 }
1882
1883 for (j = 0; j < ND; j++) {
1884 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1885 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1886 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1887 "File conflicts with dir from: %s",
1888 mDirs.valueAt(j)->getPrintableSource().string());
1889 return UNKNOWN_ERROR;
1890 }
1891 }
1892 }
1893
1894 for (i = 0; i < ND; i++) {
1895 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1896 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1897 "Invalid directory name, unable to add.");
1898 return UNKNOWN_ERROR;
1899 }
1900
1901 size_t j;
1902 for (j = i+1; j < ND; j++) {
1903 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1904 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1905 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1906 "Directory is case-insensitive equivalent to: %s",
1907 mDirs.valueAt(j)->getPrintableSource().string());
1908 return UNKNOWN_ERROR;
1909 }
1910 }
1911
1912 status_t err = mDirs.valueAt(i)->validate();
1913 if (err != NO_ERROR) {
1914 return err;
1915 }
1916 }
1917
1918 return NO_ERROR;
1919}
1920
Dianne Hackborne6b68032011-10-13 16:26:02 -07001921void AaptDir::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001922{
1923 const size_t ND=getDirs().size();
1924 size_t i;
1925 for (i=0; i<ND; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001926 getDirs().valueAt(i)->print(prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 }
1928
1929 const size_t NF=getFiles().size();
1930 for (i=0; i<NF; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001931 getFiles().valueAt(i)->print(prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001932 }
1933}
1934
1935String8 AaptDir::getPrintableSource() const
1936{
1937 if (mFiles.size() > 0) {
1938 // Arbitrarily pull the first file out of the list as the source dir.
1939 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1940 }
1941 if (mDirs.size() > 0) {
1942 // Or arbitrarily pull the first dir out of the list as the source dir.
1943 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1944 }
1945
1946 // Should never hit this case, but to be safe...
1947 return mPath;
1948
1949}
1950
1951// =========================================================================
1952// =========================================================================
1953// =========================================================================
1954
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08001955status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
1956{
1957 status_t err = NO_ERROR;
1958 size_t N = javaSymbols->mSymbols.size();
1959 for (size_t i=0; i<N; i++) {
1960 const String8& name = javaSymbols->mSymbols.keyAt(i);
1961 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
1962 ssize_t pos = mSymbols.indexOfKey(name);
1963 if (pos < 0) {
1964 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
1965 err = UNKNOWN_ERROR;
1966 continue;
1967 }
1968 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
1969 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
1970 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
1971 }
1972
1973 N = javaSymbols->mNestedSymbols.size();
1974 for (size_t i=0; i<N; i++) {
1975 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
1976 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
1977 ssize_t pos = mNestedSymbols.indexOfKey(name);
1978 if (pos < 0) {
1979 SourcePos pos;
1980 pos.error("Java symbol dir %s not defined\n", name.string());
1981 err = UNKNOWN_ERROR;
1982 continue;
1983 }
1984 //printf("**** applying java symbols in dir %s\n", name.string());
1985 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1986 if (myerr != NO_ERROR) {
1987 err = myerr;
1988 }
1989 }
1990
1991 return err;
1992}
1993
1994// =========================================================================
1995// =========================================================================
1996// =========================================================================
1997
Dianne Hackborne6b68032011-10-13 16:26:02 -07001998AaptAssets::AaptAssets()
1999 : AaptDir(String8(), String8()),
2000 mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
2001{
2002}
2003
2004const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
2005 if (mChanged) {
2006 }
2007 return mGroupEntries;
2008}
2009
2010status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
2011{
2012 mChanged = true;
2013 return AaptDir::addFile(name, file);
2014}
2015
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002016sp<AaptFile> AaptAssets::addFile(
2017 const String8& filePath, const AaptGroupEntry& entry,
2018 const String8& srcDir, sp<AaptGroup>* outGroup,
2019 const String8& resType)
2020{
2021 sp<AaptDir> dir = this;
2022 sp<AaptGroup> group;
2023 sp<AaptFile> file;
2024 String8 root, remain(filePath), partialPath;
2025 while (remain.length() > 0) {
2026 root = remain.walkPath(&remain);
2027 partialPath.appendPath(root);
2028
2029 const String8 rootStr(root);
2030
2031 if (remain.length() == 0) {
2032 ssize_t i = dir->getFiles().indexOfKey(rootStr);
2033 if (i >= 0) {
2034 group = dir->getFiles().valueAt(i);
2035 } else {
2036 group = new AaptGroup(rootStr, filePath);
2037 status_t res = dir->addFile(rootStr, group);
2038 if (res != NO_ERROR) {
2039 return NULL;
2040 }
2041 }
2042 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2043 status_t res = group->addFile(file);
2044 if (res != NO_ERROR) {
2045 return NULL;
2046 }
2047 break;
2048
2049 } else {
2050 ssize_t i = dir->getDirs().indexOfKey(rootStr);
2051 if (i >= 0) {
2052 dir = dir->getDirs().valueAt(i);
2053 } else {
2054 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2055 status_t res = dir->addDir(rootStr, subdir);
2056 if (res != NO_ERROR) {
2057 return NULL;
2058 }
2059 dir = subdir;
2060 }
2061 }
2062 }
2063
2064 mGroupEntries.add(entry);
2065 if (outGroup) *outGroup = group;
2066 return file;
2067}
2068
2069void AaptAssets::addResource(const String8& leafName, const String8& path,
2070 const sp<AaptFile>& file, const String8& resType)
2071{
2072 sp<AaptDir> res = AaptDir::makeDir(kResString);
2073 String8 dirname = file->getGroupEntry().toDirName(resType);
2074 sp<AaptDir> subdir = res->makeDir(dirname);
2075 sp<AaptGroup> grr = new AaptGroup(leafName, path);
2076 grr->addFile(file);
2077
2078 subdir->addFile(leafName, grr);
2079}
2080
2081
2082ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2083{
2084 int count;
2085 int totalCount = 0;
2086 FileType type;
2087 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2088 const size_t dirCount =resDirs.size();
2089 sp<AaptAssets> current = this;
2090
2091 const int N = bundle->getFileSpecCount();
2092
2093 /*
2094 * If a package manifest was specified, include that first.
2095 */
2096 if (bundle->getAndroidManifestFile() != NULL) {
2097 // place at root of zip.
2098 String8 srcFile(bundle->getAndroidManifestFile());
2099 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2100 NULL, String8());
2101 totalCount++;
2102 }
2103
2104 /*
2105 * If a directory of custom assets was supplied, slurp 'em up.
2106 */
Adam Lesinski09384302014-01-22 16:07:42 -08002107 const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
2108 const int AN = assetDirs.size();
2109 for (int i = 0; i < AN; i++) {
2110 FileType type = getFileType(assetDirs[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002111 if (type == kFileTypeNonexistent) {
Adam Lesinski09384302014-01-22 16:07:42 -08002112 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002113 return UNKNOWN_ERROR;
2114 }
2115 if (type != kFileTypeDirectory) {
Adam Lesinski09384302014-01-22 16:07:42 -08002116 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002117 return UNKNOWN_ERROR;
2118 }
2119
Adam Lesinski09384302014-01-22 16:07:42 -08002120 String8 assetRoot(assetDirs[i]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002121 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2122 AaptGroupEntry group;
2123 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Adam Lesinski09384302014-01-22 16:07:42 -08002124 String8(), mFullAssetPaths, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002125 if (count < 0) {
2126 totalCount = count;
2127 goto bail;
2128 }
2129 if (count > 0) {
2130 mGroupEntries.add(group);
2131 }
2132 totalCount += count;
2133
Adam Lesinski09384302014-01-22 16:07:42 -08002134 if (bundle->getVerbose()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002135 printf("Found %d custom asset file%s in %s\n",
Adam Lesinski09384302014-01-22 16:07:42 -08002136 count, (count==1) ? "" : "s", assetDirs[i]);
2137 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002138 }
2139
2140 /*
2141 * If a directory of resource-specific assets was supplied, slurp 'em up.
2142 */
2143 for (size_t i=0; i<dirCount; i++) {
2144 const char *res = resDirs[i];
2145 if (res) {
2146 type = getFileType(res);
2147 if (type == kFileTypeNonexistent) {
2148 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2149 return UNKNOWN_ERROR;
2150 }
2151 if (type == kFileTypeDirectory) {
2152 if (i>0) {
2153 sp<AaptAssets> nextOverlay = new AaptAssets();
2154 current->setOverlay(nextOverlay);
2155 current = nextOverlay;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002156 current->setFullResPaths(mFullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002157 }
2158 count = current->slurpResourceTree(bundle, String8(res));
2159
2160 if (count < 0) {
2161 totalCount = count;
2162 goto bail;
2163 }
2164 totalCount += count;
2165 }
2166 else {
2167 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2168 return UNKNOWN_ERROR;
2169 }
2170 }
2171
2172 }
2173 /*
2174 * Now do any additional raw files.
2175 */
2176 for (int arg=0; arg<N; arg++) {
2177 const char* assetDir = bundle->getFileSpecEntry(arg);
2178
2179 FileType type = getFileType(assetDir);
2180 if (type == kFileTypeNonexistent) {
2181 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2182 return UNKNOWN_ERROR;
2183 }
2184 if (type != kFileTypeDirectory) {
2185 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2186 return UNKNOWN_ERROR;
2187 }
2188
2189 String8 assetRoot(assetDir);
2190
2191 if (bundle->getVerbose())
2192 printf("Processing raw dir '%s'\n", (const char*) assetDir);
2193
2194 /*
2195 * Do a recursive traversal of subdir tree. We don't make any
2196 * guarantees about ordering, so we're okay with an inorder search
2197 * using whatever order the OS happens to hand back to us.
2198 */
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002199 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002200 if (count < 0) {
2201 /* failure; report error and remove archive */
2202 totalCount = count;
2203 goto bail;
2204 }
2205 totalCount += count;
2206
2207 if (bundle->getVerbose())
2208 printf("Found %d asset file%s in %s\n",
2209 count, (count==1) ? "" : "s", assetDir);
2210 }
2211
2212 count = validate();
2213 if (count != NO_ERROR) {
2214 totalCount = count;
2215 goto bail;
2216 }
2217
Dianne Hackborne6b68032011-10-13 16:26:02 -07002218 count = filter(bundle);
2219 if (count != NO_ERROR) {
2220 totalCount = count;
2221 goto bail;
2222 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002223
2224bail:
2225 return totalCount;
2226}
2227
2228ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2229 const AaptGroupEntry& kind,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002230 const String8& resType,
2231 sp<FilePathStore>& fullResPaths)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002232{
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002233 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002234 if (res > 0) {
2235 mGroupEntries.add(kind);
2236 }
2237
2238 return res;
2239}
2240
2241ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2242{
2243 ssize_t err = 0;
2244
2245 DIR* dir = opendir(srcDir.string());
2246 if (dir == NULL) {
2247 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2248 return UNKNOWN_ERROR;
2249 }
2250
2251 status_t count = 0;
2252
2253 /*
2254 * Run through the directory, looking for dirs that match the
2255 * expected pattern.
2256 */
2257 while (1) {
2258 struct dirent* entry = readdir(dir);
2259 if (entry == NULL) {
2260 break;
2261 }
2262
2263 if (isHidden(srcDir.string(), entry->d_name)) {
2264 continue;
2265 }
2266
2267 String8 subdirName(srcDir);
2268 subdirName.appendPath(entry->d_name);
2269
2270 AaptGroupEntry group;
2271 String8 resType;
2272 bool b = group.initFromDirName(entry->d_name, &resType);
2273 if (!b) {
2274 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2275 entry->d_name);
2276 err = -1;
2277 continue;
2278 }
2279
Dianne Hackborne6b68032011-10-13 16:26:02 -07002280 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07002281 int maxResInt = atoi(bundle->getMaxResVersion());
Dianne Hackborne6b68032011-10-13 16:26:02 -07002282 const char *verString = group.getVersionString().string();
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07002283 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2284 if (dirVersionInt > maxResInt) {
2285 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2286 continue;
2287 }
2288 }
2289
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002290 FileType type = getFileType(subdirName.string());
2291
2292 if (type == kFileTypeDirectory) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002293 sp<AaptDir> dir = makeDir(resType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002294 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002295 resType, mFullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002296 if (res < 0) {
2297 count = res;
2298 goto bail;
2299 }
2300 if (res > 0) {
2301 mGroupEntries.add(group);
2302 count += res;
2303 }
2304
Dianne Hackborne6b68032011-10-13 16:26:02 -07002305 // Only add this directory if we don't already have a resource dir
2306 // for the current type. This ensures that we only add the dir once
2307 // for all configs.
2308 sp<AaptDir> rdir = resDir(resType);
2309 if (rdir == NULL) {
2310 mResDirs.add(dir);
2311 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002312 } else {
2313 if (bundle->getVerbose()) {
2314 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
2315 }
2316 }
2317 }
2318
2319bail:
2320 closedir(dir);
2321 dir = NULL;
2322
2323 if (err != 0) {
2324 return err;
2325 }
2326 return count;
2327}
2328
2329ssize_t
2330AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2331{
2332 int count = 0;
2333 SortedVector<AaptGroupEntry> entries;
2334
2335 ZipFile* zip = new ZipFile;
2336 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2337 if (err != NO_ERROR) {
2338 fprintf(stderr, "error opening zip file %s\n", filename);
2339 count = err;
2340 delete zip;
2341 return -1;
2342 }
2343
2344 const int N = zip->getNumEntries();
2345 for (int i=0; i<N; i++) {
2346 ZipEntry* entry = zip->getEntryByIndex(i);
2347 if (entry->getDeleted()) {
2348 continue;
2349 }
2350
2351 String8 entryName(entry->getFileName());
2352
2353 String8 dirName = entryName.getPathDir();
2354 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2355
2356 String8 resType;
2357 AaptGroupEntry kind;
2358
2359 String8 remain;
2360 if (entryName.walkPath(&remain) == kResourceDir) {
2361 // these are the resources, pull their type out of the directory name
2362 kind.initFromDirName(remain.walkPath().string(), &resType);
2363 } else {
2364 // these are untyped and don't have an AaptGroupEntry
2365 }
2366 if (entries.indexOf(kind) < 0) {
2367 entries.add(kind);
2368 mGroupEntries.add(kind);
2369 }
2370
2371 // use the one from the zip file if they both exist.
2372 dir->removeFile(entryName.getPathLeaf());
2373
2374 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2375 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2376 if (err != NO_ERROR) {
2377 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2378 count = err;
2379 goto bail;
2380 }
2381 file->setCompressionMethod(entry->getCompressionMethod());
2382
2383#if 0
2384 if (entryName == "AndroidManifest.xml") {
2385 printf("AndroidManifest.xml\n");
2386 }
2387 printf("\n\nfile: %s\n", entryName.string());
2388#endif
2389
2390 size_t len = entry->getUncompressedLen();
2391 void* data = zip->uncompress(entry);
2392 void* buf = file->editData(len);
2393 memcpy(buf, data, len);
2394
2395#if 0
2396 const int OFF = 0;
2397 const unsigned char* p = (unsigned char*)data;
2398 const unsigned char* end = p+len;
2399 p += OFF;
2400 for (int i=0; i<32 && p < end; i++) {
2401 printf("0x%03x ", i*0x10 + OFF);
2402 for (int j=0; j<0x10 && p < end; j++) {
2403 printf(" %02x", *p);
2404 p++;
2405 }
2406 printf("\n");
2407 }
2408#endif
2409
2410 free(data);
2411
2412 count++;
2413 }
2414
2415bail:
2416 delete zip;
2417 return count;
2418}
2419
Dianne Hackborne6b68032011-10-13 16:26:02 -07002420status_t AaptAssets::filter(Bundle* bundle)
2421{
2422 ResourceFilter reqFilter;
2423 status_t err = reqFilter.parse(bundle->getConfigurations());
2424 if (err != NO_ERROR) {
2425 return err;
2426 }
2427
2428 ResourceFilter prefFilter;
2429 err = prefFilter.parse(bundle->getPreferredConfigurations());
2430 if (err != NO_ERROR) {
2431 return err;
2432 }
2433
2434 if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2435 return NO_ERROR;
2436 }
2437
Dianne Hackbornbd9d2bc2011-10-16 14:17:07 -07002438 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002439 if (!reqFilter.isEmpty()) {
2440 printf("Applying required filter: %s\n",
2441 bundle->getConfigurations());
2442 }
2443 if (!prefFilter.isEmpty()) {
2444 printf("Applying preferred filter: %s\n",
2445 bundle->getPreferredConfigurations());
2446 }
2447 }
2448
2449 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2450 const size_t ND = resdirs.size();
2451 for (size_t i=0; i<ND; i++) {
2452 const sp<AaptDir>& dir = resdirs.itemAt(i);
2453 if (dir->getLeaf() == kValuesDir) {
2454 // The "value" dir is special since a single file defines
2455 // multiple resources, so we can not do filtering on the
2456 // files themselves.
2457 continue;
2458 }
2459 if (dir->getLeaf() == kMipmapDir) {
2460 // We also skip the "mipmap" directory, since the point of this
2461 // is to include all densities without stripping. If you put
2462 // other configurations in here as well they won't be stripped
2463 // either... So don't do that. Seriously. What is wrong with you?
2464 continue;
2465 }
2466
2467 const size_t NG = dir->getFiles().size();
2468 for (size_t j=0; j<NG; j++) {
2469 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2470
2471 // First remove any configurations we know we don't need.
2472 for (size_t k=0; k<grp->getFiles().size(); k++) {
2473 sp<AaptFile> file = grp->getFiles().valueAt(k);
2474 if (k == 0 && grp->getFiles().size() == 1) {
2475 // If this is the only file left, we need to keep it.
2476 // Otherwise the resource IDs we are using will be inconsistent
2477 // with what we get when not stripping. Sucky, but at least
2478 // for now we can rely on the back-end doing another filtering
2479 // pass to take this out and leave us with this resource name
2480 // containing no entries.
2481 continue;
2482 }
2483 if (file->getPath().getPathExtension() == ".xml") {
2484 // We can't remove .xml files at this point, because when
2485 // we parse them they may add identifier resources, so
2486 // removing them can cause our resource identifiers to
2487 // become inconsistent.
2488 continue;
2489 }
2490 const ResTable_config& config(file->getGroupEntry().toParams());
2491 if (!reqFilter.match(config)) {
2492 if (bundle->getVerbose()) {
2493 printf("Pruning unneeded resource: %s\n",
2494 file->getPrintableSource().string());
2495 }
2496 grp->removeFile(k);
2497 k--;
2498 }
2499 }
2500
2501 // Quick check: no preferred filters, nothing more to do.
2502 if (prefFilter.isEmpty()) {
2503 continue;
2504 }
2505
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002506 // Get the preferred density if there is one. We do not match exactly for density.
2507 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2508 // pick xhdpi.
2509 uint32_t preferredDensity = 0;
2510 const SortedVector<uint32_t>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
2511 if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
2512 preferredDensity = (*preferredConfigs)[0];
2513 }
2514
Dianne Hackborne6b68032011-10-13 16:26:02 -07002515 // Now deal with preferred configurations.
2516 for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2517 for (size_t k=0; k<grp->getFiles().size(); k++) {
2518 sp<AaptFile> file = grp->getFiles().valueAt(k);
2519 if (k == 0 && grp->getFiles().size() == 1) {
2520 // If this is the only file left, we need to keep it.
2521 // Otherwise the resource IDs we are using will be inconsistent
2522 // with what we get when not stripping. Sucky, but at least
2523 // for now we can rely on the back-end doing another filtering
2524 // pass to take this out and leave us with this resource name
2525 // containing no entries.
2526 continue;
2527 }
2528 if (file->getPath().getPathExtension() == ".xml") {
2529 // We can't remove .xml files at this point, because when
2530 // we parse them they may add identifier resources, so
2531 // removing them can cause our resource identifiers to
2532 // become inconsistent.
2533 continue;
2534 }
2535 const ResTable_config& config(file->getGroupEntry().toParams());
2536 if (!prefFilter.match(axis, config)) {
2537 // This is a resource we would prefer not to have. Check
2538 // to see if have a similar variation that we would like
2539 // to have and, if so, we can drop it.
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002540
2541 uint32_t bestDensity = config.density;
2542
Dianne Hackborne6b68032011-10-13 16:26:02 -07002543 for (size_t m=0; m<grp->getFiles().size(); m++) {
2544 if (m == k) continue;
2545 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2546 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2547 if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002548 if (axis == AXIS_DENSITY && preferredDensity > 0) {
2549 // See if there is a better density resource
2550 if (mconfig.density < bestDensity &&
2551 mconfig.density > preferredDensity &&
2552 bestDensity > preferredDensity) {
2553 // This density is between our best density and
2554 // the preferred density, therefore it is better.
2555 bestDensity = mconfig.density;
2556 } else if (mconfig.density > bestDensity &&
2557 bestDensity < preferredDensity) {
2558 // This density is better than our best density and
2559 // our best density was smaller than our preferred
2560 // density, so it is better.
2561 bestDensity = mconfig.density;
2562 }
2563 } else if (prefFilter.match(axis, mconfig)) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002564 if (bundle->getVerbose()) {
2565 printf("Pruning unneeded resource: %s\n",
2566 file->getPrintableSource().string());
2567 }
2568 grp->removeFile(k);
2569 k--;
2570 break;
2571 }
2572 }
2573 }
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002574
2575 if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2576 bestDensity != config.density) {
2577 if (bundle->getVerbose()) {
2578 printf("Pruning unneeded resource: %s\n",
2579 file->getPrintableSource().string());
2580 }
2581 grp->removeFile(k);
2582 k--;
2583 }
Dianne Hackborne6b68032011-10-13 16:26:02 -07002584 }
2585 }
2586 }
2587 }
2588 }
2589
2590 return NO_ERROR;
2591}
2592
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002593sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2594{
2595 sp<AaptSymbols> sym = mSymbols.valueFor(name);
2596 if (sym == NULL) {
2597 sym = new AaptSymbols();
2598 mSymbols.add(name, sym);
2599 }
2600 return sym;
2601}
2602
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08002603sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2604{
2605 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2606 if (sym == NULL) {
2607 sym = new AaptSymbols();
2608 mJavaSymbols.add(name, sym);
2609 }
2610 return sym;
2611}
2612
2613status_t AaptAssets::applyJavaSymbols()
2614{
2615 size_t N = mJavaSymbols.size();
2616 for (size_t i=0; i<N; i++) {
2617 const String8& name = mJavaSymbols.keyAt(i);
2618 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2619 ssize_t pos = mSymbols.indexOfKey(name);
2620 if (pos < 0) {
2621 SourcePos pos;
2622 pos.error("Java symbol dir %s not defined\n", name.string());
2623 return UNKNOWN_ERROR;
2624 }
2625 //printf("**** applying java symbols in dir %s\n", name.string());
2626 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2627 if (err != NO_ERROR) {
2628 return err;
2629 }
2630 }
2631
2632 return NO_ERROR;
2633}
2634
2635bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2636 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2637 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2638 // sym.isJavaSymbol ? 1 : 0);
2639 if (!mHavePrivateSymbols) return true;
2640 if (sym.isPublic) return true;
2641 if (includePrivate && sym.isJavaSymbol) return true;
2642 return false;
2643}
2644
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002645status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2646{
2647 if (!mHaveIncludedAssets) {
2648 // Add in all includes.
2649 const Vector<const char*>& incl = bundle->getPackageIncludes();
2650 const size_t N=incl.size();
2651 for (size_t i=0; i<N; i++) {
2652 if (bundle->getVerbose())
2653 printf("Including resources from package: %s\n", incl[i]);
2654 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2655 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2656 incl[i]);
2657 return UNKNOWN_ERROR;
2658 }
2659 }
2660 mHaveIncludedAssets = true;
2661 }
2662
2663 return NO_ERROR;
2664}
2665
2666status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2667{
2668 const ResTable& res = getIncludedResources();
2669 // XXX dirty!
2670 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2671}
2672
2673const ResTable& AaptAssets::getIncludedResources() const
2674{
2675 return mIncludedAssets.getResources(false);
2676}
2677
Dianne Hackborne6b68032011-10-13 16:26:02 -07002678void AaptAssets::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002679{
Dianne Hackborne6b68032011-10-13 16:26:02 -07002680 String8 innerPrefix(prefix);
2681 innerPrefix.append(" ");
2682 String8 innerInnerPrefix(innerPrefix);
2683 innerInnerPrefix.append(" ");
2684 printf("%sConfigurations:\n", prefix.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002685 const size_t N=mGroupEntries.size();
2686 for (size_t i=0; i<N; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002687 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2688 printf("%s %s\n", prefix.string(),
2689 cname != "" ? cname.string() : "(default)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002690 }
2691
Dianne Hackborne6b68032011-10-13 16:26:02 -07002692 printf("\n%sFiles:\n", prefix.string());
2693 AaptDir::print(innerPrefix);
2694
2695 printf("\n%sResource Dirs:\n", prefix.string());
2696 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2697 const size_t NR = resdirs.size();
2698 for (size_t i=0; i<NR; i++) {
2699 const sp<AaptDir>& d = resdirs.itemAt(i);
2700 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
2701 d->print(innerInnerPrefix);
2702 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002703}
2704
Dianne Hackborne6b68032011-10-13 16:26:02 -07002705sp<AaptDir> AaptAssets::resDir(const String8& name) const
Joe Onorato1553c822009-08-30 13:36:22 -07002706{
Dianne Hackborne6b68032011-10-13 16:26:02 -07002707 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2708 const size_t N = resdirs.size();
Joe Onorato1553c822009-08-30 13:36:22 -07002709 for (size_t i=0; i<N; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002710 const sp<AaptDir>& d = resdirs.itemAt(i);
Joe Onorato1553c822009-08-30 13:36:22 -07002711 if (d->getLeaf() == name) {
2712 return d;
2713 }
2714 }
2715 return NULL;
2716}
2717
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002718bool
2719valid_symbol_name(const String8& symbol)
2720{
2721 static char const * const KEYWORDS[] = {
2722 "abstract", "assert", "boolean", "break",
2723 "byte", "case", "catch", "char", "class", "const", "continue",
2724 "default", "do", "double", "else", "enum", "extends", "final",
2725 "finally", "float", "for", "goto", "if", "implements", "import",
2726 "instanceof", "int", "interface", "long", "native", "new", "package",
2727 "private", "protected", "public", "return", "short", "static",
2728 "strictfp", "super", "switch", "synchronized", "this", "throw",
2729 "throws", "transient", "try", "void", "volatile", "while",
2730 "true", "false", "null",
2731 NULL
2732 };
2733 const char*const* k = KEYWORDS;
2734 const char*const s = symbol.string();
2735 while (*k) {
2736 if (0 == strcmp(s, *k)) {
2737 return false;
2738 }
2739 k++;
2740 }
2741 return true;
2742}