blob: f0c215eeffa5ce686d2059744a1ec047ba944793 [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
59static bool isHidden(const char *root, const char *path)
60{
Raphael3cdfc042009-09-24 15:30:53 -070061 const char *ext = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 const char *type = NULL;
63
64 // Skip all hidden files.
65 if (path[0] == '.') {
66 // Skip ., .. and .svn but don't chatter about it.
67 if (strcmp(path, ".") == 0
68 || strcmp(path, "..") == 0
69 || strcmp(path, ".svn") == 0) {
70 return true;
71 }
72 type = "hidden";
73 } else if (path[0] == '_') {
74 // skip directories starting with _ (don't chatter about it)
75 String8 subdirName(root);
76 subdirName.appendPath(path);
77 if (getFileType(subdirName.string()) == kFileTypeDirectory) {
78 return true;
79 }
80 } else if (strcmp(path, "CVS") == 0) {
81 // Skip CVS but don't chatter about it.
82 return true;
83 } else if (strcasecmp(path, "thumbs.db") == 0
84 || strcasecmp(path, "picasa.ini") == 0) {
85 // Skip suspected image indexes files.
86 type = "index";
87 } else if (path[strlen(path)-1] == '~') {
88 // Skip suspected emacs backup files.
89 type = "backup";
Raphael3cdfc042009-09-24 15:30:53 -070090 } else if ((ext = strrchr(path, '.')) != NULL && strcmp(ext, ".scc") == 0) {
91 // Skip VisualSourceSafe files and don't chatter about it
92 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 } else {
94 // Let everything else through.
95 return false;
96 }
97
98 /* If we get this far, "type" should be set and the file
99 * should be skipped.
100 */
101 String8 subdirName(root);
102 subdirName.appendPath(path);
103 fprintf(stderr, " (skipping %s %s '%s')\n", type,
104 getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file",
105 subdirName.string());
106
107 return true;
108}
109
110// =========================================================================
111// =========================================================================
112// =========================================================================
113
114status_t
115AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
116{
117 ResTable_config config;
118
119 // IMSI - MCC
120 if (getMccName(part.string(), &config)) {
121 *axis = AXIS_MCC;
122 *value = config.mcc;
123 return 0;
124 }
125
126 // IMSI - MNC
127 if (getMncName(part.string(), &config)) {
128 *axis = AXIS_MNC;
129 *value = config.mnc;
130 return 0;
131 }
132
133 // locale - language
134 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
135 *axis = AXIS_LANGUAGE;
136 *value = part[1] << 8 | part[0];
137 return 0;
138 }
139
140 // locale - language_REGION
141 if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
142 && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
143 *axis = AXIS_LANGUAGE;
144 *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
145 return 0;
146 }
147
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700148 // smallest screen dp width
149 if (getSmallestScreenWidthDpName(part.string(), &config)) {
150 *axis = AXIS_SMALLESTSCREENWIDTHDP;
151 *value = config.smallestScreenWidthDp;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700152 return 0;
153 }
154
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700155 // screen dp width
156 if (getScreenWidthDpName(part.string(), &config)) {
157 *axis = AXIS_SCREENWIDTHDP;
158 *value = config.screenWidthDp;
159 return 0;
160 }
161
162 // screen dp height
163 if (getScreenHeightDpName(part.string(), &config)) {
164 *axis = AXIS_SCREENHEIGHTDP;
165 *value = config.screenHeightDp;
166 return 0;
167 }
168
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700169 // screen layout size
170 if (getScreenLayoutSizeName(part.string(), &config)) {
171 *axis = AXIS_SCREENLAYOUTSIZE;
172 *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
173 return 0;
174 }
175
176 // screen layout long
177 if (getScreenLayoutLongName(part.string(), &config)) {
178 *axis = AXIS_SCREENLAYOUTLONG;
179 *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
180 return 0;
181 }
182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 // orientation
184 if (getOrientationName(part.string(), &config)) {
185 *axis = AXIS_ORIENTATION;
186 *value = config.orientation;
187 return 0;
188 }
189
Tobias Haamel27b28b32010-02-09 23:09:17 +0100190 // ui mode type
191 if (getUiModeTypeName(part.string(), &config)) {
192 *axis = AXIS_UIMODETYPE;
193 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
194 return 0;
195 }
196
197 // ui mode night
198 if (getUiModeNightName(part.string(), &config)) {
199 *axis = AXIS_UIMODENIGHT;
200 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
201 return 0;
202 }
203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 // density
205 if (getDensityName(part.string(), &config)) {
206 *axis = AXIS_DENSITY;
207 *value = config.density;
208 return 0;
209 }
210
211 // touchscreen
212 if (getTouchscreenName(part.string(), &config)) {
213 *axis = AXIS_TOUCHSCREEN;
214 *value = config.touchscreen;
215 return 0;
216 }
217
218 // keyboard hidden
219 if (getKeysHiddenName(part.string(), &config)) {
220 *axis = AXIS_KEYSHIDDEN;
221 *value = config.inputFlags;
222 return 0;
223 }
224
225 // keyboard
226 if (getKeyboardName(part.string(), &config)) {
227 *axis = AXIS_KEYBOARD;
228 *value = config.keyboard;
229 return 0;
230 }
231
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700232 // navigation hidden
233 if (getNavHiddenName(part.string(), &config)) {
234 *axis = AXIS_NAVHIDDEN;
235 *value = config.inputFlags;
236 return 0;
237 }
238
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 // navigation
240 if (getNavigationName(part.string(), &config)) {
241 *axis = AXIS_NAVIGATION;
242 *value = config.navigation;
243 return 0;
244 }
245
246 // screen size
247 if (getScreenSizeName(part.string(), &config)) {
248 *axis = AXIS_SCREENSIZE;
249 *value = config.screenSize;
250 return 0;
251 }
252
253 // version
254 if (getVersionName(part.string(), &config)) {
255 *axis = AXIS_VERSION;
256 *value = config.version;
257 return 0;
258 }
259
260 return 1;
261}
262
Dianne Hackborne6b68032011-10-13 16:26:02 -0700263uint32_t
264AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
265{
266 switch (axis) {
267 case AXIS_MCC:
268 return config.mcc;
269 case AXIS_MNC:
270 return config.mnc;
271 case AXIS_LANGUAGE:
272 return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
273 | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
274 case AXIS_SCREENLAYOUTSIZE:
275 return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
276 case AXIS_ORIENTATION:
277 return config.orientation;
278 case AXIS_UIMODETYPE:
279 return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
280 case AXIS_UIMODENIGHT:
281 return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
282 case AXIS_DENSITY:
283 return config.density;
284 case AXIS_TOUCHSCREEN:
285 return config.touchscreen;
286 case AXIS_KEYSHIDDEN:
287 return config.inputFlags;
288 case AXIS_KEYBOARD:
289 return config.keyboard;
290 case AXIS_NAVIGATION:
291 return config.navigation;
292 case AXIS_SCREENSIZE:
293 return config.screenSize;
294 case AXIS_SMALLESTSCREENWIDTHDP:
295 return config.smallestScreenWidthDp;
296 case AXIS_SCREENWIDTHDP:
297 return config.screenWidthDp;
298 case AXIS_SCREENHEIGHTDP:
299 return config.screenHeightDp;
300 case AXIS_VERSION:
301 return config.version;
302 }
303 return 0;
304}
305
306bool
307AaptGroupEntry::configSameExcept(const ResTable_config& config,
308 const ResTable_config& otherConfig, int axis)
309{
310 for (int i=AXIS_START; i<=AXIS_END; i++) {
311 if (i == axis) {
312 continue;
313 }
314 if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
315 return false;
316 }
317 }
318 return true;
319}
320
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321bool
322AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
323{
Dianne Hackborne6b68032011-10-13 16:26:02 -0700324 mParamsChanged = true;
325
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 Vector<String8> parts;
327
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700328 String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700329 String8 touch, key, keysHidden, nav, navHidden, size, vers;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700330 String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331
332 const char *p = dir;
333 const char *q;
334 while (NULL != (q = strchr(p, '-'))) {
335 String8 val(p, q-p);
336 val.toLower();
337 parts.add(val);
338 //printf("part: %s\n", parts[parts.size()-1].string());
339 p = q+1;
340 }
341 String8 val(p);
342 val.toLower();
343 parts.add(val);
344 //printf("part: %s\n", parts[parts.size()-1].string());
345
346 const int N = parts.size();
347 int index = 0;
348 String8 part = parts[index];
349
350 // resource type
351 if (!isValidResourceType(part)) {
352 return false;
353 }
354 *resType = part;
355
356 index++;
357 if (index == N) {
358 goto success;
359 }
360 part = parts[index];
361
362 // imsi - mcc
363 if (getMccName(part.string())) {
364 mcc = part;
365
366 index++;
367 if (index == N) {
368 goto success;
369 }
370 part = parts[index];
371 } else {
372 //printf("not mcc: %s\n", part.string());
373 }
374
375 // imsi - mnc
376 if (getMncName(part.string())) {
377 mnc = part;
378
379 index++;
380 if (index == N) {
381 goto success;
382 }
383 part = parts[index];
384 } else {
385 //printf("not mcc: %s\n", part.string());
386 }
387
388 // locale - language
389 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
390 loc = part;
391
392 index++;
393 if (index == N) {
394 goto success;
395 }
396 part = parts[index];
397 } else {
398 //printf("not language: %s\n", part.string());
399 }
400
401 // locale - region
402 if (loc.length() > 0
403 && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
404 loc += "-";
405 part.toUpper();
406 loc += part.string() + 1;
407
408 index++;
409 if (index == N) {
410 goto success;
411 }
412 part = parts[index];
413 } else {
414 //printf("not region: %s\n", part.string());
415 }
416
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700417 if (getSmallestScreenWidthDpName(part.string())) {
418 smallestwidthdp = part;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700419
420 index++;
421 if (index == N) {
422 goto success;
423 }
424 part = parts[index];
425 } else {
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700426 //printf("not smallest screen width dp: %s\n", part.string());
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700427 }
428
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700429 if (getScreenWidthDpName(part.string())) {
430 widthdp = part;
431
432 index++;
433 if (index == N) {
434 goto success;
435 }
436 part = parts[index];
437 } else {
438 //printf("not screen width dp: %s\n", part.string());
439 }
440
441 if (getScreenHeightDpName(part.string())) {
442 heightdp = part;
443
444 index++;
445 if (index == N) {
446 goto success;
447 }
448 part = parts[index];
449 } else {
450 //printf("not screen height dp: %s\n", part.string());
451 }
452
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700453 if (getScreenLayoutSizeName(part.string())) {
454 layoutsize = part;
455
456 index++;
457 if (index == N) {
458 goto success;
459 }
460 part = parts[index];
461 } else {
462 //printf("not screen layout size: %s\n", part.string());
463 }
464
465 if (getScreenLayoutLongName(part.string())) {
466 layoutlong = part;
467
468 index++;
469 if (index == N) {
470 goto success;
471 }
472 part = parts[index];
473 } else {
474 //printf("not screen layout long: %s\n", part.string());
475 }
476
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 // orientation
478 if (getOrientationName(part.string())) {
479 orient = part;
480
481 index++;
482 if (index == N) {
483 goto success;
484 }
485 part = parts[index];
486 } else {
487 //printf("not orientation: %s\n", part.string());
488 }
489
Tobias Haamel27b28b32010-02-09 23:09:17 +0100490 // ui mode type
491 if (getUiModeTypeName(part.string())) {
492 uiModeType = part;
493
494 index++;
495 if (index == N) {
496 goto success;
497 }
498 part = parts[index];
499 } else {
500 //printf("not ui mode type: %s\n", part.string());
501 }
502
503 // ui mode night
504 if (getUiModeNightName(part.string())) {
505 uiModeNight = part;
506
507 index++;
508 if (index == N) {
509 goto success;
510 }
511 part = parts[index];
512 } else {
513 //printf("not ui mode night: %s\n", part.string());
514 }
515
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 // density
517 if (getDensityName(part.string())) {
518 den = part;
519
520 index++;
521 if (index == N) {
522 goto success;
523 }
524 part = parts[index];
525 } else {
526 //printf("not density: %s\n", part.string());
527 }
528
529 // touchscreen
530 if (getTouchscreenName(part.string())) {
531 touch = part;
532
533 index++;
534 if (index == N) {
535 goto success;
536 }
537 part = parts[index];
538 } else {
539 //printf("not touchscreen: %s\n", part.string());
540 }
541
542 // keyboard hidden
543 if (getKeysHiddenName(part.string())) {
544 keysHidden = part;
545
546 index++;
547 if (index == N) {
548 goto success;
549 }
550 part = parts[index];
551 } else {
552 //printf("not keysHidden: %s\n", part.string());
553 }
554
555 // keyboard
556 if (getKeyboardName(part.string())) {
557 key = part;
558
559 index++;
560 if (index == N) {
561 goto success;
562 }
563 part = parts[index];
564 } else {
565 //printf("not keyboard: %s\n", part.string());
566 }
567
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700568 // navigation hidden
569 if (getNavHiddenName(part.string())) {
570 navHidden = part;
571
572 index++;
573 if (index == N) {
574 goto success;
575 }
576 part = parts[index];
577 } else {
578 //printf("not navHidden: %s\n", part.string());
579 }
580
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 if (getNavigationName(part.string())) {
582 nav = part;
583
584 index++;
585 if (index == N) {
586 goto success;
587 }
588 part = parts[index];
589 } else {
590 //printf("not navigation: %s\n", part.string());
591 }
592
593 if (getScreenSizeName(part.string())) {
594 size = part;
595
596 index++;
597 if (index == N) {
598 goto success;
599 }
600 part = parts[index];
601 } else {
602 //printf("not screen size: %s\n", part.string());
603 }
604
605 if (getVersionName(part.string())) {
606 vers = part;
607
608 index++;
609 if (index == N) {
610 goto success;
611 }
612 part = parts[index];
613 } else {
614 //printf("not version: %s\n", part.string());
615 }
616
617 // if there are extra parts, it doesn't match
618 return false;
619
620success:
621 this->mcc = mcc;
622 this->mnc = mnc;
623 this->locale = loc;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700624 this->screenLayoutSize = layoutsize;
625 this->screenLayoutLong = layoutlong;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700626 this->smallestScreenWidthDp = smallestwidthdp;
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700627 this->screenWidthDp = widthdp;
628 this->screenHeightDp = heightdp;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 this->orientation = orient;
Tobias Haamel27b28b32010-02-09 23:09:17 +0100630 this->uiModeType = uiModeType;
631 this->uiModeNight = uiModeNight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 this->density = den;
633 this->touchscreen = touch;
634 this->keysHidden = keysHidden;
635 this->keyboard = key;
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700636 this->navHidden = navHidden;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 this->navigation = nav;
638 this->screenSize = size;
639 this->version = vers;
640
641 // what is this anyway?
642 this->vendor = "";
643
644 return true;
645}
646
647String8
648AaptGroupEntry::toString() const
649{
650 String8 s = this->mcc;
651 s += ",";
652 s += this->mnc;
653 s += ",";
654 s += this->locale;
655 s += ",";
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700656 s += smallestScreenWidthDp;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700657 s += ",";
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700658 s += screenWidthDp;
659 s += ",";
660 s += screenHeightDp;
661 s += ",";
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700662 s += screenLayoutSize;
663 s += ",";
664 s += screenLayoutLong;
665 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 s += this->orientation;
667 s += ",";
Tobias Haamel27b28b32010-02-09 23:09:17 +0100668 s += uiModeType;
669 s += ",";
670 s += uiModeNight;
671 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 s += density;
673 s += ",";
674 s += touchscreen;
675 s += ",";
676 s += keysHidden;
677 s += ",";
678 s += keyboard;
679 s += ",";
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700680 s += navHidden;
681 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 s += navigation;
683 s += ",";
684 s += screenSize;
685 s += ",";
686 s += version;
687 return s;
688}
689
690String8
691AaptGroupEntry::toDirName(const String8& resType) const
692{
693 String8 s = resType;
694 if (this->mcc != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700695 if (s.length() > 0) {
696 s += "-";
697 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 s += mcc;
699 }
700 if (this->mnc != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700701 if (s.length() > 0) {
702 s += "-";
703 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 s += mnc;
705 }
706 if (this->locale != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700707 if (s.length() > 0) {
708 s += "-";
709 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710 s += locale;
711 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700712 if (this->smallestScreenWidthDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700713 if (s.length() > 0) {
714 s += "-";
715 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700716 s += smallestScreenWidthDp;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700717 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700718 if (this->screenWidthDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700719 if (s.length() > 0) {
720 s += "-";
721 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700722 s += screenWidthDp;
723 }
724 if (this->screenHeightDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700725 if (s.length() > 0) {
726 s += "-";
727 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700728 s += screenHeightDp;
729 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700730 if (this->screenLayoutSize != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700731 if (s.length() > 0) {
732 s += "-";
733 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700734 s += screenLayoutSize;
735 }
736 if (this->screenLayoutLong != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700737 if (s.length() > 0) {
738 s += "-";
739 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700740 s += screenLayoutLong;
741 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 if (this->orientation != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700743 if (s.length() > 0) {
744 s += "-";
745 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 s += orientation;
747 }
Tobias Haamel27b28b32010-02-09 23:09:17 +0100748 if (this->uiModeType != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700749 if (s.length() > 0) {
750 s += "-";
751 }
Tobias Haamel27b28b32010-02-09 23:09:17 +0100752 s += uiModeType;
753 }
754 if (this->uiModeNight != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700755 if (s.length() > 0) {
756 s += "-";
757 }
Tobias Haamel27b28b32010-02-09 23:09:17 +0100758 s += uiModeNight;
759 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 if (this->density != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700761 if (s.length() > 0) {
762 s += "-";
763 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 s += density;
765 }
766 if (this->touchscreen != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700767 if (s.length() > 0) {
768 s += "-";
769 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 s += touchscreen;
771 }
772 if (this->keysHidden != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700773 if (s.length() > 0) {
774 s += "-";
775 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 s += keysHidden;
777 }
778 if (this->keyboard != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700779 if (s.length() > 0) {
780 s += "-";
781 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 s += keyboard;
783 }
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700784 if (this->navHidden != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700785 if (s.length() > 0) {
786 s += "-";
787 }
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700788 s += navHidden;
789 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 if (this->navigation != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700791 if (s.length() > 0) {
792 s += "-";
793 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 s += navigation;
795 }
796 if (this->screenSize != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700797 if (s.length() > 0) {
798 s += "-";
799 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 s += screenSize;
801 }
802 if (this->version != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -0700803 if (s.length() > 0) {
804 s += "-";
805 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 s += version;
807 }
808
809 return s;
810}
811
812bool AaptGroupEntry::getMccName(const char* name,
813 ResTable_config* out)
814{
815 if (strcmp(name, kWildcardName) == 0) {
816 if (out) out->mcc = 0;
817 return true;
818 }
819 const char* c = name;
820 if (tolower(*c) != 'm') return false;
821 c++;
822 if (tolower(*c) != 'c') return false;
823 c++;
824 if (tolower(*c) != 'c') return false;
825 c++;
826
827 const char* val = c;
828
829 while (*c >= '0' && *c <= '9') {
830 c++;
831 }
832 if (*c != 0) return false;
833 if (c-val != 3) return false;
834
835 int d = atoi(val);
836 if (d != 0) {
837 if (out) out->mcc = d;
838 return true;
839 }
840
841 return false;
842}
843
844bool AaptGroupEntry::getMncName(const char* name,
845 ResTable_config* out)
846{
847 if (strcmp(name, kWildcardName) == 0) {
848 if (out) out->mcc = 0;
849 return true;
850 }
851 const char* c = name;
852 if (tolower(*c) != 'm') return false;
853 c++;
854 if (tolower(*c) != 'n') return false;
855 c++;
856 if (tolower(*c) != 'c') return false;
857 c++;
858
859 const char* val = c;
860
861 while (*c >= '0' && *c <= '9') {
862 c++;
863 }
864 if (*c != 0) return false;
865 if (c-val == 0 || c-val > 3) return false;
866
Johan Redestig5ef0b9d2010-11-09 14:13:31 +0100867 if (out) {
868 out->mnc = atoi(val);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 }
870
Johan Redestig5ef0b9d2010-11-09 14:13:31 +0100871 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872}
873
874/*
875 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
876 * "default")?
877 *
878 * TODO: Should insist that the first two letters are lower case, and the
879 * second two are upper.
880 */
881bool AaptGroupEntry::getLocaleName(const char* fileName,
882 ResTable_config* out)
883{
884 if (strcmp(fileName, kWildcardName) == 0
885 || strcmp(fileName, kDefaultLocale) == 0) {
886 if (out) {
887 out->language[0] = 0;
888 out->language[1] = 0;
889 out->country[0] = 0;
890 out->country[1] = 0;
891 }
892 return true;
893 }
894
895 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
896 if (out) {
897 out->language[0] = fileName[0];
898 out->language[1] = fileName[1];
899 out->country[0] = 0;
900 out->country[1] = 0;
901 }
902 return true;
903 }
904
905 if (strlen(fileName) == 5 &&
906 isalpha(fileName[0]) &&
907 isalpha(fileName[1]) &&
908 fileName[2] == '-' &&
909 isalpha(fileName[3]) &&
910 isalpha(fileName[4])) {
911 if (out) {
912 out->language[0] = fileName[0];
913 out->language[1] = fileName[1];
914 out->country[0] = fileName[3];
915 out->country[1] = fileName[4];
916 }
917 return true;
918 }
919
920 return false;
921}
922
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700923bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
924 ResTable_config* out)
925{
926 if (strcmp(name, kWildcardName) == 0) {
927 if (out) out->screenLayout =
928 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
929 | ResTable_config::SCREENSIZE_ANY;
930 return true;
931 } else if (strcmp(name, "small") == 0) {
932 if (out) out->screenLayout =
933 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
934 | ResTable_config::SCREENSIZE_SMALL;
935 return true;
936 } else if (strcmp(name, "normal") == 0) {
937 if (out) out->screenLayout =
938 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
939 | ResTable_config::SCREENSIZE_NORMAL;
940 return true;
941 } else if (strcmp(name, "large") == 0) {
942 if (out) out->screenLayout =
943 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
944 | ResTable_config::SCREENSIZE_LARGE;
945 return true;
Dianne Hackborn14cee9f2010-04-23 17:51:26 -0700946 } else if (strcmp(name, "xlarge") == 0) {
947 if (out) out->screenLayout =
948 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
949 | ResTable_config::SCREENSIZE_XLARGE;
950 return true;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700951 }
952
953 return false;
954}
955
956bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
957 ResTable_config* out)
958{
959 if (strcmp(name, kWildcardName) == 0) {
960 if (out) out->screenLayout =
961 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
962 | ResTable_config::SCREENLONG_ANY;
963 return true;
964 } else if (strcmp(name, "long") == 0) {
965 if (out) out->screenLayout =
966 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
967 | ResTable_config::SCREENLONG_YES;
968 return true;
969 } else if (strcmp(name, "notlong") == 0) {
970 if (out) out->screenLayout =
971 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
972 | ResTable_config::SCREENLONG_NO;
973 return true;
974 }
975
976 return false;
977}
978
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979bool AaptGroupEntry::getOrientationName(const char* name,
980 ResTable_config* out)
981{
982 if (strcmp(name, kWildcardName) == 0) {
983 if (out) out->orientation = out->ORIENTATION_ANY;
984 return true;
985 } else if (strcmp(name, "port") == 0) {
986 if (out) out->orientation = out->ORIENTATION_PORT;
987 return true;
988 } else if (strcmp(name, "land") == 0) {
989 if (out) out->orientation = out->ORIENTATION_LAND;
990 return true;
991 } else if (strcmp(name, "square") == 0) {
992 if (out) out->orientation = out->ORIENTATION_SQUARE;
993 return true;
994 }
995
996 return false;
997}
998
Tobias Haamel27b28b32010-02-09 23:09:17 +0100999bool AaptGroupEntry::getUiModeTypeName(const char* name,
1000 ResTable_config* out)
1001{
1002 if (strcmp(name, kWildcardName) == 0) {
1003 if (out) out->uiMode =
1004 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
Dianne Hackborn7299c412010-03-04 18:41:49 -08001005 | ResTable_config::UI_MODE_TYPE_ANY;
1006 return true;
1007 } else if (strcmp(name, "desk") == 0) {
1008 if (out) out->uiMode =
1009 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1010 | ResTable_config::UI_MODE_TYPE_DESK;
Tobias Haamel27b28b32010-02-09 23:09:17 +01001011 return true;
1012 } else if (strcmp(name, "car") == 0) {
1013 if (out) out->uiMode =
1014 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1015 | ResTable_config::UI_MODE_TYPE_CAR;
1016 return true;
Dianne Hackborne360bb62011-05-20 16:11:04 -07001017 } else if (strcmp(name, "television") == 0) {
1018 if (out) out->uiMode =
1019 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1020 | ResTable_config::UI_MODE_TYPE_TELEVISION;
1021 return true;
Joe Onorato44fcb832011-12-14 20:59:30 -08001022 } else if (strcmp(name, "appliance") == 0) {
1023 if (out) out->uiMode =
1024 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1025 | ResTable_config::UI_MODE_TYPE_APPLIANCE;
1026 return true;
Tobias Haamel27b28b32010-02-09 23:09:17 +01001027 }
1028
1029 return false;
1030}
1031
1032bool AaptGroupEntry::getUiModeNightName(const char* name,
1033 ResTable_config* out)
1034{
1035 if (strcmp(name, kWildcardName) == 0) {
1036 if (out) out->uiMode =
1037 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1038 | ResTable_config::UI_MODE_NIGHT_ANY;
1039 return true;
1040 } else if (strcmp(name, "night") == 0) {
1041 if (out) out->uiMode =
1042 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1043 | ResTable_config::UI_MODE_NIGHT_YES;
1044 return true;
1045 } else if (strcmp(name, "notnight") == 0) {
1046 if (out) out->uiMode =
1047 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1048 | ResTable_config::UI_MODE_NIGHT_NO;
1049 return true;
1050 }
1051
1052 return false;
1053}
1054
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055bool AaptGroupEntry::getDensityName(const char* name,
1056 ResTable_config* out)
1057{
1058 if (strcmp(name, kWildcardName) == 0) {
Dianne Hackborna53b8282009-07-17 11:13:48 -07001059 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 return true;
1061 }
Dianne Hackborna53b8282009-07-17 11:13:48 -07001062
1063 if (strcmp(name, "nodpi") == 0) {
1064 if (out) out->density = ResTable_config::DENSITY_NONE;
1065 return true;
1066 }
1067
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001068 if (strcmp(name, "ldpi") == 0) {
1069 if (out) out->density = ResTable_config::DENSITY_LOW;
1070 return true;
1071 }
1072
1073 if (strcmp(name, "mdpi") == 0) {
1074 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1075 return true;
1076 }
1077
Dianne Hackbornb96cbbd2011-05-27 13:40:26 -07001078 if (strcmp(name, "tvdpi") == 0) {
1079 if (out) out->density = ResTable_config::DENSITY_TV;
1080 return true;
1081 }
1082
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001083 if (strcmp(name, "hdpi") == 0) {
1084 if (out) out->density = ResTable_config::DENSITY_HIGH;
1085 return true;
1086 }
1087
Dianne Hackborn588feee2010-06-04 14:36:39 -07001088 if (strcmp(name, "xhdpi") == 0) {
1089 if (out) out->density = ResTable_config::DENSITY_MEDIUM*2;
1090 return true;
1091 }
1092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 char* c = (char*)name;
1094 while (*c >= '0' && *c <= '9') {
1095 c++;
1096 }
1097
1098 // check that we have 'dpi' after the last digit.
1099 if (toupper(c[0]) != 'D' ||
1100 toupper(c[1]) != 'P' ||
1101 toupper(c[2]) != 'I' ||
1102 c[3] != 0) {
1103 return false;
1104 }
1105
1106 // temporarily replace the first letter with \0 to
1107 // use atoi.
1108 char tmp = c[0];
1109 c[0] = '\0';
1110
1111 int d = atoi(name);
1112 c[0] = tmp;
1113
1114 if (d != 0) {
1115 if (out) out->density = d;
1116 return true;
1117 }
1118
1119 return false;
1120}
1121
1122bool AaptGroupEntry::getTouchscreenName(const char* name,
1123 ResTable_config* out)
1124{
1125 if (strcmp(name, kWildcardName) == 0) {
1126 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1127 return true;
1128 } else if (strcmp(name, "notouch") == 0) {
1129 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1130 return true;
1131 } else if (strcmp(name, "stylus") == 0) {
1132 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1133 return true;
1134 } else if (strcmp(name, "finger") == 0) {
1135 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1136 return true;
1137 }
1138
1139 return false;
1140}
1141
1142bool AaptGroupEntry::getKeysHiddenName(const char* name,
1143 ResTable_config* out)
1144{
1145 uint8_t mask = 0;
1146 uint8_t value = 0;
1147 if (strcmp(name, kWildcardName) == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001148 mask = ResTable_config::MASK_KEYSHIDDEN;
1149 value = ResTable_config::KEYSHIDDEN_ANY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 } else if (strcmp(name, "keysexposed") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001151 mask = ResTable_config::MASK_KEYSHIDDEN;
1152 value = ResTable_config::KEYSHIDDEN_NO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 } else if (strcmp(name, "keyshidden") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001154 mask = ResTable_config::MASK_KEYSHIDDEN;
1155 value = ResTable_config::KEYSHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001156 } else if (strcmp(name, "keyssoft") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001157 mask = ResTable_config::MASK_KEYSHIDDEN;
1158 value = ResTable_config::KEYSHIDDEN_SOFT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001159 }
1160
1161 if (mask != 0) {
1162 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1163 return true;
1164 }
1165
1166 return false;
1167}
1168
1169bool AaptGroupEntry::getKeyboardName(const char* name,
1170 ResTable_config* out)
1171{
1172 if (strcmp(name, kWildcardName) == 0) {
1173 if (out) out->keyboard = out->KEYBOARD_ANY;
1174 return true;
1175 } else if (strcmp(name, "nokeys") == 0) {
1176 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1177 return true;
1178 } else if (strcmp(name, "qwerty") == 0) {
1179 if (out) out->keyboard = out->KEYBOARD_QWERTY;
1180 return true;
1181 } else if (strcmp(name, "12key") == 0) {
1182 if (out) out->keyboard = out->KEYBOARD_12KEY;
1183 return true;
1184 }
1185
1186 return false;
1187}
1188
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001189bool AaptGroupEntry::getNavHiddenName(const char* name,
1190 ResTable_config* out)
1191{
1192 uint8_t mask = 0;
1193 uint8_t value = 0;
1194 if (strcmp(name, kWildcardName) == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001195 mask = ResTable_config::MASK_NAVHIDDEN;
1196 value = ResTable_config::NAVHIDDEN_ANY;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001197 } else if (strcmp(name, "navexposed") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001198 mask = ResTable_config::MASK_NAVHIDDEN;
1199 value = ResTable_config::NAVHIDDEN_NO;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001200 } else if (strcmp(name, "navhidden") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001201 mask = ResTable_config::MASK_NAVHIDDEN;
1202 value = ResTable_config::NAVHIDDEN_YES;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001203 }
1204
1205 if (mask != 0) {
1206 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1207 return true;
1208 }
1209
1210 return false;
1211}
1212
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213bool AaptGroupEntry::getNavigationName(const char* name,
1214 ResTable_config* out)
1215{
1216 if (strcmp(name, kWildcardName) == 0) {
1217 if (out) out->navigation = out->NAVIGATION_ANY;
1218 return true;
1219 } else if (strcmp(name, "nonav") == 0) {
1220 if (out) out->navigation = out->NAVIGATION_NONAV;
1221 return true;
1222 } else if (strcmp(name, "dpad") == 0) {
1223 if (out) out->navigation = out->NAVIGATION_DPAD;
1224 return true;
1225 } else if (strcmp(name, "trackball") == 0) {
1226 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1227 return true;
1228 } else if (strcmp(name, "wheel") == 0) {
1229 if (out) out->navigation = out->NAVIGATION_WHEEL;
1230 return true;
1231 }
1232
1233 return false;
1234}
1235
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001236bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237{
1238 if (strcmp(name, kWildcardName) == 0) {
1239 if (out) {
1240 out->screenWidth = out->SCREENWIDTH_ANY;
1241 out->screenHeight = out->SCREENHEIGHT_ANY;
1242 }
1243 return true;
1244 }
1245
1246 const char* x = name;
1247 while (*x >= '0' && *x <= '9') x++;
1248 if (x == name || *x != 'x') return false;
1249 String8 xName(name, x-name);
1250 x++;
1251
1252 const char* y = x;
1253 while (*y >= '0' && *y <= '9') y++;
1254 if (y == name || *y != 0) return false;
1255 String8 yName(x, y-x);
1256
1257 uint16_t w = (uint16_t)atoi(xName.string());
1258 uint16_t h = (uint16_t)atoi(yName.string());
1259 if (w < h) {
1260 return false;
1261 }
1262
1263 if (out) {
1264 out->screenWidth = w;
1265 out->screenHeight = h;
1266 }
1267
1268 return true;
1269}
1270
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001271bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1272{
1273 if (strcmp(name, kWildcardName) == 0) {
1274 if (out) {
1275 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1276 }
1277 return true;
1278 }
1279
1280 if (*name != 's') return false;
1281 name++;
1282 if (*name != 'w') return false;
1283 name++;
1284 const char* x = name;
1285 while (*x >= '0' && *x <= '9') x++;
1286 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1287 String8 xName(name, x-name);
1288
1289 if (out) {
1290 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1291 }
1292
1293 return true;
1294}
1295
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001296bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1297{
1298 if (strcmp(name, kWildcardName) == 0) {
1299 if (out) {
1300 out->screenWidthDp = out->SCREENWIDTH_ANY;
1301 }
1302 return true;
1303 }
1304
1305 if (*name != 'w') return false;
1306 name++;
1307 const char* x = name;
1308 while (*x >= '0' && *x <= '9') x++;
1309 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1310 String8 xName(name, x-name);
1311
1312 if (out) {
1313 out->screenWidthDp = (uint16_t)atoi(xName.string());
1314 }
1315
1316 return true;
1317}
1318
1319bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1320{
1321 if (strcmp(name, kWildcardName) == 0) {
1322 if (out) {
1323 out->screenHeightDp = out->SCREENWIDTH_ANY;
1324 }
1325 return true;
1326 }
1327
1328 if (*name != 'h') return false;
1329 name++;
1330 const char* x = name;
1331 while (*x >= '0' && *x <= '9') x++;
1332 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1333 String8 xName(name, x-name);
1334
1335 if (out) {
1336 out->screenHeightDp = (uint16_t)atoi(xName.string());
1337 }
1338
1339 return true;
1340}
1341
1342bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343{
1344 if (strcmp(name, kWildcardName) == 0) {
1345 if (out) {
1346 out->sdkVersion = out->SDKVERSION_ANY;
1347 out->minorVersion = out->MINORVERSION_ANY;
1348 }
1349 return true;
1350 }
1351
1352 if (*name != 'v') {
1353 return false;
1354 }
1355
1356 name++;
1357 const char* s = name;
1358 while (*s >= '0' && *s <= '9') s++;
1359 if (s == name || *s != 0) return false;
1360 String8 sdkName(name, s-name);
1361
1362 if (out) {
1363 out->sdkVersion = (uint16_t)atoi(sdkName.string());
1364 out->minorVersion = 0;
1365 }
1366
1367 return true;
1368}
1369
1370int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1371{
1372 int v = mcc.compare(o.mcc);
1373 if (v == 0) v = mnc.compare(o.mnc);
1374 if (v == 0) v = locale.compare(o.locale);
1375 if (v == 0) v = vendor.compare(o.vendor);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001376 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001377 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1378 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001379 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1380 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 if (v == 0) v = orientation.compare(o.orientation);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001382 if (v == 0) v = uiModeType.compare(o.uiModeType);
1383 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 if (v == 0) v = density.compare(o.density);
1385 if (v == 0) v = touchscreen.compare(o.touchscreen);
1386 if (v == 0) v = keysHidden.compare(o.keysHidden);
1387 if (v == 0) v = keyboard.compare(o.keyboard);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001388 if (v == 0) v = navHidden.compare(o.navHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001389 if (v == 0) v = navigation.compare(o.navigation);
1390 if (v == 0) v = screenSize.compare(o.screenSize);
1391 if (v == 0) v = version.compare(o.version);
1392 return v;
1393}
1394
Dianne Hackborne6b68032011-10-13 16:26:02 -07001395const ResTable_config& AaptGroupEntry::toParams() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001396{
Dianne Hackborne6b68032011-10-13 16:26:02 -07001397 if (!mParamsChanged) {
1398 return mParams;
1399 }
1400
1401 mParamsChanged = false;
1402 ResTable_config& params(mParams);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 memset(&params, 0, sizeof(params));
1404 getMccName(mcc.string(), &params);
1405 getMncName(mnc.string(), &params);
1406 getLocaleName(locale.string(), &params);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001407 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001408 getScreenWidthDpName(screenWidthDp.string(), &params);
1409 getScreenHeightDpName(screenHeightDp.string(), &params);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001410 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1411 getScreenLayoutLongName(screenLayoutLong.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001412 getOrientationName(orientation.string(), &params);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001413 getUiModeTypeName(uiModeType.string(), &params);
1414 getUiModeNightName(uiModeNight.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001415 getDensityName(density.string(), &params);
1416 getTouchscreenName(touchscreen.string(), &params);
1417 getKeysHiddenName(keysHidden.string(), &params);
1418 getKeyboardName(keyboard.string(), &params);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001419 getNavHiddenName(navHidden.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001420 getNavigationName(navigation.string(), &params);
1421 getScreenSizeName(screenSize.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001422 getVersionName(version.string(), &params);
Dianne Hackbornef05e072010-03-01 17:43:39 -08001423
1424 // Fix up version number based on specified parameters.
1425 int minSdk = 0;
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001426 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1427 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001428 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001429 minSdk = SDK_HONEYCOMB_MR2;
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001430 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
Dianne Hackbornef05e072010-03-01 17:43:39 -08001431 != ResTable_config::UI_MODE_TYPE_ANY
1432 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1433 != ResTable_config::UI_MODE_NIGHT_ANY) {
1434 minSdk = SDK_FROYO;
1435 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1436 != ResTable_config::SCREENSIZE_ANY
1437 || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1438 != ResTable_config::SCREENLONG_ANY
1439 || params.density != ResTable_config::DENSITY_DEFAULT) {
1440 minSdk = SDK_DONUT;
1441 }
1442
1443 if (minSdk > params.sdkVersion) {
1444 params.sdkVersion = minSdk;
1445 }
1446
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001447 return params;
1448}
1449
1450// =========================================================================
1451// =========================================================================
1452// =========================================================================
1453
1454void* AaptFile::editData(size_t size)
1455{
1456 if (size <= mBufferSize) {
1457 mDataSize = size;
1458 return mData;
1459 }
1460 size_t allocSize = (size*3)/2;
1461 void* buf = realloc(mData, allocSize);
1462 if (buf == NULL) {
1463 return NULL;
1464 }
1465 mData = buf;
1466 mDataSize = size;
1467 mBufferSize = allocSize;
1468 return buf;
1469}
1470
1471void* AaptFile::editData(size_t* outSize)
1472{
1473 if (outSize) {
1474 *outSize = mDataSize;
1475 }
1476 return mData;
1477}
1478
1479void* AaptFile::padData(size_t wordSize)
1480{
1481 const size_t extra = mDataSize%wordSize;
1482 if (extra == 0) {
1483 return mData;
1484 }
1485
1486 size_t initial = mDataSize;
1487 void* data = editData(initial+(wordSize-extra));
1488 if (data != NULL) {
1489 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1490 }
1491 return data;
1492}
1493
1494status_t AaptFile::writeData(const void* data, size_t size)
1495{
1496 size_t end = mDataSize;
1497 size_t total = size + end;
1498 void* buf = editData(total);
1499 if (buf == NULL) {
1500 return UNKNOWN_ERROR;
1501 }
1502 memcpy(((char*)buf)+end, data, size);
1503 return NO_ERROR;
1504}
1505
1506void AaptFile::clearData()
1507{
1508 if (mData != NULL) free(mData);
1509 mData = NULL;
1510 mDataSize = 0;
1511 mBufferSize = 0;
1512}
1513
1514String8 AaptFile::getPrintableSource() const
1515{
1516 if (hasData()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001517 String8 name(mGroupEntry.toDirName(String8()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 name.appendPath(mPath);
1519 name.append(" #generated");
1520 return name;
1521 }
1522 return mSourceFile;
1523}
1524
1525// =========================================================================
1526// =========================================================================
1527// =========================================================================
1528
1529status_t AaptGroup::addFile(const sp<AaptFile>& file)
1530{
1531 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1532 file->mPath = mPath;
1533 mFiles.add(file->getGroupEntry(), file);
1534 return NO_ERROR;
1535 }
1536
Dianne Hackborne6b68032011-10-13 16:26:02 -07001537#if 0
1538 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1539 file->getSourceFile().string(),
1540 file->getGroupEntry().toDirName(String8()).string(),
1541 mLeaf.string(), mPath.string());
1542#endif
1543
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001544 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1545 getPrintableSource().string());
1546 return UNKNOWN_ERROR;
1547}
1548
1549void AaptGroup::removeFile(size_t index)
1550{
1551 mFiles.removeItemsAt(index);
1552}
1553
Dianne Hackborne6b68032011-10-13 16:26:02 -07001554void AaptGroup::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555{
Dianne Hackborne6b68032011-10-13 16:26:02 -07001556 printf("%s%s\n", prefix.string(), getPath().string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 const size_t N=mFiles.size();
1558 size_t i;
1559 for (i=0; i<N; i++) {
1560 sp<AaptFile> file = mFiles.valueAt(i);
1561 const AaptGroupEntry& e = file->getGroupEntry();
1562 if (file->hasData()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001563 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 (int)file->getSize());
1565 } else {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001566 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1567 file->getPrintableSource().string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001568 }
Dianne Hackborne6b68032011-10-13 16:26:02 -07001569 //printf("%s File Group Entry: %s\n", prefix.string(),
1570 // file->getGroupEntry().toDirName(String8()).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001571 }
1572}
1573
1574String8 AaptGroup::getPrintableSource() const
1575{
1576 if (mFiles.size() > 0) {
1577 // Arbitrarily pull the first source file out of the list.
1578 return mFiles.valueAt(0)->getPrintableSource();
1579 }
1580
1581 // Should never hit this case, but to be safe...
1582 return getPath();
1583
1584}
1585
1586// =========================================================================
1587// =========================================================================
1588// =========================================================================
1589
1590status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1591{
1592 if (mFiles.indexOfKey(name) >= 0) {
1593 return ALREADY_EXISTS;
1594 }
1595 mFiles.add(name, file);
1596 return NO_ERROR;
1597}
1598
1599status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1600{
1601 if (mDirs.indexOfKey(name) >= 0) {
1602 return ALREADY_EXISTS;
1603 }
1604 mDirs.add(name, dir);
1605 return NO_ERROR;
1606}
1607
1608sp<AaptDir> AaptDir::makeDir(const String8& path)
1609{
1610 String8 name;
1611 String8 remain = path;
1612
1613 sp<AaptDir> subdir = this;
1614 while (name = remain.walkPath(&remain), remain != "") {
1615 subdir = subdir->makeDir(name);
1616 }
1617
1618 ssize_t i = subdir->mDirs.indexOfKey(name);
1619 if (i >= 0) {
1620 return subdir->mDirs.valueAt(i);
1621 }
1622 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1623 subdir->mDirs.add(name, dir);
1624 return dir;
1625}
1626
1627void AaptDir::removeFile(const String8& name)
1628{
1629 mFiles.removeItem(name);
1630}
1631
1632void AaptDir::removeDir(const String8& name)
1633{
1634 mDirs.removeItem(name);
1635}
1636
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1638{
1639 sp<AaptGroup> group;
1640 if (mFiles.indexOfKey(leafName) >= 0) {
1641 group = mFiles.valueFor(leafName);
1642 } else {
1643 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1644 mFiles.add(leafName, group);
1645 }
1646
1647 return group->addFile(file);
1648}
1649
1650ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001651 const AaptGroupEntry& kind, const String8& resType,
1652 sp<FilePathStore>& fullResPaths)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001653{
1654 Vector<String8> fileNames;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001655 {
1656 DIR* dir = NULL;
1657
1658 dir = opendir(srcDir.string());
1659 if (dir == NULL) {
1660 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1661 return UNKNOWN_ERROR;
1662 }
1663
1664 /*
1665 * Slurp the filenames out of the directory.
1666 */
1667 while (1) {
1668 struct dirent* entry;
1669
1670 entry = readdir(dir);
1671 if (entry == NULL)
1672 break;
1673
1674 if (isHidden(srcDir.string(), entry->d_name))
1675 continue;
1676
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001677 String8 name(entry->d_name);
1678 fileNames.add(name);
1679 // Add fully qualified path for dependency purposes
1680 // if we're collecting them
1681 if (fullResPaths != NULL) {
1682 fullResPaths->add(srcDir.appendPathCopy(name));
1683 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001685 closedir(dir);
1686 }
1687
1688 ssize_t count = 0;
1689
1690 /*
1691 * Stash away the files and recursively descend into subdirectories.
1692 */
1693 const size_t N = fileNames.size();
1694 size_t i;
1695 for (i = 0; i < N; i++) {
1696 String8 pathName(srcDir);
1697 FileType type;
1698
1699 pathName.appendPath(fileNames[i].string());
1700 type = getFileType(pathName.string());
1701 if (type == kFileTypeDirectory) {
1702 sp<AaptDir> subdir;
1703 bool notAdded = false;
1704 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1705 subdir = mDirs.valueFor(fileNames[i]);
1706 } else {
1707 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1708 notAdded = true;
1709 }
1710 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001711 resType, fullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001712 if (res < NO_ERROR) {
1713 return res;
1714 }
1715 if (res > 0 && notAdded) {
1716 mDirs.add(fileNames[i], subdir);
1717 }
1718 count += res;
1719 } else if (type == kFileTypeRegular) {
1720 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1721 status_t err = addLeafFile(fileNames[i], file);
1722 if (err != NO_ERROR) {
1723 return err;
1724 }
1725
1726 count++;
1727
1728 } else {
1729 if (bundle->getVerbose())
1730 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
1731 }
1732 }
1733
1734 return count;
1735}
1736
1737status_t AaptDir::validate() const
1738{
1739 const size_t NF = mFiles.size();
1740 const size_t ND = mDirs.size();
1741 size_t i;
1742 for (i = 0; i < NF; i++) {
1743 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1744 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1745 "Invalid filename. Unable to add.");
1746 return UNKNOWN_ERROR;
1747 }
1748
1749 size_t j;
1750 for (j = i+1; j < NF; j++) {
1751 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1752 mFiles.valueAt(j)->getLeaf().string()) == 0) {
1753 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1754 "File is case-insensitive equivalent to: %s",
1755 mFiles.valueAt(j)->getPrintableSource().string());
1756 return UNKNOWN_ERROR;
1757 }
1758
1759 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1760 // (this is mostly caught by the "marked" stuff, below)
1761 }
1762
1763 for (j = 0; j < ND; j++) {
1764 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1765 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1766 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1767 "File conflicts with dir from: %s",
1768 mDirs.valueAt(j)->getPrintableSource().string());
1769 return UNKNOWN_ERROR;
1770 }
1771 }
1772 }
1773
1774 for (i = 0; i < ND; i++) {
1775 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1776 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1777 "Invalid directory name, unable to add.");
1778 return UNKNOWN_ERROR;
1779 }
1780
1781 size_t j;
1782 for (j = i+1; j < ND; j++) {
1783 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1784 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1785 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1786 "Directory is case-insensitive equivalent to: %s",
1787 mDirs.valueAt(j)->getPrintableSource().string());
1788 return UNKNOWN_ERROR;
1789 }
1790 }
1791
1792 status_t err = mDirs.valueAt(i)->validate();
1793 if (err != NO_ERROR) {
1794 return err;
1795 }
1796 }
1797
1798 return NO_ERROR;
1799}
1800
Dianne Hackborne6b68032011-10-13 16:26:02 -07001801void AaptDir::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802{
1803 const size_t ND=getDirs().size();
1804 size_t i;
1805 for (i=0; i<ND; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001806 getDirs().valueAt(i)->print(prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001807 }
1808
1809 const size_t NF=getFiles().size();
1810 for (i=0; i<NF; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001811 getFiles().valueAt(i)->print(prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812 }
1813}
1814
1815String8 AaptDir::getPrintableSource() const
1816{
1817 if (mFiles.size() > 0) {
1818 // Arbitrarily pull the first file out of the list as the source dir.
1819 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1820 }
1821 if (mDirs.size() > 0) {
1822 // Or arbitrarily pull the first dir out of the list as the source dir.
1823 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1824 }
1825
1826 // Should never hit this case, but to be safe...
1827 return mPath;
1828
1829}
1830
1831// =========================================================================
1832// =========================================================================
1833// =========================================================================
1834
Dianne Hackborne6b68032011-10-13 16:26:02 -07001835AaptAssets::AaptAssets()
1836 : AaptDir(String8(), String8()),
1837 mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
1838{
1839}
1840
1841const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
1842 if (mChanged) {
1843 }
1844 return mGroupEntries;
1845}
1846
1847status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
1848{
1849 mChanged = true;
1850 return AaptDir::addFile(name, file);
1851}
1852
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001853sp<AaptFile> AaptAssets::addFile(
1854 const String8& filePath, const AaptGroupEntry& entry,
1855 const String8& srcDir, sp<AaptGroup>* outGroup,
1856 const String8& resType)
1857{
1858 sp<AaptDir> dir = this;
1859 sp<AaptGroup> group;
1860 sp<AaptFile> file;
1861 String8 root, remain(filePath), partialPath;
1862 while (remain.length() > 0) {
1863 root = remain.walkPath(&remain);
1864 partialPath.appendPath(root);
1865
1866 const String8 rootStr(root);
1867
1868 if (remain.length() == 0) {
1869 ssize_t i = dir->getFiles().indexOfKey(rootStr);
1870 if (i >= 0) {
1871 group = dir->getFiles().valueAt(i);
1872 } else {
1873 group = new AaptGroup(rootStr, filePath);
1874 status_t res = dir->addFile(rootStr, group);
1875 if (res != NO_ERROR) {
1876 return NULL;
1877 }
1878 }
1879 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1880 status_t res = group->addFile(file);
1881 if (res != NO_ERROR) {
1882 return NULL;
1883 }
1884 break;
1885
1886 } else {
1887 ssize_t i = dir->getDirs().indexOfKey(rootStr);
1888 if (i >= 0) {
1889 dir = dir->getDirs().valueAt(i);
1890 } else {
1891 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1892 status_t res = dir->addDir(rootStr, subdir);
1893 if (res != NO_ERROR) {
1894 return NULL;
1895 }
1896 dir = subdir;
1897 }
1898 }
1899 }
1900
1901 mGroupEntries.add(entry);
1902 if (outGroup) *outGroup = group;
1903 return file;
1904}
1905
1906void AaptAssets::addResource(const String8& leafName, const String8& path,
1907 const sp<AaptFile>& file, const String8& resType)
1908{
1909 sp<AaptDir> res = AaptDir::makeDir(kResString);
1910 String8 dirname = file->getGroupEntry().toDirName(resType);
1911 sp<AaptDir> subdir = res->makeDir(dirname);
1912 sp<AaptGroup> grr = new AaptGroup(leafName, path);
1913 grr->addFile(file);
1914
1915 subdir->addFile(leafName, grr);
1916}
1917
1918
1919ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1920{
1921 int count;
1922 int totalCount = 0;
1923 FileType type;
1924 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1925 const size_t dirCount =resDirs.size();
1926 sp<AaptAssets> current = this;
1927
1928 const int N = bundle->getFileSpecCount();
1929
1930 /*
1931 * If a package manifest was specified, include that first.
1932 */
1933 if (bundle->getAndroidManifestFile() != NULL) {
1934 // place at root of zip.
1935 String8 srcFile(bundle->getAndroidManifestFile());
1936 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1937 NULL, String8());
1938 totalCount++;
1939 }
1940
1941 /*
1942 * If a directory of custom assets was supplied, slurp 'em up.
1943 */
1944 if (bundle->getAssetSourceDir()) {
1945 const char* assetDir = bundle->getAssetSourceDir();
1946
1947 FileType type = getFileType(assetDir);
1948 if (type == kFileTypeNonexistent) {
1949 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1950 return UNKNOWN_ERROR;
1951 }
1952 if (type != kFileTypeDirectory) {
1953 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1954 return UNKNOWN_ERROR;
1955 }
1956
1957 String8 assetRoot(assetDir);
1958 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1959 AaptGroupEntry group;
1960 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001961 String8(), mFullAssetPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001962 if (count < 0) {
1963 totalCount = count;
1964 goto bail;
1965 }
1966 if (count > 0) {
1967 mGroupEntries.add(group);
1968 }
1969 totalCount += count;
1970
1971 if (bundle->getVerbose())
1972 printf("Found %d custom asset file%s in %s\n",
1973 count, (count==1) ? "" : "s", assetDir);
1974 }
1975
1976 /*
1977 * If a directory of resource-specific assets was supplied, slurp 'em up.
1978 */
1979 for (size_t i=0; i<dirCount; i++) {
1980 const char *res = resDirs[i];
1981 if (res) {
1982 type = getFileType(res);
1983 if (type == kFileTypeNonexistent) {
1984 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1985 return UNKNOWN_ERROR;
1986 }
1987 if (type == kFileTypeDirectory) {
1988 if (i>0) {
1989 sp<AaptAssets> nextOverlay = new AaptAssets();
1990 current->setOverlay(nextOverlay);
1991 current = nextOverlay;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001992 current->setFullResPaths(mFullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001993 }
1994 count = current->slurpResourceTree(bundle, String8(res));
1995
1996 if (count < 0) {
1997 totalCount = count;
1998 goto bail;
1999 }
2000 totalCount += count;
2001 }
2002 else {
2003 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2004 return UNKNOWN_ERROR;
2005 }
2006 }
2007
2008 }
2009 /*
2010 * Now do any additional raw files.
2011 */
2012 for (int arg=0; arg<N; arg++) {
2013 const char* assetDir = bundle->getFileSpecEntry(arg);
2014
2015 FileType type = getFileType(assetDir);
2016 if (type == kFileTypeNonexistent) {
2017 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2018 return UNKNOWN_ERROR;
2019 }
2020 if (type != kFileTypeDirectory) {
2021 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2022 return UNKNOWN_ERROR;
2023 }
2024
2025 String8 assetRoot(assetDir);
2026
2027 if (bundle->getVerbose())
2028 printf("Processing raw dir '%s'\n", (const char*) assetDir);
2029
2030 /*
2031 * Do a recursive traversal of subdir tree. We don't make any
2032 * guarantees about ordering, so we're okay with an inorder search
2033 * using whatever order the OS happens to hand back to us.
2034 */
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002035 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002036 if (count < 0) {
2037 /* failure; report error and remove archive */
2038 totalCount = count;
2039 goto bail;
2040 }
2041 totalCount += count;
2042
2043 if (bundle->getVerbose())
2044 printf("Found %d asset file%s in %s\n",
2045 count, (count==1) ? "" : "s", assetDir);
2046 }
2047
2048 count = validate();
2049 if (count != NO_ERROR) {
2050 totalCount = count;
2051 goto bail;
2052 }
2053
Dianne Hackborne6b68032011-10-13 16:26:02 -07002054 count = filter(bundle);
2055 if (count != NO_ERROR) {
2056 totalCount = count;
2057 goto bail;
2058 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002059
2060bail:
2061 return totalCount;
2062}
2063
2064ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2065 const AaptGroupEntry& kind,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002066 const String8& resType,
2067 sp<FilePathStore>& fullResPaths)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002068{
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002069 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002070 if (res > 0) {
2071 mGroupEntries.add(kind);
2072 }
2073
2074 return res;
2075}
2076
2077ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2078{
2079 ssize_t err = 0;
2080
2081 DIR* dir = opendir(srcDir.string());
2082 if (dir == NULL) {
2083 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2084 return UNKNOWN_ERROR;
2085 }
2086
2087 status_t count = 0;
2088
2089 /*
2090 * Run through the directory, looking for dirs that match the
2091 * expected pattern.
2092 */
2093 while (1) {
2094 struct dirent* entry = readdir(dir);
2095 if (entry == NULL) {
2096 break;
2097 }
2098
2099 if (isHidden(srcDir.string(), entry->d_name)) {
2100 continue;
2101 }
2102
2103 String8 subdirName(srcDir);
2104 subdirName.appendPath(entry->d_name);
2105
2106 AaptGroupEntry group;
2107 String8 resType;
2108 bool b = group.initFromDirName(entry->d_name, &resType);
2109 if (!b) {
2110 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2111 entry->d_name);
2112 err = -1;
2113 continue;
2114 }
2115
Dianne Hackborne6b68032011-10-13 16:26:02 -07002116 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07002117 int maxResInt = atoi(bundle->getMaxResVersion());
Dianne Hackborne6b68032011-10-13 16:26:02 -07002118 const char *verString = group.getVersionString().string();
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07002119 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2120 if (dirVersionInt > maxResInt) {
2121 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2122 continue;
2123 }
2124 }
2125
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002126 FileType type = getFileType(subdirName.string());
2127
2128 if (type == kFileTypeDirectory) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002129 sp<AaptDir> dir = makeDir(resType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002130 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002131 resType, mFullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002132 if (res < 0) {
2133 count = res;
2134 goto bail;
2135 }
2136 if (res > 0) {
2137 mGroupEntries.add(group);
2138 count += res;
2139 }
2140
Dianne Hackborne6b68032011-10-13 16:26:02 -07002141 // Only add this directory if we don't already have a resource dir
2142 // for the current type. This ensures that we only add the dir once
2143 // for all configs.
2144 sp<AaptDir> rdir = resDir(resType);
2145 if (rdir == NULL) {
2146 mResDirs.add(dir);
2147 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002148 } else {
2149 if (bundle->getVerbose()) {
2150 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
2151 }
2152 }
2153 }
2154
2155bail:
2156 closedir(dir);
2157 dir = NULL;
2158
2159 if (err != 0) {
2160 return err;
2161 }
2162 return count;
2163}
2164
2165ssize_t
2166AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2167{
2168 int count = 0;
2169 SortedVector<AaptGroupEntry> entries;
2170
2171 ZipFile* zip = new ZipFile;
2172 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2173 if (err != NO_ERROR) {
2174 fprintf(stderr, "error opening zip file %s\n", filename);
2175 count = err;
2176 delete zip;
2177 return -1;
2178 }
2179
2180 const int N = zip->getNumEntries();
2181 for (int i=0; i<N; i++) {
2182 ZipEntry* entry = zip->getEntryByIndex(i);
2183 if (entry->getDeleted()) {
2184 continue;
2185 }
2186
2187 String8 entryName(entry->getFileName());
2188
2189 String8 dirName = entryName.getPathDir();
2190 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2191
2192 String8 resType;
2193 AaptGroupEntry kind;
2194
2195 String8 remain;
2196 if (entryName.walkPath(&remain) == kResourceDir) {
2197 // these are the resources, pull their type out of the directory name
2198 kind.initFromDirName(remain.walkPath().string(), &resType);
2199 } else {
2200 // these are untyped and don't have an AaptGroupEntry
2201 }
2202 if (entries.indexOf(kind) < 0) {
2203 entries.add(kind);
2204 mGroupEntries.add(kind);
2205 }
2206
2207 // use the one from the zip file if they both exist.
2208 dir->removeFile(entryName.getPathLeaf());
2209
2210 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2211 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2212 if (err != NO_ERROR) {
2213 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2214 count = err;
2215 goto bail;
2216 }
2217 file->setCompressionMethod(entry->getCompressionMethod());
2218
2219#if 0
2220 if (entryName == "AndroidManifest.xml") {
2221 printf("AndroidManifest.xml\n");
2222 }
2223 printf("\n\nfile: %s\n", entryName.string());
2224#endif
2225
2226 size_t len = entry->getUncompressedLen();
2227 void* data = zip->uncompress(entry);
2228 void* buf = file->editData(len);
2229 memcpy(buf, data, len);
2230
2231#if 0
2232 const int OFF = 0;
2233 const unsigned char* p = (unsigned char*)data;
2234 const unsigned char* end = p+len;
2235 p += OFF;
2236 for (int i=0; i<32 && p < end; i++) {
2237 printf("0x%03x ", i*0x10 + OFF);
2238 for (int j=0; j<0x10 && p < end; j++) {
2239 printf(" %02x", *p);
2240 p++;
2241 }
2242 printf("\n");
2243 }
2244#endif
2245
2246 free(data);
2247
2248 count++;
2249 }
2250
2251bail:
2252 delete zip;
2253 return count;
2254}
2255
Dianne Hackborne6b68032011-10-13 16:26:02 -07002256status_t AaptAssets::filter(Bundle* bundle)
2257{
2258 ResourceFilter reqFilter;
2259 status_t err = reqFilter.parse(bundle->getConfigurations());
2260 if (err != NO_ERROR) {
2261 return err;
2262 }
2263
2264 ResourceFilter prefFilter;
2265 err = prefFilter.parse(bundle->getPreferredConfigurations());
2266 if (err != NO_ERROR) {
2267 return err;
2268 }
2269
2270 if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2271 return NO_ERROR;
2272 }
2273
Dianne Hackbornbd9d2bc2011-10-16 14:17:07 -07002274 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002275 if (!reqFilter.isEmpty()) {
2276 printf("Applying required filter: %s\n",
2277 bundle->getConfigurations());
2278 }
2279 if (!prefFilter.isEmpty()) {
2280 printf("Applying preferred filter: %s\n",
2281 bundle->getPreferredConfigurations());
2282 }
2283 }
2284
2285 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2286 const size_t ND = resdirs.size();
2287 for (size_t i=0; i<ND; i++) {
2288 const sp<AaptDir>& dir = resdirs.itemAt(i);
2289 if (dir->getLeaf() == kValuesDir) {
2290 // The "value" dir is special since a single file defines
2291 // multiple resources, so we can not do filtering on the
2292 // files themselves.
2293 continue;
2294 }
2295 if (dir->getLeaf() == kMipmapDir) {
2296 // We also skip the "mipmap" directory, since the point of this
2297 // is to include all densities without stripping. If you put
2298 // other configurations in here as well they won't be stripped
2299 // either... So don't do that. Seriously. What is wrong with you?
2300 continue;
2301 }
2302
2303 const size_t NG = dir->getFiles().size();
2304 for (size_t j=0; j<NG; j++) {
2305 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2306
2307 // First remove any configurations we know we don't need.
2308 for (size_t k=0; k<grp->getFiles().size(); k++) {
2309 sp<AaptFile> file = grp->getFiles().valueAt(k);
2310 if (k == 0 && grp->getFiles().size() == 1) {
2311 // If this is the only file left, we need to keep it.
2312 // Otherwise the resource IDs we are using will be inconsistent
2313 // with what we get when not stripping. Sucky, but at least
2314 // for now we can rely on the back-end doing another filtering
2315 // pass to take this out and leave us with this resource name
2316 // containing no entries.
2317 continue;
2318 }
2319 if (file->getPath().getPathExtension() == ".xml") {
2320 // We can't remove .xml files at this point, because when
2321 // we parse them they may add identifier resources, so
2322 // removing them can cause our resource identifiers to
2323 // become inconsistent.
2324 continue;
2325 }
2326 const ResTable_config& config(file->getGroupEntry().toParams());
2327 if (!reqFilter.match(config)) {
2328 if (bundle->getVerbose()) {
2329 printf("Pruning unneeded resource: %s\n",
2330 file->getPrintableSource().string());
2331 }
2332 grp->removeFile(k);
2333 k--;
2334 }
2335 }
2336
2337 // Quick check: no preferred filters, nothing more to do.
2338 if (prefFilter.isEmpty()) {
2339 continue;
2340 }
2341
2342 // Now deal with preferred configurations.
2343 for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2344 for (size_t k=0; k<grp->getFiles().size(); k++) {
2345 sp<AaptFile> file = grp->getFiles().valueAt(k);
2346 if (k == 0 && grp->getFiles().size() == 1) {
2347 // If this is the only file left, we need to keep it.
2348 // Otherwise the resource IDs we are using will be inconsistent
2349 // with what we get when not stripping. Sucky, but at least
2350 // for now we can rely on the back-end doing another filtering
2351 // pass to take this out and leave us with this resource name
2352 // containing no entries.
2353 continue;
2354 }
2355 if (file->getPath().getPathExtension() == ".xml") {
2356 // We can't remove .xml files at this point, because when
2357 // we parse them they may add identifier resources, so
2358 // removing them can cause our resource identifiers to
2359 // become inconsistent.
2360 continue;
2361 }
2362 const ResTable_config& config(file->getGroupEntry().toParams());
2363 if (!prefFilter.match(axis, config)) {
2364 // This is a resource we would prefer not to have. Check
2365 // to see if have a similar variation that we would like
2366 // to have and, if so, we can drop it.
2367 for (size_t m=0; m<grp->getFiles().size(); m++) {
2368 if (m == k) continue;
2369 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2370 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2371 if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2372 if (prefFilter.match(axis, mconfig)) {
2373 if (bundle->getVerbose()) {
2374 printf("Pruning unneeded resource: %s\n",
2375 file->getPrintableSource().string());
2376 }
2377 grp->removeFile(k);
2378 k--;
2379 break;
2380 }
2381 }
2382 }
2383 }
2384 }
2385 }
2386 }
2387 }
2388
2389 return NO_ERROR;
2390}
2391
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002392sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2393{
2394 sp<AaptSymbols> sym = mSymbols.valueFor(name);
2395 if (sym == NULL) {
2396 sym = new AaptSymbols();
2397 mSymbols.add(name, sym);
2398 }
2399 return sym;
2400}
2401
2402status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2403{
2404 if (!mHaveIncludedAssets) {
2405 // Add in all includes.
2406 const Vector<const char*>& incl = bundle->getPackageIncludes();
2407 const size_t N=incl.size();
2408 for (size_t i=0; i<N; i++) {
2409 if (bundle->getVerbose())
2410 printf("Including resources from package: %s\n", incl[i]);
2411 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2412 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2413 incl[i]);
2414 return UNKNOWN_ERROR;
2415 }
2416 }
2417 mHaveIncludedAssets = true;
2418 }
2419
2420 return NO_ERROR;
2421}
2422
2423status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2424{
2425 const ResTable& res = getIncludedResources();
2426 // XXX dirty!
2427 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2428}
2429
2430const ResTable& AaptAssets::getIncludedResources() const
2431{
2432 return mIncludedAssets.getResources(false);
2433}
2434
Dianne Hackborne6b68032011-10-13 16:26:02 -07002435void AaptAssets::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002436{
Dianne Hackborne6b68032011-10-13 16:26:02 -07002437 String8 innerPrefix(prefix);
2438 innerPrefix.append(" ");
2439 String8 innerInnerPrefix(innerPrefix);
2440 innerInnerPrefix.append(" ");
2441 printf("%sConfigurations:\n", prefix.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002442 const size_t N=mGroupEntries.size();
2443 for (size_t i=0; i<N; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002444 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2445 printf("%s %s\n", prefix.string(),
2446 cname != "" ? cname.string() : "(default)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002447 }
2448
Dianne Hackborne6b68032011-10-13 16:26:02 -07002449 printf("\n%sFiles:\n", prefix.string());
2450 AaptDir::print(innerPrefix);
2451
2452 printf("\n%sResource Dirs:\n", prefix.string());
2453 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2454 const size_t NR = resdirs.size();
2455 for (size_t i=0; i<NR; i++) {
2456 const sp<AaptDir>& d = resdirs.itemAt(i);
2457 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
2458 d->print(innerInnerPrefix);
2459 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002460}
2461
Dianne Hackborne6b68032011-10-13 16:26:02 -07002462sp<AaptDir> AaptAssets::resDir(const String8& name) const
Joe Onorato1553c822009-08-30 13:36:22 -07002463{
Dianne Hackborne6b68032011-10-13 16:26:02 -07002464 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2465 const size_t N = resdirs.size();
Joe Onorato1553c822009-08-30 13:36:22 -07002466 for (size_t i=0; i<N; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002467 const sp<AaptDir>& d = resdirs.itemAt(i);
Joe Onorato1553c822009-08-30 13:36:22 -07002468 if (d->getLeaf() == name) {
2469 return d;
2470 }
2471 }
2472 return NULL;
2473}
2474
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002475bool
2476valid_symbol_name(const String8& symbol)
2477{
2478 static char const * const KEYWORDS[] = {
2479 "abstract", "assert", "boolean", "break",
2480 "byte", "case", "catch", "char", "class", "const", "continue",
2481 "default", "do", "double", "else", "enum", "extends", "final",
2482 "finally", "float", "for", "goto", "if", "implements", "import",
2483 "instanceof", "int", "interface", "long", "native", "new", "package",
2484 "private", "protected", "public", "return", "short", "static",
2485 "strictfp", "super", "switch", "synchronized", "this", "throw",
2486 "throws", "transient", "try", "void", "volatile", "while",
2487 "true", "false", "null",
2488 NULL
2489 };
2490 const char*const* k = KEYWORDS;
2491 const char*const s = symbol.string();
2492 while (*k) {
2493 if (0 == strcmp(s, *k)) {
2494 return false;
2495 }
2496 k++;
2497 }
2498 return true;
2499}