blob: 69a9c7feca992c58bcfe629dc0bcf898603faf57 [file] [log] [blame]
Adam Lesinskifab50872014-04-16 14:40:42 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include <androidfw/ResourceTypes.h>
18#include <ctype.h>
19
20#include "AaptConfig.h"
21#include "AaptAssets.h"
22#include "AaptUtil.h"
23#include "ResourceFilter.h"
24
25using android::String8;
26using android::Vector;
27using android::ResTable_config;
28
29namespace AaptConfig {
30
31static const char* kWildcardName = "any";
32
33bool parse(const String8& str, ConfigDescription* out) {
34 Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-');
35
36 ConfigDescription config;
37 AaptLocaleValue locale;
38 ssize_t index = 0;
39 ssize_t localeIndex = 0;
40 const ssize_t N = parts.size();
41 const char* part = parts[index].string();
42
43 if (str.length() == 0) {
44 goto success;
45 }
46
47 if (parseMcc(part, &config)) {
48 index++;
49 if (index == N) {
50 goto success;
51 }
52 part = parts[index].string();
53 }
54
55 if (parseMnc(part, &config)) {
56 index++;
57 if (index == N) {
58 goto success;
59 }
60 part = parts[index].string();
61 }
62
63 // Locale spans a few '-' separators, so we let it
64 // control the index.
65 localeIndex = locale.initFromDirName(parts, index);
66 if (localeIndex < 0) {
67 return false;
68 } else if (localeIndex > index) {
69 locale.writeTo(&config);
70 index = localeIndex;
71 if (index >= N) {
72 goto success;
73 }
74 part = parts[index].string();
75 }
76
77 if (parseLayoutDirection(part, &config)) {
78 index++;
79 if (index == N) {
80 goto success;
81 }
82 part = parts[index].string();
83 }
84
85 if (parseSmallestScreenWidthDp(part, &config)) {
86 index++;
87 if (index == N) {
88 goto success;
89 }
90 part = parts[index].string();
91 }
92
93 if (parseScreenWidthDp(part, &config)) {
94 index++;
95 if (index == N) {
96 goto success;
97 }
98 part = parts[index].string();
99 }
100
101 if (parseScreenHeightDp(part, &config)) {
102 index++;
103 if (index == N) {
104 goto success;
105 }
106 part = parts[index].string();
107 }
108
109 if (parseScreenLayoutSize(part, &config)) {
110 index++;
111 if (index == N) {
112 goto success;
113 }
114 part = parts[index].string();
115 }
116
117 if (parseScreenLayoutLong(part, &config)) {
118 index++;
119 if (index == N) {
120 goto success;
121 }
122 part = parts[index].string();
123 }
124
125 if (parseOrientation(part, &config)) {
126 index++;
127 if (index == N) {
128 goto success;
129 }
130 part = parts[index].string();
131 }
132
133 if (parseUiModeType(part, &config)) {
134 index++;
135 if (index == N) {
136 goto success;
137 }
138 part = parts[index].string();
139 }
140
141 if (parseUiModeNight(part, &config)) {
142 index++;
143 if (index == N) {
144 goto success;
145 }
146 part = parts[index].string();
147 }
148
149 if (parseDensity(part, &config)) {
150 index++;
151 if (index == N) {
152 goto success;
153 }
154 part = parts[index].string();
155 }
156
157 if (parseTouchscreen(part, &config)) {
158 index++;
159 if (index == N) {
160 goto success;
161 }
162 part = parts[index].string();
163 }
164
165 if (parseKeysHidden(part, &config)) {
166 index++;
167 if (index == N) {
168 goto success;
169 }
170 part = parts[index].string();
171 }
172
173 if (parseKeyboard(part, &config)) {
174 index++;
175 if (index == N) {
176 goto success;
177 }
178 part = parts[index].string();
179 }
180
181 if (parseNavHidden(part, &config)) {
182 index++;
183 if (index == N) {
184 goto success;
185 }
186 part = parts[index].string();
187 }
188
189 if (parseNavigation(part, &config)) {
190 index++;
191 if (index == N) {
192 goto success;
193 }
194 part = parts[index].string();
195 }
196
197 if (parseScreenSize(part, &config)) {
198 index++;
199 if (index == N) {
200 goto success;
201 }
202 part = parts[index].string();
203 }
204
205 if (parseVersion(part, &config)) {
206 index++;
207 if (index == N) {
208 goto success;
209 }
210 part = parts[index].string();
211 }
212
213 // Unrecognized.
214 return false;
215
216success:
217 if (out != NULL) {
218 applyVersionForCompatibility(&config);
219 *out = config;
220 }
221 return true;
222}
223
224bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) {
225 Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ',');
226 const size_t N = parts.size();
227 for (size_t i = 0; i < N; i++) {
228 ConfigDescription config;
229 if (!parse(parts[i], &config)) {
230 return false;
231 }
232 outSet->insert(config);
233 }
234 return true;
235}
236
237void applyVersionForCompatibility(ConfigDescription* config) {
238 if (config == NULL) {
239 return;
240 }
241
242 uint16_t minSdk = 0;
243 if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
244 || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
245 || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
246 minSdk = SDK_HONEYCOMB_MR2;
247 } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
248 != ResTable_config::UI_MODE_TYPE_ANY
249 || (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
250 != ResTable_config::UI_MODE_NIGHT_ANY) {
251 minSdk = SDK_FROYO;
252 } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
253 != ResTable_config::SCREENSIZE_ANY
254 || (config->screenLayout & ResTable_config::MASK_SCREENLONG)
255 != ResTable_config::SCREENLONG_ANY
256 || config->density != ResTable_config::DENSITY_DEFAULT) {
257 minSdk = SDK_DONUT;
258 }
259
260 if (minSdk > config->sdkVersion) {
261 config->sdkVersion = minSdk;
262 }
263}
264
265bool parseMcc(const char* name, ResTable_config* out) {
266 if (strcmp(name, kWildcardName) == 0) {
267 if (out) out->mcc = 0;
268 return true;
269 }
270 const char* c = name;
271 if (tolower(*c) != 'm') return false;
272 c++;
273 if (tolower(*c) != 'c') return false;
274 c++;
275 if (tolower(*c) != 'c') return false;
276 c++;
277
278 const char* val = c;
279
280 while (*c >= '0' && *c <= '9') {
281 c++;
282 }
283 if (*c != 0) return false;
284 if (c-val != 3) return false;
285
286 int d = atoi(val);
287 if (d != 0) {
288 if (out) out->mcc = d;
289 return true;
290 }
291
292 return false;
293}
294
295bool parseMnc(const char* name, ResTable_config* out) {
296 if (strcmp(name, kWildcardName) == 0) {
297 if (out) out->mcc = 0;
298 return true;
299 }
300 const char* c = name;
301 if (tolower(*c) != 'm') return false;
302 c++;
303 if (tolower(*c) != 'n') return false;
304 c++;
305 if (tolower(*c) != 'c') return false;
306 c++;
307
308 const char* val = c;
309
310 while (*c >= '0' && *c <= '9') {
311 c++;
312 }
313 if (*c != 0) return false;
314 if (c-val == 0 || c-val > 3) return false;
315
316 if (out) {
317 out->mnc = atoi(val);
318 if (out->mnc == 0) {
319 out->mnc = ACONFIGURATION_MNC_ZERO;
320 }
321 }
322
323 return true;
324}
325
326bool parseLayoutDirection(const char* name, ResTable_config* out) {
327 if (strcmp(name, kWildcardName) == 0) {
328 if (out) out->screenLayout =
329 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
330 | ResTable_config::LAYOUTDIR_ANY;
331 return true;
332 } else if (strcmp(name, "ldltr") == 0) {
333 if (out) out->screenLayout =
334 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
335 | ResTable_config::LAYOUTDIR_LTR;
336 return true;
337 } else if (strcmp(name, "ldrtl") == 0) {
338 if (out) out->screenLayout =
339 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
340 | ResTable_config::LAYOUTDIR_RTL;
341 return true;
342 }
343
344 return false;
345}
346
347bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
348 if (strcmp(name, kWildcardName) == 0) {
349 if (out) out->screenLayout =
350 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
351 | ResTable_config::SCREENSIZE_ANY;
352 return true;
353 } else if (strcmp(name, "small") == 0) {
354 if (out) out->screenLayout =
355 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
356 | ResTable_config::SCREENSIZE_SMALL;
357 return true;
358 } else if (strcmp(name, "normal") == 0) {
359 if (out) out->screenLayout =
360 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
361 | ResTable_config::SCREENSIZE_NORMAL;
362 return true;
363 } else if (strcmp(name, "large") == 0) {
364 if (out) out->screenLayout =
365 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
366 | ResTable_config::SCREENSIZE_LARGE;
367 return true;
368 } else if (strcmp(name, "xlarge") == 0) {
369 if (out) out->screenLayout =
370 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
371 | ResTable_config::SCREENSIZE_XLARGE;
372 return true;
373 }
374
375 return false;
376}
377
378bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
379 if (strcmp(name, kWildcardName) == 0) {
380 if (out) out->screenLayout =
381 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
382 | ResTable_config::SCREENLONG_ANY;
383 return true;
384 } else if (strcmp(name, "long") == 0) {
385 if (out) out->screenLayout =
386 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
387 | ResTable_config::SCREENLONG_YES;
388 return true;
389 } else if (strcmp(name, "notlong") == 0) {
390 if (out) out->screenLayout =
391 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
392 | ResTable_config::SCREENLONG_NO;
393 return true;
394 }
395
396 return false;
397}
398
399bool parseOrientation(const char* name, ResTable_config* out) {
400 if (strcmp(name, kWildcardName) == 0) {
401 if (out) out->orientation = out->ORIENTATION_ANY;
402 return true;
403 } else if (strcmp(name, "port") == 0) {
404 if (out) out->orientation = out->ORIENTATION_PORT;
405 return true;
406 } else if (strcmp(name, "land") == 0) {
407 if (out) out->orientation = out->ORIENTATION_LAND;
408 return true;
409 } else if (strcmp(name, "square") == 0) {
410 if (out) out->orientation = out->ORIENTATION_SQUARE;
411 return true;
412 }
413
414 return false;
415}
416
417bool parseUiModeType(const char* name, ResTable_config* out) {
418 if (strcmp(name, kWildcardName) == 0) {
419 if (out) out->uiMode =
420 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
421 | ResTable_config::UI_MODE_TYPE_ANY;
422 return true;
423 } else if (strcmp(name, "desk") == 0) {
424 if (out) out->uiMode =
425 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
426 | ResTable_config::UI_MODE_TYPE_DESK;
427 return true;
428 } else if (strcmp(name, "car") == 0) {
429 if (out) out->uiMode =
430 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
431 | ResTable_config::UI_MODE_TYPE_CAR;
432 return true;
433 } else if (strcmp(name, "television") == 0) {
434 if (out) out->uiMode =
435 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
436 | ResTable_config::UI_MODE_TYPE_TELEVISION;
437 return true;
438 } else if (strcmp(name, "appliance") == 0) {
439 if (out) out->uiMode =
440 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
441 | ResTable_config::UI_MODE_TYPE_APPLIANCE;
442 return true;
443 } else if (strcmp(name, "watch") == 0) {
444 if (out) out->uiMode =
445 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
446 | ResTable_config::UI_MODE_TYPE_WATCH;
447 return true;
448 }
449
450 return false;
451}
452
453bool parseUiModeNight(const char* name, ResTable_config* out) {
454 if (strcmp(name, kWildcardName) == 0) {
455 if (out) out->uiMode =
456 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
457 | ResTable_config::UI_MODE_NIGHT_ANY;
458 return true;
459 } else if (strcmp(name, "night") == 0) {
460 if (out) out->uiMode =
461 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
462 | ResTable_config::UI_MODE_NIGHT_YES;
463 return true;
464 } else if (strcmp(name, "notnight") == 0) {
465 if (out) out->uiMode =
466 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
467 | ResTable_config::UI_MODE_NIGHT_NO;
468 return true;
469 }
470
471 return false;
472}
473
474bool parseDensity(const char* name, ResTable_config* out) {
475 if (strcmp(name, kWildcardName) == 0) {
476 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
477 return true;
478 }
479
480 if (strcmp(name, "nodpi") == 0) {
481 if (out) out->density = ResTable_config::DENSITY_NONE;
482 return true;
483 }
484
485 if (strcmp(name, "ldpi") == 0) {
486 if (out) out->density = ResTable_config::DENSITY_LOW;
487 return true;
488 }
489
490 if (strcmp(name, "mdpi") == 0) {
491 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
492 return true;
493 }
494
495 if (strcmp(name, "tvdpi") == 0) {
496 if (out) out->density = ResTable_config::DENSITY_TV;
497 return true;
498 }
499
500 if (strcmp(name, "hdpi") == 0) {
501 if (out) out->density = ResTable_config::DENSITY_HIGH;
502 return true;
503 }
504
505 if (strcmp(name, "xhdpi") == 0) {
506 if (out) out->density = ResTable_config::DENSITY_XHIGH;
507 return true;
508 }
509
510 if (strcmp(name, "xxhdpi") == 0) {
511 if (out) out->density = ResTable_config::DENSITY_XXHIGH;
512 return true;
513 }
514
515 if (strcmp(name, "xxxhdpi") == 0) {
516 if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
517 return true;
518 }
519
520 char* c = (char*)name;
521 while (*c >= '0' && *c <= '9') {
522 c++;
523 }
524
525 // check that we have 'dpi' after the last digit.
526 if (toupper(c[0]) != 'D' ||
527 toupper(c[1]) != 'P' ||
528 toupper(c[2]) != 'I' ||
529 c[3] != 0) {
530 return false;
531 }
532
533 // temporarily replace the first letter with \0 to
534 // use atoi.
535 char tmp = c[0];
536 c[0] = '\0';
537
538 int d = atoi(name);
539 c[0] = tmp;
540
541 if (d != 0) {
542 if (out) out->density = d;
543 return true;
544 }
545
546 return false;
547}
548
549bool parseTouchscreen(const char* name, ResTable_config* out) {
550 if (strcmp(name, kWildcardName) == 0) {
551 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
552 return true;
553 } else if (strcmp(name, "notouch") == 0) {
554 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
555 return true;
556 } else if (strcmp(name, "stylus") == 0) {
557 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
558 return true;
559 } else if (strcmp(name, "finger") == 0) {
560 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
561 return true;
562 }
563
564 return false;
565}
566
567bool parseKeysHidden(const char* name, ResTable_config* out) {
568 uint8_t mask = 0;
569 uint8_t value = 0;
570 if (strcmp(name, kWildcardName) == 0) {
571 mask = ResTable_config::MASK_KEYSHIDDEN;
572 value = ResTable_config::KEYSHIDDEN_ANY;
573 } else if (strcmp(name, "keysexposed") == 0) {
574 mask = ResTable_config::MASK_KEYSHIDDEN;
575 value = ResTable_config::KEYSHIDDEN_NO;
576 } else if (strcmp(name, "keyshidden") == 0) {
577 mask = ResTable_config::MASK_KEYSHIDDEN;
578 value = ResTable_config::KEYSHIDDEN_YES;
579 } else if (strcmp(name, "keyssoft") == 0) {
580 mask = ResTable_config::MASK_KEYSHIDDEN;
581 value = ResTable_config::KEYSHIDDEN_SOFT;
582 }
583
584 if (mask != 0) {
585 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
586 return true;
587 }
588
589 return false;
590}
591
592bool parseKeyboard(const char* name, ResTable_config* out) {
593 if (strcmp(name, kWildcardName) == 0) {
594 if (out) out->keyboard = out->KEYBOARD_ANY;
595 return true;
596 } else if (strcmp(name, "nokeys") == 0) {
597 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
598 return true;
599 } else if (strcmp(name, "qwerty") == 0) {
600 if (out) out->keyboard = out->KEYBOARD_QWERTY;
601 return true;
602 } else if (strcmp(name, "12key") == 0) {
603 if (out) out->keyboard = out->KEYBOARD_12KEY;
604 return true;
605 }
606
607 return false;
608}
609
610bool parseNavHidden(const char* name, ResTable_config* out) {
611 uint8_t mask = 0;
612 uint8_t value = 0;
613 if (strcmp(name, kWildcardName) == 0) {
614 mask = ResTable_config::MASK_NAVHIDDEN;
615 value = ResTable_config::NAVHIDDEN_ANY;
616 } else if (strcmp(name, "navexposed") == 0) {
617 mask = ResTable_config::MASK_NAVHIDDEN;
618 value = ResTable_config::NAVHIDDEN_NO;
619 } else if (strcmp(name, "navhidden") == 0) {
620 mask = ResTable_config::MASK_NAVHIDDEN;
621 value = ResTable_config::NAVHIDDEN_YES;
622 }
623
624 if (mask != 0) {
625 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
626 return true;
627 }
628
629 return false;
630}
631
632bool parseNavigation(const char* name, ResTable_config* out) {
633 if (strcmp(name, kWildcardName) == 0) {
634 if (out) out->navigation = out->NAVIGATION_ANY;
635 return true;
636 } else if (strcmp(name, "nonav") == 0) {
637 if (out) out->navigation = out->NAVIGATION_NONAV;
638 return true;
639 } else if (strcmp(name, "dpad") == 0) {
640 if (out) out->navigation = out->NAVIGATION_DPAD;
641 return true;
642 } else if (strcmp(name, "trackball") == 0) {
643 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
644 return true;
645 } else if (strcmp(name, "wheel") == 0) {
646 if (out) out->navigation = out->NAVIGATION_WHEEL;
647 return true;
648 }
649
650 return false;
651}
652
653bool parseScreenSize(const char* name, ResTable_config* out) {
654 if (strcmp(name, kWildcardName) == 0) {
655 if (out) {
656 out->screenWidth = out->SCREENWIDTH_ANY;
657 out->screenHeight = out->SCREENHEIGHT_ANY;
658 }
659 return true;
660 }
661
662 const char* x = name;
663 while (*x >= '0' && *x <= '9') x++;
664 if (x == name || *x != 'x') return false;
665 String8 xName(name, x-name);
666 x++;
667
668 const char* y = x;
669 while (*y >= '0' && *y <= '9') y++;
670 if (y == name || *y != 0) return false;
671 String8 yName(x, y-x);
672
673 uint16_t w = (uint16_t)atoi(xName.string());
674 uint16_t h = (uint16_t)atoi(yName.string());
675 if (w < h) {
676 return false;
677 }
678
679 if (out) {
680 out->screenWidth = w;
681 out->screenHeight = h;
682 }
683
684 return true;
685}
686
687bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
688 if (strcmp(name, kWildcardName) == 0) {
689 if (out) {
690 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
691 }
692 return true;
693 }
694
695 if (*name != 's') return false;
696 name++;
697 if (*name != 'w') return false;
698 name++;
699 const char* x = name;
700 while (*x >= '0' && *x <= '9') x++;
701 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
702 String8 xName(name, x-name);
703
704 if (out) {
705 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
706 }
707
708 return true;
709}
710
711bool parseScreenWidthDp(const char* name, ResTable_config* out) {
712 if (strcmp(name, kWildcardName) == 0) {
713 if (out) {
714 out->screenWidthDp = out->SCREENWIDTH_ANY;
715 }
716 return true;
717 }
718
719 if (*name != 'w') return false;
720 name++;
721 const char* x = name;
722 while (*x >= '0' && *x <= '9') x++;
723 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
724 String8 xName(name, x-name);
725
726 if (out) {
727 out->screenWidthDp = (uint16_t)atoi(xName.string());
728 }
729
730 return true;
731}
732
733bool parseScreenHeightDp(const char* name, ResTable_config* out) {
734 if (strcmp(name, kWildcardName) == 0) {
735 if (out) {
736 out->screenHeightDp = out->SCREENWIDTH_ANY;
737 }
738 return true;
739 }
740
741 if (*name != 'h') return false;
742 name++;
743 const char* x = name;
744 while (*x >= '0' && *x <= '9') x++;
745 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
746 String8 xName(name, x-name);
747
748 if (out) {
749 out->screenHeightDp = (uint16_t)atoi(xName.string());
750 }
751
752 return true;
753}
754
755bool parseVersion(const char* name, ResTable_config* out) {
756 if (strcmp(name, kWildcardName) == 0) {
757 if (out) {
758 out->sdkVersion = out->SDKVERSION_ANY;
759 out->minorVersion = out->MINORVERSION_ANY;
760 }
761 return true;
762 }
763
764 if (*name != 'v') {
765 return false;
766 }
767
768 name++;
769 const char* s = name;
770 while (*s >= '0' && *s <= '9') s++;
771 if (s == name || *s != 0) return false;
772 String8 sdkName(name, s-name);
773
774 if (out) {
775 out->sdkVersion = (uint16_t)atoi(sdkName.string());
776 out->minorVersion = 0;
777 }
778
779 return true;
780}
781
782String8 getVersion(const ResTable_config& config) {
783 return String8::format("v%u", config.sdkVersion);
784}
785
786bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
787 return a.diff(b) == axisMask;
788}
789
790} // namespace AaptConfig