blob: 3d6537a9013e41725bbeb4cfe2cca31a353973dc [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;
Tobias Haamel27b28b32010-02-09 23:09:17 +01001022 }
1023
1024 return false;
1025}
1026
1027bool AaptGroupEntry::getUiModeNightName(const char* name,
1028 ResTable_config* out)
1029{
1030 if (strcmp(name, kWildcardName) == 0) {
1031 if (out) out->uiMode =
1032 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1033 | ResTable_config::UI_MODE_NIGHT_ANY;
1034 return true;
1035 } else if (strcmp(name, "night") == 0) {
1036 if (out) out->uiMode =
1037 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1038 | ResTable_config::UI_MODE_NIGHT_YES;
1039 return true;
1040 } else if (strcmp(name, "notnight") == 0) {
1041 if (out) out->uiMode =
1042 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1043 | ResTable_config::UI_MODE_NIGHT_NO;
1044 return true;
1045 }
1046
1047 return false;
1048}
1049
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050bool AaptGroupEntry::getDensityName(const char* name,
1051 ResTable_config* out)
1052{
1053 if (strcmp(name, kWildcardName) == 0) {
Dianne Hackborna53b8282009-07-17 11:13:48 -07001054 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 return true;
1056 }
Dianne Hackborna53b8282009-07-17 11:13:48 -07001057
1058 if (strcmp(name, "nodpi") == 0) {
1059 if (out) out->density = ResTable_config::DENSITY_NONE;
1060 return true;
1061 }
1062
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001063 if (strcmp(name, "ldpi") == 0) {
1064 if (out) out->density = ResTable_config::DENSITY_LOW;
1065 return true;
1066 }
1067
1068 if (strcmp(name, "mdpi") == 0) {
1069 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1070 return true;
1071 }
1072
Dianne Hackbornb96cbbd2011-05-27 13:40:26 -07001073 if (strcmp(name, "tvdpi") == 0) {
1074 if (out) out->density = ResTable_config::DENSITY_TV;
1075 return true;
1076 }
1077
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001078 if (strcmp(name, "hdpi") == 0) {
1079 if (out) out->density = ResTable_config::DENSITY_HIGH;
1080 return true;
1081 }
1082
Dianne Hackborn588feee2010-06-04 14:36:39 -07001083 if (strcmp(name, "xhdpi") == 0) {
1084 if (out) out->density = ResTable_config::DENSITY_MEDIUM*2;
1085 return true;
1086 }
1087
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 char* c = (char*)name;
1089 while (*c >= '0' && *c <= '9') {
1090 c++;
1091 }
1092
1093 // check that we have 'dpi' after the last digit.
1094 if (toupper(c[0]) != 'D' ||
1095 toupper(c[1]) != 'P' ||
1096 toupper(c[2]) != 'I' ||
1097 c[3] != 0) {
1098 return false;
1099 }
1100
1101 // temporarily replace the first letter with \0 to
1102 // use atoi.
1103 char tmp = c[0];
1104 c[0] = '\0';
1105
1106 int d = atoi(name);
1107 c[0] = tmp;
1108
1109 if (d != 0) {
1110 if (out) out->density = d;
1111 return true;
1112 }
1113
1114 return false;
1115}
1116
1117bool AaptGroupEntry::getTouchscreenName(const char* name,
1118 ResTable_config* out)
1119{
1120 if (strcmp(name, kWildcardName) == 0) {
1121 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1122 return true;
1123 } else if (strcmp(name, "notouch") == 0) {
1124 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1125 return true;
1126 } else if (strcmp(name, "stylus") == 0) {
1127 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1128 return true;
1129 } else if (strcmp(name, "finger") == 0) {
1130 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1131 return true;
1132 }
1133
1134 return false;
1135}
1136
1137bool AaptGroupEntry::getKeysHiddenName(const char* name,
1138 ResTable_config* out)
1139{
1140 uint8_t mask = 0;
1141 uint8_t value = 0;
1142 if (strcmp(name, kWildcardName) == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001143 mask = ResTable_config::MASK_KEYSHIDDEN;
1144 value = ResTable_config::KEYSHIDDEN_ANY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 } else if (strcmp(name, "keysexposed") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001146 mask = ResTable_config::MASK_KEYSHIDDEN;
1147 value = ResTable_config::KEYSHIDDEN_NO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 } else if (strcmp(name, "keyshidden") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001149 mask = ResTable_config::MASK_KEYSHIDDEN;
1150 value = ResTable_config::KEYSHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001151 } else if (strcmp(name, "keyssoft") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001152 mask = ResTable_config::MASK_KEYSHIDDEN;
1153 value = ResTable_config::KEYSHIDDEN_SOFT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001154 }
1155
1156 if (mask != 0) {
1157 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1158 return true;
1159 }
1160
1161 return false;
1162}
1163
1164bool AaptGroupEntry::getKeyboardName(const char* name,
1165 ResTable_config* out)
1166{
1167 if (strcmp(name, kWildcardName) == 0) {
1168 if (out) out->keyboard = out->KEYBOARD_ANY;
1169 return true;
1170 } else if (strcmp(name, "nokeys") == 0) {
1171 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1172 return true;
1173 } else if (strcmp(name, "qwerty") == 0) {
1174 if (out) out->keyboard = out->KEYBOARD_QWERTY;
1175 return true;
1176 } else if (strcmp(name, "12key") == 0) {
1177 if (out) out->keyboard = out->KEYBOARD_12KEY;
1178 return true;
1179 }
1180
1181 return false;
1182}
1183
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001184bool AaptGroupEntry::getNavHiddenName(const char* name,
1185 ResTable_config* out)
1186{
1187 uint8_t mask = 0;
1188 uint8_t value = 0;
1189 if (strcmp(name, kWildcardName) == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001190 mask = ResTable_config::MASK_NAVHIDDEN;
1191 value = ResTable_config::NAVHIDDEN_ANY;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001192 } else if (strcmp(name, "navexposed") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001193 mask = ResTable_config::MASK_NAVHIDDEN;
1194 value = ResTable_config::NAVHIDDEN_NO;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001195 } else if (strcmp(name, "navhidden") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001196 mask = ResTable_config::MASK_NAVHIDDEN;
1197 value = ResTable_config::NAVHIDDEN_YES;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001198 }
1199
1200 if (mask != 0) {
1201 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1202 return true;
1203 }
1204
1205 return false;
1206}
1207
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208bool AaptGroupEntry::getNavigationName(const char* name,
1209 ResTable_config* out)
1210{
1211 if (strcmp(name, kWildcardName) == 0) {
1212 if (out) out->navigation = out->NAVIGATION_ANY;
1213 return true;
1214 } else if (strcmp(name, "nonav") == 0) {
1215 if (out) out->navigation = out->NAVIGATION_NONAV;
1216 return true;
1217 } else if (strcmp(name, "dpad") == 0) {
1218 if (out) out->navigation = out->NAVIGATION_DPAD;
1219 return true;
1220 } else if (strcmp(name, "trackball") == 0) {
1221 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1222 return true;
1223 } else if (strcmp(name, "wheel") == 0) {
1224 if (out) out->navigation = out->NAVIGATION_WHEEL;
1225 return true;
1226 }
1227
1228 return false;
1229}
1230
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001231bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232{
1233 if (strcmp(name, kWildcardName) == 0) {
1234 if (out) {
1235 out->screenWidth = out->SCREENWIDTH_ANY;
1236 out->screenHeight = out->SCREENHEIGHT_ANY;
1237 }
1238 return true;
1239 }
1240
1241 const char* x = name;
1242 while (*x >= '0' && *x <= '9') x++;
1243 if (x == name || *x != 'x') return false;
1244 String8 xName(name, x-name);
1245 x++;
1246
1247 const char* y = x;
1248 while (*y >= '0' && *y <= '9') y++;
1249 if (y == name || *y != 0) return false;
1250 String8 yName(x, y-x);
1251
1252 uint16_t w = (uint16_t)atoi(xName.string());
1253 uint16_t h = (uint16_t)atoi(yName.string());
1254 if (w < h) {
1255 return false;
1256 }
1257
1258 if (out) {
1259 out->screenWidth = w;
1260 out->screenHeight = h;
1261 }
1262
1263 return true;
1264}
1265
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001266bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1267{
1268 if (strcmp(name, kWildcardName) == 0) {
1269 if (out) {
1270 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1271 }
1272 return true;
1273 }
1274
1275 if (*name != 's') return false;
1276 name++;
1277 if (*name != 'w') return false;
1278 name++;
1279 const char* x = name;
1280 while (*x >= '0' && *x <= '9') x++;
1281 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1282 String8 xName(name, x-name);
1283
1284 if (out) {
1285 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1286 }
1287
1288 return true;
1289}
1290
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001291bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1292{
1293 if (strcmp(name, kWildcardName) == 0) {
1294 if (out) {
1295 out->screenWidthDp = out->SCREENWIDTH_ANY;
1296 }
1297 return true;
1298 }
1299
1300 if (*name != 'w') return false;
1301 name++;
1302 const char* x = name;
1303 while (*x >= '0' && *x <= '9') x++;
1304 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1305 String8 xName(name, x-name);
1306
1307 if (out) {
1308 out->screenWidthDp = (uint16_t)atoi(xName.string());
1309 }
1310
1311 return true;
1312}
1313
1314bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1315{
1316 if (strcmp(name, kWildcardName) == 0) {
1317 if (out) {
1318 out->screenHeightDp = out->SCREENWIDTH_ANY;
1319 }
1320 return true;
1321 }
1322
1323 if (*name != 'h') return false;
1324 name++;
1325 const char* x = name;
1326 while (*x >= '0' && *x <= '9') x++;
1327 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1328 String8 xName(name, x-name);
1329
1330 if (out) {
1331 out->screenHeightDp = (uint16_t)atoi(xName.string());
1332 }
1333
1334 return true;
1335}
1336
1337bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001338{
1339 if (strcmp(name, kWildcardName) == 0) {
1340 if (out) {
1341 out->sdkVersion = out->SDKVERSION_ANY;
1342 out->minorVersion = out->MINORVERSION_ANY;
1343 }
1344 return true;
1345 }
1346
1347 if (*name != 'v') {
1348 return false;
1349 }
1350
1351 name++;
1352 const char* s = name;
1353 while (*s >= '0' && *s <= '9') s++;
1354 if (s == name || *s != 0) return false;
1355 String8 sdkName(name, s-name);
1356
1357 if (out) {
1358 out->sdkVersion = (uint16_t)atoi(sdkName.string());
1359 out->minorVersion = 0;
1360 }
1361
1362 return true;
1363}
1364
1365int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1366{
1367 int v = mcc.compare(o.mcc);
1368 if (v == 0) v = mnc.compare(o.mnc);
1369 if (v == 0) v = locale.compare(o.locale);
1370 if (v == 0) v = vendor.compare(o.vendor);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001371 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001372 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1373 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001374 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1375 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 if (v == 0) v = orientation.compare(o.orientation);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001377 if (v == 0) v = uiModeType.compare(o.uiModeType);
1378 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001379 if (v == 0) v = density.compare(o.density);
1380 if (v == 0) v = touchscreen.compare(o.touchscreen);
1381 if (v == 0) v = keysHidden.compare(o.keysHidden);
1382 if (v == 0) v = keyboard.compare(o.keyboard);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001383 if (v == 0) v = navHidden.compare(o.navHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 if (v == 0) v = navigation.compare(o.navigation);
1385 if (v == 0) v = screenSize.compare(o.screenSize);
1386 if (v == 0) v = version.compare(o.version);
1387 return v;
1388}
1389
Dianne Hackborne6b68032011-10-13 16:26:02 -07001390const ResTable_config& AaptGroupEntry::toParams() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391{
Dianne Hackborne6b68032011-10-13 16:26:02 -07001392 if (!mParamsChanged) {
1393 return mParams;
1394 }
1395
1396 mParamsChanged = false;
1397 ResTable_config& params(mParams);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 memset(&params, 0, sizeof(params));
1399 getMccName(mcc.string(), &params);
1400 getMncName(mnc.string(), &params);
1401 getLocaleName(locale.string(), &params);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001402 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001403 getScreenWidthDpName(screenWidthDp.string(), &params);
1404 getScreenHeightDpName(screenHeightDp.string(), &params);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001405 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1406 getScreenLayoutLongName(screenLayoutLong.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 getOrientationName(orientation.string(), &params);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001408 getUiModeTypeName(uiModeType.string(), &params);
1409 getUiModeNightName(uiModeNight.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 getDensityName(density.string(), &params);
1411 getTouchscreenName(touchscreen.string(), &params);
1412 getKeysHiddenName(keysHidden.string(), &params);
1413 getKeyboardName(keyboard.string(), &params);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001414 getNavHiddenName(navHidden.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001415 getNavigationName(navigation.string(), &params);
1416 getScreenSizeName(screenSize.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417 getVersionName(version.string(), &params);
Dianne Hackbornef05e072010-03-01 17:43:39 -08001418
1419 // Fix up version number based on specified parameters.
1420 int minSdk = 0;
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001421 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1422 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001423 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001424 minSdk = SDK_HONEYCOMB_MR2;
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001425 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
Dianne Hackbornef05e072010-03-01 17:43:39 -08001426 != ResTable_config::UI_MODE_TYPE_ANY
1427 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1428 != ResTable_config::UI_MODE_NIGHT_ANY) {
1429 minSdk = SDK_FROYO;
1430 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1431 != ResTable_config::SCREENSIZE_ANY
1432 || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1433 != ResTable_config::SCREENLONG_ANY
1434 || params.density != ResTable_config::DENSITY_DEFAULT) {
1435 minSdk = SDK_DONUT;
1436 }
1437
1438 if (minSdk > params.sdkVersion) {
1439 params.sdkVersion = minSdk;
1440 }
1441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 return params;
1443}
1444
1445// =========================================================================
1446// =========================================================================
1447// =========================================================================
1448
1449void* AaptFile::editData(size_t size)
1450{
1451 if (size <= mBufferSize) {
1452 mDataSize = size;
1453 return mData;
1454 }
1455 size_t allocSize = (size*3)/2;
1456 void* buf = realloc(mData, allocSize);
1457 if (buf == NULL) {
1458 return NULL;
1459 }
1460 mData = buf;
1461 mDataSize = size;
1462 mBufferSize = allocSize;
1463 return buf;
1464}
1465
1466void* AaptFile::editData(size_t* outSize)
1467{
1468 if (outSize) {
1469 *outSize = mDataSize;
1470 }
1471 return mData;
1472}
1473
1474void* AaptFile::padData(size_t wordSize)
1475{
1476 const size_t extra = mDataSize%wordSize;
1477 if (extra == 0) {
1478 return mData;
1479 }
1480
1481 size_t initial = mDataSize;
1482 void* data = editData(initial+(wordSize-extra));
1483 if (data != NULL) {
1484 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1485 }
1486 return data;
1487}
1488
1489status_t AaptFile::writeData(const void* data, size_t size)
1490{
1491 size_t end = mDataSize;
1492 size_t total = size + end;
1493 void* buf = editData(total);
1494 if (buf == NULL) {
1495 return UNKNOWN_ERROR;
1496 }
1497 memcpy(((char*)buf)+end, data, size);
1498 return NO_ERROR;
1499}
1500
1501void AaptFile::clearData()
1502{
1503 if (mData != NULL) free(mData);
1504 mData = NULL;
1505 mDataSize = 0;
1506 mBufferSize = 0;
1507}
1508
1509String8 AaptFile::getPrintableSource() const
1510{
1511 if (hasData()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001512 String8 name(mGroupEntry.toDirName(String8()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 name.appendPath(mPath);
1514 name.append(" #generated");
1515 return name;
1516 }
1517 return mSourceFile;
1518}
1519
1520// =========================================================================
1521// =========================================================================
1522// =========================================================================
1523
1524status_t AaptGroup::addFile(const sp<AaptFile>& file)
1525{
1526 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1527 file->mPath = mPath;
1528 mFiles.add(file->getGroupEntry(), file);
1529 return NO_ERROR;
1530 }
1531
Dianne Hackborne6b68032011-10-13 16:26:02 -07001532#if 0
1533 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1534 file->getSourceFile().string(),
1535 file->getGroupEntry().toDirName(String8()).string(),
1536 mLeaf.string(), mPath.string());
1537#endif
1538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1540 getPrintableSource().string());
1541 return UNKNOWN_ERROR;
1542}
1543
1544void AaptGroup::removeFile(size_t index)
1545{
1546 mFiles.removeItemsAt(index);
1547}
1548
Dianne Hackborne6b68032011-10-13 16:26:02 -07001549void AaptGroup::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550{
Dianne Hackborne6b68032011-10-13 16:26:02 -07001551 printf("%s%s\n", prefix.string(), getPath().string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552 const size_t N=mFiles.size();
1553 size_t i;
1554 for (i=0; i<N; i++) {
1555 sp<AaptFile> file = mFiles.valueAt(i);
1556 const AaptGroupEntry& e = file->getGroupEntry();
1557 if (file->hasData()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001558 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001559 (int)file->getSize());
1560 } else {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001561 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1562 file->getPrintableSource().string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 }
Dianne Hackborne6b68032011-10-13 16:26:02 -07001564 //printf("%s File Group Entry: %s\n", prefix.string(),
1565 // file->getGroupEntry().toDirName(String8()).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001566 }
1567}
1568
1569String8 AaptGroup::getPrintableSource() const
1570{
1571 if (mFiles.size() > 0) {
1572 // Arbitrarily pull the first source file out of the list.
1573 return mFiles.valueAt(0)->getPrintableSource();
1574 }
1575
1576 // Should never hit this case, but to be safe...
1577 return getPath();
1578
1579}
1580
1581// =========================================================================
1582// =========================================================================
1583// =========================================================================
1584
1585status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1586{
1587 if (mFiles.indexOfKey(name) >= 0) {
1588 return ALREADY_EXISTS;
1589 }
1590 mFiles.add(name, file);
1591 return NO_ERROR;
1592}
1593
1594status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1595{
1596 if (mDirs.indexOfKey(name) >= 0) {
1597 return ALREADY_EXISTS;
1598 }
1599 mDirs.add(name, dir);
1600 return NO_ERROR;
1601}
1602
1603sp<AaptDir> AaptDir::makeDir(const String8& path)
1604{
1605 String8 name;
1606 String8 remain = path;
1607
1608 sp<AaptDir> subdir = this;
1609 while (name = remain.walkPath(&remain), remain != "") {
1610 subdir = subdir->makeDir(name);
1611 }
1612
1613 ssize_t i = subdir->mDirs.indexOfKey(name);
1614 if (i >= 0) {
1615 return subdir->mDirs.valueAt(i);
1616 }
1617 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1618 subdir->mDirs.add(name, dir);
1619 return dir;
1620}
1621
1622void AaptDir::removeFile(const String8& name)
1623{
1624 mFiles.removeItem(name);
1625}
1626
1627void AaptDir::removeDir(const String8& name)
1628{
1629 mDirs.removeItem(name);
1630}
1631
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001632status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1633{
1634 sp<AaptGroup> group;
1635 if (mFiles.indexOfKey(leafName) >= 0) {
1636 group = mFiles.valueFor(leafName);
1637 } else {
1638 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1639 mFiles.add(leafName, group);
1640 }
1641
1642 return group->addFile(file);
1643}
1644
1645ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001646 const AaptGroupEntry& kind, const String8& resType,
1647 sp<FilePathStore>& fullResPaths)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648{
1649 Vector<String8> fileNames;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001650 {
1651 DIR* dir = NULL;
1652
1653 dir = opendir(srcDir.string());
1654 if (dir == NULL) {
1655 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1656 return UNKNOWN_ERROR;
1657 }
1658
1659 /*
1660 * Slurp the filenames out of the directory.
1661 */
1662 while (1) {
1663 struct dirent* entry;
1664
1665 entry = readdir(dir);
1666 if (entry == NULL)
1667 break;
1668
1669 if (isHidden(srcDir.string(), entry->d_name))
1670 continue;
1671
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001672 String8 name(entry->d_name);
1673 fileNames.add(name);
1674 // Add fully qualified path for dependency purposes
1675 // if we're collecting them
1676 if (fullResPaths != NULL) {
1677 fullResPaths->add(srcDir.appendPathCopy(name));
1678 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001679 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001680 closedir(dir);
1681 }
1682
1683 ssize_t count = 0;
1684
1685 /*
1686 * Stash away the files and recursively descend into subdirectories.
1687 */
1688 const size_t N = fileNames.size();
1689 size_t i;
1690 for (i = 0; i < N; i++) {
1691 String8 pathName(srcDir);
1692 FileType type;
1693
1694 pathName.appendPath(fileNames[i].string());
1695 type = getFileType(pathName.string());
1696 if (type == kFileTypeDirectory) {
1697 sp<AaptDir> subdir;
1698 bool notAdded = false;
1699 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1700 subdir = mDirs.valueFor(fileNames[i]);
1701 } else {
1702 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1703 notAdded = true;
1704 }
1705 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001706 resType, fullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 if (res < NO_ERROR) {
1708 return res;
1709 }
1710 if (res > 0 && notAdded) {
1711 mDirs.add(fileNames[i], subdir);
1712 }
1713 count += res;
1714 } else if (type == kFileTypeRegular) {
1715 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1716 status_t err = addLeafFile(fileNames[i], file);
1717 if (err != NO_ERROR) {
1718 return err;
1719 }
1720
1721 count++;
1722
1723 } else {
1724 if (bundle->getVerbose())
1725 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
1726 }
1727 }
1728
1729 return count;
1730}
1731
1732status_t AaptDir::validate() const
1733{
1734 const size_t NF = mFiles.size();
1735 const size_t ND = mDirs.size();
1736 size_t i;
1737 for (i = 0; i < NF; i++) {
1738 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1739 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1740 "Invalid filename. Unable to add.");
1741 return UNKNOWN_ERROR;
1742 }
1743
1744 size_t j;
1745 for (j = i+1; j < NF; j++) {
1746 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1747 mFiles.valueAt(j)->getLeaf().string()) == 0) {
1748 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1749 "File is case-insensitive equivalent to: %s",
1750 mFiles.valueAt(j)->getPrintableSource().string());
1751 return UNKNOWN_ERROR;
1752 }
1753
1754 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1755 // (this is mostly caught by the "marked" stuff, below)
1756 }
1757
1758 for (j = 0; j < ND; j++) {
1759 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1760 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1761 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1762 "File conflicts with dir from: %s",
1763 mDirs.valueAt(j)->getPrintableSource().string());
1764 return UNKNOWN_ERROR;
1765 }
1766 }
1767 }
1768
1769 for (i = 0; i < ND; i++) {
1770 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1771 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1772 "Invalid directory name, unable to add.");
1773 return UNKNOWN_ERROR;
1774 }
1775
1776 size_t j;
1777 for (j = i+1; j < ND; j++) {
1778 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1779 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1780 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1781 "Directory is case-insensitive equivalent to: %s",
1782 mDirs.valueAt(j)->getPrintableSource().string());
1783 return UNKNOWN_ERROR;
1784 }
1785 }
1786
1787 status_t err = mDirs.valueAt(i)->validate();
1788 if (err != NO_ERROR) {
1789 return err;
1790 }
1791 }
1792
1793 return NO_ERROR;
1794}
1795
Dianne Hackborne6b68032011-10-13 16:26:02 -07001796void AaptDir::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001797{
1798 const size_t ND=getDirs().size();
1799 size_t i;
1800 for (i=0; i<ND; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001801 getDirs().valueAt(i)->print(prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 }
1803
1804 const size_t NF=getFiles().size();
1805 for (i=0; i<NF; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001806 getFiles().valueAt(i)->print(prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001807 }
1808}
1809
1810String8 AaptDir::getPrintableSource() const
1811{
1812 if (mFiles.size() > 0) {
1813 // Arbitrarily pull the first file out of the list as the source dir.
1814 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1815 }
1816 if (mDirs.size() > 0) {
1817 // Or arbitrarily pull the first dir out of the list as the source dir.
1818 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1819 }
1820
1821 // Should never hit this case, but to be safe...
1822 return mPath;
1823
1824}
1825
1826// =========================================================================
1827// =========================================================================
1828// =========================================================================
1829
Dianne Hackborne6b68032011-10-13 16:26:02 -07001830AaptAssets::AaptAssets()
1831 : AaptDir(String8(), String8()),
1832 mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
1833{
1834}
1835
1836const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
1837 if (mChanged) {
1838 }
1839 return mGroupEntries;
1840}
1841
1842status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
1843{
1844 mChanged = true;
1845 return AaptDir::addFile(name, file);
1846}
1847
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848sp<AaptFile> AaptAssets::addFile(
1849 const String8& filePath, const AaptGroupEntry& entry,
1850 const String8& srcDir, sp<AaptGroup>* outGroup,
1851 const String8& resType)
1852{
1853 sp<AaptDir> dir = this;
1854 sp<AaptGroup> group;
1855 sp<AaptFile> file;
1856 String8 root, remain(filePath), partialPath;
1857 while (remain.length() > 0) {
1858 root = remain.walkPath(&remain);
1859 partialPath.appendPath(root);
1860
1861 const String8 rootStr(root);
1862
1863 if (remain.length() == 0) {
1864 ssize_t i = dir->getFiles().indexOfKey(rootStr);
1865 if (i >= 0) {
1866 group = dir->getFiles().valueAt(i);
1867 } else {
1868 group = new AaptGroup(rootStr, filePath);
1869 status_t res = dir->addFile(rootStr, group);
1870 if (res != NO_ERROR) {
1871 return NULL;
1872 }
1873 }
1874 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1875 status_t res = group->addFile(file);
1876 if (res != NO_ERROR) {
1877 return NULL;
1878 }
1879 break;
1880
1881 } else {
1882 ssize_t i = dir->getDirs().indexOfKey(rootStr);
1883 if (i >= 0) {
1884 dir = dir->getDirs().valueAt(i);
1885 } else {
1886 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1887 status_t res = dir->addDir(rootStr, subdir);
1888 if (res != NO_ERROR) {
1889 return NULL;
1890 }
1891 dir = subdir;
1892 }
1893 }
1894 }
1895
1896 mGroupEntries.add(entry);
1897 if (outGroup) *outGroup = group;
1898 return file;
1899}
1900
1901void AaptAssets::addResource(const String8& leafName, const String8& path,
1902 const sp<AaptFile>& file, const String8& resType)
1903{
1904 sp<AaptDir> res = AaptDir::makeDir(kResString);
1905 String8 dirname = file->getGroupEntry().toDirName(resType);
1906 sp<AaptDir> subdir = res->makeDir(dirname);
1907 sp<AaptGroup> grr = new AaptGroup(leafName, path);
1908 grr->addFile(file);
1909
1910 subdir->addFile(leafName, grr);
1911}
1912
1913
1914ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1915{
1916 int count;
1917 int totalCount = 0;
1918 FileType type;
1919 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1920 const size_t dirCount =resDirs.size();
1921 sp<AaptAssets> current = this;
1922
1923 const int N = bundle->getFileSpecCount();
1924
1925 /*
1926 * If a package manifest was specified, include that first.
1927 */
1928 if (bundle->getAndroidManifestFile() != NULL) {
1929 // place at root of zip.
1930 String8 srcFile(bundle->getAndroidManifestFile());
1931 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1932 NULL, String8());
1933 totalCount++;
1934 }
1935
1936 /*
1937 * If a directory of custom assets was supplied, slurp 'em up.
1938 */
1939 if (bundle->getAssetSourceDir()) {
1940 const char* assetDir = bundle->getAssetSourceDir();
1941
1942 FileType type = getFileType(assetDir);
1943 if (type == kFileTypeNonexistent) {
1944 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1945 return UNKNOWN_ERROR;
1946 }
1947 if (type != kFileTypeDirectory) {
1948 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1949 return UNKNOWN_ERROR;
1950 }
1951
1952 String8 assetRoot(assetDir);
1953 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1954 AaptGroupEntry group;
1955 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Josiah Gaskin03589cc2011-06-27 16:26:02 -07001956 String8(), mFullAssetPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001957 if (count < 0) {
1958 totalCount = count;
1959 goto bail;
1960 }
1961 if (count > 0) {
1962 mGroupEntries.add(group);
1963 }
1964 totalCount += count;
1965
1966 if (bundle->getVerbose())
1967 printf("Found %d custom asset file%s in %s\n",
1968 count, (count==1) ? "" : "s", assetDir);
1969 }
1970
1971 /*
1972 * If a directory of resource-specific assets was supplied, slurp 'em up.
1973 */
1974 for (size_t i=0; i<dirCount; i++) {
1975 const char *res = resDirs[i];
1976 if (res) {
1977 type = getFileType(res);
1978 if (type == kFileTypeNonexistent) {
1979 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1980 return UNKNOWN_ERROR;
1981 }
1982 if (type == kFileTypeDirectory) {
1983 if (i>0) {
1984 sp<AaptAssets> nextOverlay = new AaptAssets();
1985 current->setOverlay(nextOverlay);
1986 current = nextOverlay;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001987 current->setFullResPaths(mFullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001988 }
1989 count = current->slurpResourceTree(bundle, String8(res));
1990
1991 if (count < 0) {
1992 totalCount = count;
1993 goto bail;
1994 }
1995 totalCount += count;
1996 }
1997 else {
1998 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1999 return UNKNOWN_ERROR;
2000 }
2001 }
2002
2003 }
2004 /*
2005 * Now do any additional raw files.
2006 */
2007 for (int arg=0; arg<N; arg++) {
2008 const char* assetDir = bundle->getFileSpecEntry(arg);
2009
2010 FileType type = getFileType(assetDir);
2011 if (type == kFileTypeNonexistent) {
2012 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2013 return UNKNOWN_ERROR;
2014 }
2015 if (type != kFileTypeDirectory) {
2016 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2017 return UNKNOWN_ERROR;
2018 }
2019
2020 String8 assetRoot(assetDir);
2021
2022 if (bundle->getVerbose())
2023 printf("Processing raw dir '%s'\n", (const char*) assetDir);
2024
2025 /*
2026 * Do a recursive traversal of subdir tree. We don't make any
2027 * guarantees about ordering, so we're okay with an inorder search
2028 * using whatever order the OS happens to hand back to us.
2029 */
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002030 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002031 if (count < 0) {
2032 /* failure; report error and remove archive */
2033 totalCount = count;
2034 goto bail;
2035 }
2036 totalCount += count;
2037
2038 if (bundle->getVerbose())
2039 printf("Found %d asset file%s in %s\n",
2040 count, (count==1) ? "" : "s", assetDir);
2041 }
2042
2043 count = validate();
2044 if (count != NO_ERROR) {
2045 totalCount = count;
2046 goto bail;
2047 }
2048
Dianne Hackborne6b68032011-10-13 16:26:02 -07002049 count = filter(bundle);
2050 if (count != NO_ERROR) {
2051 totalCount = count;
2052 goto bail;
2053 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002054
2055bail:
2056 return totalCount;
2057}
2058
2059ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2060 const AaptGroupEntry& kind,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002061 const String8& resType,
2062 sp<FilePathStore>& fullResPaths)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002063{
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002064 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002065 if (res > 0) {
2066 mGroupEntries.add(kind);
2067 }
2068
2069 return res;
2070}
2071
2072ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2073{
2074 ssize_t err = 0;
2075
2076 DIR* dir = opendir(srcDir.string());
2077 if (dir == NULL) {
2078 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2079 return UNKNOWN_ERROR;
2080 }
2081
2082 status_t count = 0;
2083
2084 /*
2085 * Run through the directory, looking for dirs that match the
2086 * expected pattern.
2087 */
2088 while (1) {
2089 struct dirent* entry = readdir(dir);
2090 if (entry == NULL) {
2091 break;
2092 }
2093
2094 if (isHidden(srcDir.string(), entry->d_name)) {
2095 continue;
2096 }
2097
2098 String8 subdirName(srcDir);
2099 subdirName.appendPath(entry->d_name);
2100
2101 AaptGroupEntry group;
2102 String8 resType;
2103 bool b = group.initFromDirName(entry->d_name, &resType);
2104 if (!b) {
2105 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2106 entry->d_name);
2107 err = -1;
2108 continue;
2109 }
2110
Dianne Hackborne6b68032011-10-13 16:26:02 -07002111 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07002112 int maxResInt = atoi(bundle->getMaxResVersion());
Dianne Hackborne6b68032011-10-13 16:26:02 -07002113 const char *verString = group.getVersionString().string();
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07002114 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2115 if (dirVersionInt > maxResInt) {
2116 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2117 continue;
2118 }
2119 }
2120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002121 FileType type = getFileType(subdirName.string());
2122
2123 if (type == kFileTypeDirectory) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002124 sp<AaptDir> dir = makeDir(resType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002125 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002126 resType, mFullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002127 if (res < 0) {
2128 count = res;
2129 goto bail;
2130 }
2131 if (res > 0) {
2132 mGroupEntries.add(group);
2133 count += res;
2134 }
2135
Dianne Hackborne6b68032011-10-13 16:26:02 -07002136 // Only add this directory if we don't already have a resource dir
2137 // for the current type. This ensures that we only add the dir once
2138 // for all configs.
2139 sp<AaptDir> rdir = resDir(resType);
2140 if (rdir == NULL) {
2141 mResDirs.add(dir);
2142 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002143 } else {
2144 if (bundle->getVerbose()) {
2145 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
2146 }
2147 }
2148 }
2149
2150bail:
2151 closedir(dir);
2152 dir = NULL;
2153
2154 if (err != 0) {
2155 return err;
2156 }
2157 return count;
2158}
2159
2160ssize_t
2161AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2162{
2163 int count = 0;
2164 SortedVector<AaptGroupEntry> entries;
2165
2166 ZipFile* zip = new ZipFile;
2167 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2168 if (err != NO_ERROR) {
2169 fprintf(stderr, "error opening zip file %s\n", filename);
2170 count = err;
2171 delete zip;
2172 return -1;
2173 }
2174
2175 const int N = zip->getNumEntries();
2176 for (int i=0; i<N; i++) {
2177 ZipEntry* entry = zip->getEntryByIndex(i);
2178 if (entry->getDeleted()) {
2179 continue;
2180 }
2181
2182 String8 entryName(entry->getFileName());
2183
2184 String8 dirName = entryName.getPathDir();
2185 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2186
2187 String8 resType;
2188 AaptGroupEntry kind;
2189
2190 String8 remain;
2191 if (entryName.walkPath(&remain) == kResourceDir) {
2192 // these are the resources, pull their type out of the directory name
2193 kind.initFromDirName(remain.walkPath().string(), &resType);
2194 } else {
2195 // these are untyped and don't have an AaptGroupEntry
2196 }
2197 if (entries.indexOf(kind) < 0) {
2198 entries.add(kind);
2199 mGroupEntries.add(kind);
2200 }
2201
2202 // use the one from the zip file if they both exist.
2203 dir->removeFile(entryName.getPathLeaf());
2204
2205 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2206 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2207 if (err != NO_ERROR) {
2208 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2209 count = err;
2210 goto bail;
2211 }
2212 file->setCompressionMethod(entry->getCompressionMethod());
2213
2214#if 0
2215 if (entryName == "AndroidManifest.xml") {
2216 printf("AndroidManifest.xml\n");
2217 }
2218 printf("\n\nfile: %s\n", entryName.string());
2219#endif
2220
2221 size_t len = entry->getUncompressedLen();
2222 void* data = zip->uncompress(entry);
2223 void* buf = file->editData(len);
2224 memcpy(buf, data, len);
2225
2226#if 0
2227 const int OFF = 0;
2228 const unsigned char* p = (unsigned char*)data;
2229 const unsigned char* end = p+len;
2230 p += OFF;
2231 for (int i=0; i<32 && p < end; i++) {
2232 printf("0x%03x ", i*0x10 + OFF);
2233 for (int j=0; j<0x10 && p < end; j++) {
2234 printf(" %02x", *p);
2235 p++;
2236 }
2237 printf("\n");
2238 }
2239#endif
2240
2241 free(data);
2242
2243 count++;
2244 }
2245
2246bail:
2247 delete zip;
2248 return count;
2249}
2250
Dianne Hackborne6b68032011-10-13 16:26:02 -07002251status_t AaptAssets::filter(Bundle* bundle)
2252{
2253 ResourceFilter reqFilter;
2254 status_t err = reqFilter.parse(bundle->getConfigurations());
2255 if (err != NO_ERROR) {
2256 return err;
2257 }
2258
2259 ResourceFilter prefFilter;
2260 err = prefFilter.parse(bundle->getPreferredConfigurations());
2261 if (err != NO_ERROR) {
2262 return err;
2263 }
2264
2265 if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2266 return NO_ERROR;
2267 }
2268
Dianne Hackbornbd9d2bc2011-10-16 14:17:07 -07002269 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002270 if (!reqFilter.isEmpty()) {
2271 printf("Applying required filter: %s\n",
2272 bundle->getConfigurations());
2273 }
2274 if (!prefFilter.isEmpty()) {
2275 printf("Applying preferred filter: %s\n",
2276 bundle->getPreferredConfigurations());
2277 }
2278 }
2279
2280 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2281 const size_t ND = resdirs.size();
2282 for (size_t i=0; i<ND; i++) {
2283 const sp<AaptDir>& dir = resdirs.itemAt(i);
2284 if (dir->getLeaf() == kValuesDir) {
2285 // The "value" dir is special since a single file defines
2286 // multiple resources, so we can not do filtering on the
2287 // files themselves.
2288 continue;
2289 }
2290 if (dir->getLeaf() == kMipmapDir) {
2291 // We also skip the "mipmap" directory, since the point of this
2292 // is to include all densities without stripping. If you put
2293 // other configurations in here as well they won't be stripped
2294 // either... So don't do that. Seriously. What is wrong with you?
2295 continue;
2296 }
2297
2298 const size_t NG = dir->getFiles().size();
2299 for (size_t j=0; j<NG; j++) {
2300 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2301
2302 // First remove any configurations we know we don't need.
2303 for (size_t k=0; k<grp->getFiles().size(); k++) {
2304 sp<AaptFile> file = grp->getFiles().valueAt(k);
2305 if (k == 0 && grp->getFiles().size() == 1) {
2306 // If this is the only file left, we need to keep it.
2307 // Otherwise the resource IDs we are using will be inconsistent
2308 // with what we get when not stripping. Sucky, but at least
2309 // for now we can rely on the back-end doing another filtering
2310 // pass to take this out and leave us with this resource name
2311 // containing no entries.
2312 continue;
2313 }
2314 if (file->getPath().getPathExtension() == ".xml") {
2315 // We can't remove .xml files at this point, because when
2316 // we parse them they may add identifier resources, so
2317 // removing them can cause our resource identifiers to
2318 // become inconsistent.
2319 continue;
2320 }
2321 const ResTable_config& config(file->getGroupEntry().toParams());
2322 if (!reqFilter.match(config)) {
2323 if (bundle->getVerbose()) {
2324 printf("Pruning unneeded resource: %s\n",
2325 file->getPrintableSource().string());
2326 }
2327 grp->removeFile(k);
2328 k--;
2329 }
2330 }
2331
2332 // Quick check: no preferred filters, nothing more to do.
2333 if (prefFilter.isEmpty()) {
2334 continue;
2335 }
2336
2337 // Now deal with preferred configurations.
2338 for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2339 for (size_t k=0; k<grp->getFiles().size(); k++) {
2340 sp<AaptFile> file = grp->getFiles().valueAt(k);
2341 if (k == 0 && grp->getFiles().size() == 1) {
2342 // If this is the only file left, we need to keep it.
2343 // Otherwise the resource IDs we are using will be inconsistent
2344 // with what we get when not stripping. Sucky, but at least
2345 // for now we can rely on the back-end doing another filtering
2346 // pass to take this out and leave us with this resource name
2347 // containing no entries.
2348 continue;
2349 }
2350 if (file->getPath().getPathExtension() == ".xml") {
2351 // We can't remove .xml files at this point, because when
2352 // we parse them they may add identifier resources, so
2353 // removing them can cause our resource identifiers to
2354 // become inconsistent.
2355 continue;
2356 }
2357 const ResTable_config& config(file->getGroupEntry().toParams());
2358 if (!prefFilter.match(axis, config)) {
2359 // This is a resource we would prefer not to have. Check
2360 // to see if have a similar variation that we would like
2361 // to have and, if so, we can drop it.
2362 for (size_t m=0; m<grp->getFiles().size(); m++) {
2363 if (m == k) continue;
2364 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2365 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2366 if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2367 if (prefFilter.match(axis, mconfig)) {
2368 if (bundle->getVerbose()) {
2369 printf("Pruning unneeded resource: %s\n",
2370 file->getPrintableSource().string());
2371 }
2372 grp->removeFile(k);
2373 k--;
2374 break;
2375 }
2376 }
2377 }
2378 }
2379 }
2380 }
2381 }
2382 }
2383
2384 return NO_ERROR;
2385}
2386
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002387sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2388{
2389 sp<AaptSymbols> sym = mSymbols.valueFor(name);
2390 if (sym == NULL) {
2391 sym = new AaptSymbols();
2392 mSymbols.add(name, sym);
2393 }
2394 return sym;
2395}
2396
2397status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2398{
2399 if (!mHaveIncludedAssets) {
2400 // Add in all includes.
2401 const Vector<const char*>& incl = bundle->getPackageIncludes();
2402 const size_t N=incl.size();
2403 for (size_t i=0; i<N; i++) {
2404 if (bundle->getVerbose())
2405 printf("Including resources from package: %s\n", incl[i]);
2406 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2407 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2408 incl[i]);
2409 return UNKNOWN_ERROR;
2410 }
2411 }
2412 mHaveIncludedAssets = true;
2413 }
2414
2415 return NO_ERROR;
2416}
2417
2418status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2419{
2420 const ResTable& res = getIncludedResources();
2421 // XXX dirty!
2422 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2423}
2424
2425const ResTable& AaptAssets::getIncludedResources() const
2426{
2427 return mIncludedAssets.getResources(false);
2428}
2429
Dianne Hackborne6b68032011-10-13 16:26:02 -07002430void AaptAssets::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002431{
Dianne Hackborne6b68032011-10-13 16:26:02 -07002432 String8 innerPrefix(prefix);
2433 innerPrefix.append(" ");
2434 String8 innerInnerPrefix(innerPrefix);
2435 innerInnerPrefix.append(" ");
2436 printf("%sConfigurations:\n", prefix.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002437 const size_t N=mGroupEntries.size();
2438 for (size_t i=0; i<N; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002439 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2440 printf("%s %s\n", prefix.string(),
2441 cname != "" ? cname.string() : "(default)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002442 }
2443
Dianne Hackborne6b68032011-10-13 16:26:02 -07002444 printf("\n%sFiles:\n", prefix.string());
2445 AaptDir::print(innerPrefix);
2446
2447 printf("\n%sResource Dirs:\n", prefix.string());
2448 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2449 const size_t NR = resdirs.size();
2450 for (size_t i=0; i<NR; i++) {
2451 const sp<AaptDir>& d = resdirs.itemAt(i);
2452 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
2453 d->print(innerInnerPrefix);
2454 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002455}
2456
Dianne Hackborne6b68032011-10-13 16:26:02 -07002457sp<AaptDir> AaptAssets::resDir(const String8& name) const
Joe Onorato1553c822009-08-30 13:36:22 -07002458{
Dianne Hackborne6b68032011-10-13 16:26:02 -07002459 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2460 const size_t N = resdirs.size();
Joe Onorato1553c822009-08-30 13:36:22 -07002461 for (size_t i=0; i<N; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002462 const sp<AaptDir>& d = resdirs.itemAt(i);
Joe Onorato1553c822009-08-30 13:36:22 -07002463 if (d->getLeaf() == name) {
2464 return d;
2465 }
2466 }
2467 return NULL;
2468}
2469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002470bool
2471valid_symbol_name(const String8& symbol)
2472{
2473 static char const * const KEYWORDS[] = {
2474 "abstract", "assert", "boolean", "break",
2475 "byte", "case", "catch", "char", "class", "const", "continue",
2476 "default", "do", "double", "else", "enum", "extends", "final",
2477 "finally", "float", "for", "goto", "if", "implements", "import",
2478 "instanceof", "int", "interface", "long", "native", "new", "package",
2479 "private", "protected", "public", "return", "short", "static",
2480 "strictfp", "super", "switch", "synchronized", "this", "throw",
2481 "throws", "transient", "try", "void", "volatile", "while",
2482 "true", "false", "null",
2483 NULL
2484 };
2485 const char*const* k = KEYWORDS;
2486 const char*const s = symbol.string();
2487 while (*k) {
2488 if (0 == strcmp(s, *k)) {
2489 return false;
2490 }
2491 k++;
2492 }
2493 return true;
2494}