2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <androidfw/ResourceTypes.h>
20 #include "AaptConfig.h"
21 #include "AaptAssets.h"
23 #include "ResourceFilter.h"
24 #include "SdkConstants.h"
26 using android::String8;
27 using android::Vector;
28 using android::ResTable_config;
30 namespace AaptConfig {
32 static const char* kWildcardName = "any";
34 bool parse(const String8& str, ConfigDescription* out) {
35 Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-');
37 ConfigDescription config;
38 AaptLocaleValue locale;
40 ssize_t localeIndex = 0;
41 const ssize_t N = parts.size();
42 const char* part = parts[index].string();
44 if (str.length() == 0) {
48 if (parseMcc(part, &config)) {
53 part = parts[index].string();
56 if (parseMnc(part, &config)) {
61 part = parts[index].string();
64 // Locale spans a few '-' separators, so we let it
66 localeIndex = locale.initFromDirName(parts, index);
67 if (localeIndex < 0) {
69 } else if (localeIndex > index) {
70 locale.writeTo(&config);
75 part = parts[index].string();
78 if (parseLayoutDirection(part, &config)) {
83 part = parts[index].string();
86 if (parseSmallestScreenWidthDp(part, &config)) {
91 part = parts[index].string();
94 if (parseScreenWidthDp(part, &config)) {
99 part = parts[index].string();
102 if (parseScreenHeightDp(part, &config)) {
107 part = parts[index].string();
110 if (parseScreenLayoutSize(part, &config)) {
115 part = parts[index].string();
118 if (parseScreenLayoutLong(part, &config)) {
123 part = parts[index].string();
126 if (parseOrientation(part, &config)) {
131 part = parts[index].string();
134 if (parseUiModeType(part, &config)) {
139 part = parts[index].string();
142 if (parseUiModeNight(part, &config)) {
147 part = parts[index].string();
150 if (parseDensity(part, &config)) {
155 part = parts[index].string();
158 if (parseTouchscreen(part, &config)) {
163 part = parts[index].string();
166 if (parseKeysHidden(part, &config)) {
171 part = parts[index].string();
174 if (parseKeyboard(part, &config)) {
179 part = parts[index].string();
182 if (parseNavHidden(part, &config)) {
187 part = parts[index].string();
190 if (parseNavigation(part, &config)) {
195 part = parts[index].string();
198 if (parseScreenSize(part, &config)) {
203 part = parts[index].string();
206 if (parseVersion(part, &config)) {
211 part = parts[index].string();
219 applyVersionForCompatibility(&config);
225 bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) {
226 Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ',');
227 const size_t N = parts.size();
228 for (size_t i = 0; i < N; i++) {
229 ConfigDescription config;
230 if (!parse(parts[i], &config)) {
233 outSet->insert(config);
238 void applyVersionForCompatibility(ConfigDescription* config) {
239 if (config == NULL) {
244 if (config->density == ResTable_config::DENSITY_ANY) {
245 minSdk = SDK_LOLLIPOP;
246 } else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
247 || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
248 || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
249 minSdk = SDK_HONEYCOMB_MR2;
250 } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
251 != ResTable_config::UI_MODE_TYPE_ANY
252 || (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
253 != ResTable_config::UI_MODE_NIGHT_ANY) {
255 } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
256 != ResTable_config::SCREENSIZE_ANY
257 || (config->screenLayout & ResTable_config::MASK_SCREENLONG)
258 != ResTable_config::SCREENLONG_ANY
259 || config->density != ResTable_config::DENSITY_DEFAULT) {
263 if (minSdk > config->sdkVersion) {
264 config->sdkVersion = minSdk;
268 bool parseMcc(const char* name, ResTable_config* out) {
269 if (strcmp(name, kWildcardName) == 0) {
270 if (out) out->mcc = 0;
273 const char* c = name;
274 if (tolower(*c) != 'm') return false;
276 if (tolower(*c) != 'c') return false;
278 if (tolower(*c) != 'c') return false;
283 while (*c >= '0' && *c <= '9') {
286 if (*c != 0) return false;
287 if (c-val != 3) return false;
291 if (out) out->mcc = d;
298 bool parseMnc(const char* name, ResTable_config* out) {
299 if (strcmp(name, kWildcardName) == 0) {
300 if (out) out->mcc = 0;
303 const char* c = name;
304 if (tolower(*c) != 'm') return false;
306 if (tolower(*c) != 'n') return false;
308 if (tolower(*c) != 'c') return false;
313 while (*c >= '0' && *c <= '9') {
316 if (*c != 0) return false;
317 if (c-val == 0 || c-val > 3) return false;
320 out->mnc = atoi(val);
322 out->mnc = ACONFIGURATION_MNC_ZERO;
329 bool parseLayoutDirection(const char* name, ResTable_config* out) {
330 if (strcmp(name, kWildcardName) == 0) {
331 if (out) out->screenLayout =
332 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
333 | ResTable_config::LAYOUTDIR_ANY;
335 } else if (strcmp(name, "ldltr") == 0) {
336 if (out) out->screenLayout =
337 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
338 | ResTable_config::LAYOUTDIR_LTR;
340 } else if (strcmp(name, "ldrtl") == 0) {
341 if (out) out->screenLayout =
342 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
343 | ResTable_config::LAYOUTDIR_RTL;
350 bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
351 if (strcmp(name, kWildcardName) == 0) {
352 if (out) out->screenLayout =
353 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
354 | ResTable_config::SCREENSIZE_ANY;
356 } else if (strcmp(name, "small") == 0) {
357 if (out) out->screenLayout =
358 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
359 | ResTable_config::SCREENSIZE_SMALL;
361 } else if (strcmp(name, "normal") == 0) {
362 if (out) out->screenLayout =
363 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
364 | ResTable_config::SCREENSIZE_NORMAL;
366 } else if (strcmp(name, "large") == 0) {
367 if (out) out->screenLayout =
368 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
369 | ResTable_config::SCREENSIZE_LARGE;
371 } else if (strcmp(name, "xlarge") == 0) {
372 if (out) out->screenLayout =
373 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
374 | ResTable_config::SCREENSIZE_XLARGE;
381 bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
382 if (strcmp(name, kWildcardName) == 0) {
383 if (out) out->screenLayout =
384 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
385 | ResTable_config::SCREENLONG_ANY;
387 } else if (strcmp(name, "long") == 0) {
388 if (out) out->screenLayout =
389 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
390 | ResTable_config::SCREENLONG_YES;
392 } else if (strcmp(name, "notlong") == 0) {
393 if (out) out->screenLayout =
394 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
395 | ResTable_config::SCREENLONG_NO;
402 bool parseOrientation(const char* name, ResTable_config* out) {
403 if (strcmp(name, kWildcardName) == 0) {
404 if (out) out->orientation = out->ORIENTATION_ANY;
406 } else if (strcmp(name, "port") == 0) {
407 if (out) out->orientation = out->ORIENTATION_PORT;
409 } else if (strcmp(name, "land") == 0) {
410 if (out) out->orientation = out->ORIENTATION_LAND;
412 } else if (strcmp(name, "square") == 0) {
413 if (out) out->orientation = out->ORIENTATION_SQUARE;
420 bool parseUiModeType(const char* name, ResTable_config* out) {
421 if (strcmp(name, kWildcardName) == 0) {
422 if (out) out->uiMode =
423 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
424 | ResTable_config::UI_MODE_TYPE_ANY;
426 } else if (strcmp(name, "desk") == 0) {
427 if (out) out->uiMode =
428 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
429 | ResTable_config::UI_MODE_TYPE_DESK;
431 } else if (strcmp(name, "car") == 0) {
432 if (out) out->uiMode =
433 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
434 | ResTable_config::UI_MODE_TYPE_CAR;
436 } else if (strcmp(name, "television") == 0) {
437 if (out) out->uiMode =
438 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
439 | ResTable_config::UI_MODE_TYPE_TELEVISION;
441 } else if (strcmp(name, "appliance") == 0) {
442 if (out) out->uiMode =
443 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
444 | ResTable_config::UI_MODE_TYPE_APPLIANCE;
446 } else if (strcmp(name, "watch") == 0) {
447 if (out) out->uiMode =
448 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
449 | ResTable_config::UI_MODE_TYPE_WATCH;
456 bool parseUiModeNight(const char* name, ResTable_config* out) {
457 if (strcmp(name, kWildcardName) == 0) {
458 if (out) out->uiMode =
459 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
460 | ResTable_config::UI_MODE_NIGHT_ANY;
462 } else if (strcmp(name, "night") == 0) {
463 if (out) out->uiMode =
464 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
465 | ResTable_config::UI_MODE_NIGHT_YES;
467 } else if (strcmp(name, "notnight") == 0) {
468 if (out) out->uiMode =
469 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
470 | ResTable_config::UI_MODE_NIGHT_NO;
477 bool parseDensity(const char* name, ResTable_config* out) {
478 if (strcmp(name, kWildcardName) == 0) {
479 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
483 if (strcmp(name, "anydpi") == 0) {
484 if (out) out->density = ResTable_config::DENSITY_ANY;
488 if (strcmp(name, "nodpi") == 0) {
489 if (out) out->density = ResTable_config::DENSITY_NONE;
493 if (strcmp(name, "ldpi") == 0) {
494 if (out) out->density = ResTable_config::DENSITY_LOW;
498 if (strcmp(name, "mdpi") == 0) {
499 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
503 if (strcmp(name, "tvdpi") == 0) {
504 if (out) out->density = ResTable_config::DENSITY_TV;
508 if (strcmp(name, "hdpi") == 0) {
509 if (out) out->density = ResTable_config::DENSITY_HIGH;
513 if (strcmp(name, "xhdpi") == 0) {
514 if (out) out->density = ResTable_config::DENSITY_XHIGH;
518 if (strcmp(name, "xxhdpi") == 0) {
519 if (out) out->density = ResTable_config::DENSITY_XXHIGH;
523 if (strcmp(name, "xxxhdpi") == 0) {
524 if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
528 char* c = (char*)name;
529 while (*c >= '0' && *c <= '9') {
533 // check that we have 'dpi' after the last digit.
534 if (toupper(c[0]) != 'D' ||
535 toupper(c[1]) != 'P' ||
536 toupper(c[2]) != 'I' ||
541 // temporarily replace the first letter with \0 to
550 if (out) out->density = d;
557 bool parseTouchscreen(const char* name, ResTable_config* out) {
558 if (strcmp(name, kWildcardName) == 0) {
559 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
561 } else if (strcmp(name, "notouch") == 0) {
562 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
564 } else if (strcmp(name, "stylus") == 0) {
565 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
567 } else if (strcmp(name, "finger") == 0) {
568 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
575 bool parseKeysHidden(const char* name, ResTable_config* out) {
578 if (strcmp(name, kWildcardName) == 0) {
579 mask = ResTable_config::MASK_KEYSHIDDEN;
580 value = ResTable_config::KEYSHIDDEN_ANY;
581 } else if (strcmp(name, "keysexposed") == 0) {
582 mask = ResTable_config::MASK_KEYSHIDDEN;
583 value = ResTable_config::KEYSHIDDEN_NO;
584 } else if (strcmp(name, "keyshidden") == 0) {
585 mask = ResTable_config::MASK_KEYSHIDDEN;
586 value = ResTable_config::KEYSHIDDEN_YES;
587 } else if (strcmp(name, "keyssoft") == 0) {
588 mask = ResTable_config::MASK_KEYSHIDDEN;
589 value = ResTable_config::KEYSHIDDEN_SOFT;
593 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
600 bool parseKeyboard(const char* name, ResTable_config* out) {
601 if (strcmp(name, kWildcardName) == 0) {
602 if (out) out->keyboard = out->KEYBOARD_ANY;
604 } else if (strcmp(name, "nokeys") == 0) {
605 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
607 } else if (strcmp(name, "qwerty") == 0) {
608 if (out) out->keyboard = out->KEYBOARD_QWERTY;
610 } else if (strcmp(name, "12key") == 0) {
611 if (out) out->keyboard = out->KEYBOARD_12KEY;
618 bool parseNavHidden(const char* name, ResTable_config* out) {
621 if (strcmp(name, kWildcardName) == 0) {
622 mask = ResTable_config::MASK_NAVHIDDEN;
623 value = ResTable_config::NAVHIDDEN_ANY;
624 } else if (strcmp(name, "navexposed") == 0) {
625 mask = ResTable_config::MASK_NAVHIDDEN;
626 value = ResTable_config::NAVHIDDEN_NO;
627 } else if (strcmp(name, "navhidden") == 0) {
628 mask = ResTable_config::MASK_NAVHIDDEN;
629 value = ResTable_config::NAVHIDDEN_YES;
633 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
640 bool parseNavigation(const char* name, ResTable_config* out) {
641 if (strcmp(name, kWildcardName) == 0) {
642 if (out) out->navigation = out->NAVIGATION_ANY;
644 } else if (strcmp(name, "nonav") == 0) {
645 if (out) out->navigation = out->NAVIGATION_NONAV;
647 } else if (strcmp(name, "dpad") == 0) {
648 if (out) out->navigation = out->NAVIGATION_DPAD;
650 } else if (strcmp(name, "trackball") == 0) {
651 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
653 } else if (strcmp(name, "wheel") == 0) {
654 if (out) out->navigation = out->NAVIGATION_WHEEL;
661 bool parseScreenSize(const char* name, ResTable_config* out) {
662 if (strcmp(name, kWildcardName) == 0) {
664 out->screenWidth = out->SCREENWIDTH_ANY;
665 out->screenHeight = out->SCREENHEIGHT_ANY;
670 const char* x = name;
671 while (*x >= '0' && *x <= '9') x++;
672 if (x == name || *x != 'x') return false;
673 String8 xName(name, x-name);
677 while (*y >= '0' && *y <= '9') y++;
678 if (y == name || *y != 0) return false;
679 String8 yName(x, y-x);
681 uint16_t w = (uint16_t)atoi(xName.string());
682 uint16_t h = (uint16_t)atoi(yName.string());
688 out->screenWidth = w;
689 out->screenHeight = h;
695 bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
696 if (strcmp(name, kWildcardName) == 0) {
698 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
703 if (*name != 's') return false;
705 if (*name != 'w') return false;
707 const char* x = name;
708 while (*x >= '0' && *x <= '9') x++;
709 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
710 String8 xName(name, x-name);
713 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
719 bool parseScreenWidthDp(const char* name, ResTable_config* out) {
720 if (strcmp(name, kWildcardName) == 0) {
722 out->screenWidthDp = out->SCREENWIDTH_ANY;
727 if (*name != 'w') return false;
729 const char* x = name;
730 while (*x >= '0' && *x <= '9') x++;
731 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
732 String8 xName(name, x-name);
735 out->screenWidthDp = (uint16_t)atoi(xName.string());
741 bool parseScreenHeightDp(const char* name, ResTable_config* out) {
742 if (strcmp(name, kWildcardName) == 0) {
744 out->screenHeightDp = out->SCREENWIDTH_ANY;
749 if (*name != 'h') return false;
751 const char* x = name;
752 while (*x >= '0' && *x <= '9') x++;
753 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
754 String8 xName(name, x-name);
757 out->screenHeightDp = (uint16_t)atoi(xName.string());
763 bool parseVersion(const char* name, ResTable_config* out) {
764 if (strcmp(name, kWildcardName) == 0) {
766 out->sdkVersion = out->SDKVERSION_ANY;
767 out->minorVersion = out->MINORVERSION_ANY;
777 const char* s = name;
778 while (*s >= '0' && *s <= '9') s++;
779 if (s == name || *s != 0) return false;
780 String8 sdkName(name, s-name);
783 out->sdkVersion = (uint16_t)atoi(sdkName.string());
784 out->minorVersion = 0;
790 String8 getVersion(const ResTable_config& config) {
791 return String8::format("v%u", config.sdkVersion);
794 bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
795 return a.diff(b) == axisMask;
798 bool isDensityOnly(const ResTable_config& config) {
799 if (config.density == ResTable_config::DENSITY_DEFAULT) {
803 if (config.density == ResTable_config::DENSITY_ANY) {
804 if (config.sdkVersion != SDK_LOLLIPOP) {
805 // Someone modified the sdkVersion from the default, this is not safe to assume.
808 } else if (config.sdkVersion != SDK_DONUT) {
812 const uint32_t mask = ResTable_config::CONFIG_DENSITY | ResTable_config::CONFIG_VERSION;
813 const ConfigDescription nullConfig;
814 return (nullConfig.diff(config) & ~mask) == 0;
817 } // namespace AaptConfig