1/*
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#include "SdkConstants.h"
25
26using android::String8;
27using android::Vector;
28using android::ResTable_config;
29
30namespace AaptConfig {
31
32static const char* kWildcardName = "any";
33
34bool parse(const String8& str, ConfigDescription* out) {
35    Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-');
36
37    ConfigDescription config;
38    AaptLocaleValue locale;
39    ssize_t index = 0;
40    ssize_t localeIndex = 0;
41    const ssize_t N = parts.size();
42    const char* part = parts[index].string();
43
44    if (str.length() == 0) {
45        goto success;
46    }
47
48    if (parseMcc(part, &config)) {
49        index++;
50        if (index == N) {
51            goto success;
52        }
53        part = parts[index].string();
54    }
55
56    if (parseMnc(part, &config)) {
57        index++;
58        if (index == N) {
59            goto success;
60        }
61        part = parts[index].string();
62    }
63
64    // Locale spans a few '-' separators, so we let it
65    // control the index.
66    localeIndex = locale.initFromDirName(parts, index);
67    if (localeIndex < 0) {
68        return false;
69    } else if (localeIndex > index) {
70        locale.writeTo(&config);
71        index = localeIndex;
72        if (index >= N) {
73            goto success;
74        }
75        part = parts[index].string();
76    }
77
78    if (parseLayoutDirection(part, &config)) {
79        index++;
80        if (index == N) {
81            goto success;
82        }
83        part = parts[index].string();
84    }
85
86    if (parseSmallestScreenWidthDp(part, &config)) {
87        index++;
88        if (index == N) {
89            goto success;
90        }
91        part = parts[index].string();
92    }
93
94    if (parseScreenWidthDp(part, &config)) {
95        index++;
96        if (index == N) {
97            goto success;
98        }
99        part = parts[index].string();
100    }
101
102    if (parseScreenHeightDp(part, &config)) {
103        index++;
104        if (index == N) {
105            goto success;
106        }
107        part = parts[index].string();
108    }
109
110    if (parseScreenLayoutSize(part, &config)) {
111        index++;
112        if (index == N) {
113            goto success;
114        }
115        part = parts[index].string();
116    }
117
118    if (parseScreenLayoutLong(part, &config)) {
119        index++;
120        if (index == N) {
121            goto success;
122        }
123        part = parts[index].string();
124    }
125
126    if (parseOrientation(part, &config)) {
127        index++;
128        if (index == N) {
129            goto success;
130        }
131        part = parts[index].string();
132    }
133
134    if (parseUiModeType(part, &config)) {
135        index++;
136        if (index == N) {
137            goto success;
138        }
139        part = parts[index].string();
140    }
141
142    if (parseUiModeNight(part, &config)) {
143        index++;
144        if (index == N) {
145            goto success;
146        }
147        part = parts[index].string();
148    }
149
150    if (parseDensity(part, &config)) {
151        index++;
152        if (index == N) {
153            goto success;
154        }
155        part = parts[index].string();
156    }
157
158    if (parseTouchscreen(part, &config)) {
159        index++;
160        if (index == N) {
161            goto success;
162        }
163        part = parts[index].string();
164    }
165
166    if (parseKeysHidden(part, &config)) {
167        index++;
168        if (index == N) {
169            goto success;
170        }
171        part = parts[index].string();
172    }
173
174    if (parseKeyboard(part, &config)) {
175        index++;
176        if (index == N) {
177            goto success;
178        }
179        part = parts[index].string();
180    }
181
182    if (parseNavHidden(part, &config)) {
183        index++;
184        if (index == N) {
185            goto success;
186        }
187        part = parts[index].string();
188    }
189
190    if (parseNavigation(part, &config)) {
191        index++;
192        if (index == N) {
193            goto success;
194        }
195        part = parts[index].string();
196    }
197
198    if (parseScreenSize(part, &config)) {
199        index++;
200        if (index == N) {
201            goto success;
202        }
203        part = parts[index].string();
204    }
205
206    if (parseVersion(part, &config)) {
207        index++;
208        if (index == N) {
209            goto success;
210        }
211        part = parts[index].string();
212    }
213
214    // Unrecognized.
215    return false;
216
217success:
218    if (out != NULL) {
219        applyVersionForCompatibility(&config);
220        *out = config;
221    }
222    return true;
223}
224
225bool 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)) {
231            return false;
232        }
233        outSet->insert(config);
234    }
235    return true;
236}
237
238void applyVersionForCompatibility(ConfigDescription* config) {
239    if (config == NULL) {
240        return;
241    }
242
243    uint16_t minSdk = 0;
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) {
254        minSdk = SDK_FROYO;
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) {
260        minSdk = SDK_DONUT;
261    }
262
263    if (minSdk > config->sdkVersion) {
264        config->sdkVersion = minSdk;
265    }
266}
267
268bool parseMcc(const char* name, ResTable_config* out) {
269    if (strcmp(name, kWildcardName) == 0) {
270        if (out) out->mcc = 0;
271        return true;
272    }
273    const char* c = name;
274    if (tolower(*c) != 'm') return false;
275    c++;
276    if (tolower(*c) != 'c') return false;
277    c++;
278    if (tolower(*c) != 'c') return false;
279    c++;
280
281    const char* val = c;
282
283    while (*c >= '0' && *c <= '9') {
284        c++;
285    }
286    if (*c != 0) return false;
287    if (c-val != 3) return false;
288
289    int d = atoi(val);
290    if (d != 0) {
291        if (out) out->mcc = d;
292        return true;
293    }
294
295    return false;
296}
297
298bool parseMnc(const char* name, ResTable_config* out) {
299    if (strcmp(name, kWildcardName) == 0) {
300        if (out) out->mcc = 0;
301        return true;
302    }
303    const char* c = name;
304    if (tolower(*c) != 'm') return false;
305    c++;
306    if (tolower(*c) != 'n') return false;
307    c++;
308    if (tolower(*c) != 'c') return false;
309    c++;
310
311    const char* val = c;
312
313    while (*c >= '0' && *c <= '9') {
314        c++;
315    }
316    if (*c != 0) return false;
317    if (c-val == 0 || c-val > 3) return false;
318
319    if (out) {
320        out->mnc = atoi(val);
321        if (out->mnc == 0) {
322            out->mnc = ACONFIGURATION_MNC_ZERO;
323        }
324    }
325
326    return true;
327}
328
329bool 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;
334        return true;
335    } else if (strcmp(name, "ldltr") == 0) {
336        if (out) out->screenLayout =
337                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
338                | ResTable_config::LAYOUTDIR_LTR;
339        return true;
340    } else if (strcmp(name, "ldrtl") == 0) {
341        if (out) out->screenLayout =
342                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
343                | ResTable_config::LAYOUTDIR_RTL;
344        return true;
345    }
346
347    return false;
348}
349
350bool 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;
355        return true;
356    } else if (strcmp(name, "small") == 0) {
357        if (out) out->screenLayout =
358                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
359                | ResTable_config::SCREENSIZE_SMALL;
360        return true;
361    } else if (strcmp(name, "normal") == 0) {
362        if (out) out->screenLayout =
363                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
364                | ResTable_config::SCREENSIZE_NORMAL;
365        return true;
366    } else if (strcmp(name, "large") == 0) {
367        if (out) out->screenLayout =
368                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
369                | ResTable_config::SCREENSIZE_LARGE;
370        return true;
371    } else if (strcmp(name, "xlarge") == 0) {
372        if (out) out->screenLayout =
373                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
374                | ResTable_config::SCREENSIZE_XLARGE;
375        return true;
376    }
377
378    return false;
379}
380
381bool 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;
386        return true;
387    } else if (strcmp(name, "long") == 0) {
388        if (out) out->screenLayout =
389                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
390                | ResTable_config::SCREENLONG_YES;
391        return true;
392    } else if (strcmp(name, "notlong") == 0) {
393        if (out) out->screenLayout =
394                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
395                | ResTable_config::SCREENLONG_NO;
396        return true;
397    }
398
399    return false;
400}
401
402bool parseOrientation(const char* name, ResTable_config* out) {
403    if (strcmp(name, kWildcardName) == 0) {
404        if (out) out->orientation = out->ORIENTATION_ANY;
405        return true;
406    } else if (strcmp(name, "port") == 0) {
407        if (out) out->orientation = out->ORIENTATION_PORT;
408        return true;
409    } else if (strcmp(name, "land") == 0) {
410        if (out) out->orientation = out->ORIENTATION_LAND;
411        return true;
412    } else if (strcmp(name, "square") == 0) {
413        if (out) out->orientation = out->ORIENTATION_SQUARE;
414        return true;
415    }
416
417    return false;
418}
419
420bool 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;
425        return true;
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;
430        return true;
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;
435        return true;
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;
440        return true;
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;
445        return true;
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;
450        return true;
451    }
452
453    return false;
454}
455
456bool 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;
461        return true;
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;
466        return true;
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;
471        return true;
472    }
473
474    return false;
475}
476
477bool parseDensity(const char* name, ResTable_config* out) {
478    if (strcmp(name, kWildcardName) == 0) {
479        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
480        return true;
481    }
482
483    if (strcmp(name, "anydpi") == 0) {
484        if (out) out->density = ResTable_config::DENSITY_ANY;
485        return true;
486    }
487
488    if (strcmp(name, "nodpi") == 0) {
489        if (out) out->density = ResTable_config::DENSITY_NONE;
490        return true;
491    }
492
493    if (strcmp(name, "ldpi") == 0) {
494        if (out) out->density = ResTable_config::DENSITY_LOW;
495        return true;
496    }
497
498    if (strcmp(name, "mdpi") == 0) {
499        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
500        return true;
501    }
502
503    if (strcmp(name, "tvdpi") == 0) {
504        if (out) out->density = ResTable_config::DENSITY_TV;
505        return true;
506    }
507
508    if (strcmp(name, "hdpi") == 0) {
509        if (out) out->density = ResTable_config::DENSITY_HIGH;
510        return true;
511    }
512
513    if (strcmp(name, "xhdpi") == 0) {
514        if (out) out->density = ResTable_config::DENSITY_XHIGH;
515        return true;
516    }
517
518    if (strcmp(name, "xxhdpi") == 0) {
519        if (out) out->density = ResTable_config::DENSITY_XXHIGH;
520        return true;
521    }
522
523    if (strcmp(name, "xxxhdpi") == 0) {
524        if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
525        return true;
526    }
527
528    char* c = (char*)name;
529    while (*c >= '0' && *c <= '9') {
530        c++;
531    }
532
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' ||
537            c[3] != 0) {
538        return false;
539    }
540
541    // temporarily replace the first letter with \0 to
542    // use atoi.
543    char tmp = c[0];
544    c[0] = '\0';
545
546    int d = atoi(name);
547    c[0] = tmp;
548
549    if (d != 0) {
550        if (out) out->density = d;
551        return true;
552    }
553
554    return false;
555}
556
557bool parseTouchscreen(const char* name, ResTable_config* out) {
558    if (strcmp(name, kWildcardName) == 0) {
559        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
560        return true;
561    } else if (strcmp(name, "notouch") == 0) {
562        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
563        return true;
564    } else if (strcmp(name, "stylus") == 0) {
565        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
566        return true;
567    } else if (strcmp(name, "finger") == 0) {
568        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
569        return true;
570    }
571
572    return false;
573}
574
575bool parseKeysHidden(const char* name, ResTable_config* out) {
576    uint8_t mask = 0;
577    uint8_t value = 0;
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;
590    }
591
592    if (mask != 0) {
593        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
594        return true;
595    }
596
597    return false;
598}
599
600bool parseKeyboard(const char* name, ResTable_config* out) {
601    if (strcmp(name, kWildcardName) == 0) {
602        if (out) out->keyboard = out->KEYBOARD_ANY;
603        return true;
604    } else if (strcmp(name, "nokeys") == 0) {
605        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
606        return true;
607    } else if (strcmp(name, "qwerty") == 0) {
608        if (out) out->keyboard = out->KEYBOARD_QWERTY;
609        return true;
610    } else if (strcmp(name, "12key") == 0) {
611        if (out) out->keyboard = out->KEYBOARD_12KEY;
612        return true;
613    }
614
615    return false;
616}
617
618bool parseNavHidden(const char* name, ResTable_config* out) {
619    uint8_t mask = 0;
620    uint8_t value = 0;
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;
630    }
631
632    if (mask != 0) {
633        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
634        return true;
635    }
636
637    return false;
638}
639
640bool parseNavigation(const char* name, ResTable_config* out) {
641    if (strcmp(name, kWildcardName) == 0) {
642        if (out) out->navigation = out->NAVIGATION_ANY;
643        return true;
644    } else if (strcmp(name, "nonav") == 0) {
645        if (out) out->navigation = out->NAVIGATION_NONAV;
646        return true;
647    } else if (strcmp(name, "dpad") == 0) {
648        if (out) out->navigation = out->NAVIGATION_DPAD;
649        return true;
650    } else if (strcmp(name, "trackball") == 0) {
651        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
652        return true;
653    } else if (strcmp(name, "wheel") == 0) {
654        if (out) out->navigation = out->NAVIGATION_WHEEL;
655        return true;
656    }
657
658    return false;
659}
660
661bool parseScreenSize(const char* name, ResTable_config* out) {
662    if (strcmp(name, kWildcardName) == 0) {
663        if (out) {
664            out->screenWidth = out->SCREENWIDTH_ANY;
665            out->screenHeight = out->SCREENHEIGHT_ANY;
666        }
667        return true;
668    }
669
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);
674    x++;
675
676    const char* y = x;
677    while (*y >= '0' && *y <= '9') y++;
678    if (y == name || *y != 0) return false;
679    String8 yName(x, y-x);
680
681    uint16_t w = (uint16_t)atoi(xName.string());
682    uint16_t h = (uint16_t)atoi(yName.string());
683    if (w < h) {
684        return false;
685    }
686
687    if (out) {
688        out->screenWidth = w;
689        out->screenHeight = h;
690    }
691
692    return true;
693}
694
695bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
696    if (strcmp(name, kWildcardName) == 0) {
697        if (out) {
698            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
699        }
700        return true;
701    }
702
703    if (*name != 's') return false;
704    name++;
705    if (*name != 'w') return false;
706    name++;
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);
711
712    if (out) {
713        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
714    }
715
716    return true;
717}
718
719bool parseScreenWidthDp(const char* name, ResTable_config* out) {
720    if (strcmp(name, kWildcardName) == 0) {
721        if (out) {
722            out->screenWidthDp = out->SCREENWIDTH_ANY;
723        }
724        return true;
725    }
726
727    if (*name != 'w') return false;
728    name++;
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);
733
734    if (out) {
735        out->screenWidthDp = (uint16_t)atoi(xName.string());
736    }
737
738    return true;
739}
740
741bool parseScreenHeightDp(const char* name, ResTable_config* out) {
742    if (strcmp(name, kWildcardName) == 0) {
743        if (out) {
744            out->screenHeightDp = out->SCREENWIDTH_ANY;
745        }
746        return true;
747    }
748
749    if (*name != 'h') return false;
750    name++;
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);
755
756    if (out) {
757        out->screenHeightDp = (uint16_t)atoi(xName.string());
758    }
759
760    return true;
761}
762
763bool parseVersion(const char* name, ResTable_config* out) {
764    if (strcmp(name, kWildcardName) == 0) {
765        if (out) {
766            out->sdkVersion = out->SDKVERSION_ANY;
767            out->minorVersion = out->MINORVERSION_ANY;
768        }
769        return true;
770    }
771
772    if (*name != 'v') {
773        return false;
774    }
775
776    name++;
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);
781
782    if (out) {
783        out->sdkVersion = (uint16_t)atoi(sdkName.string());
784        out->minorVersion = 0;
785    }
786
787    return true;
788}
789
790String8 getVersion(const ResTable_config& config) {
791    return String8::format("v%u", config.sdkVersion);
792}
793
794bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
795    return a.diff(b) == axisMask;
796}
797
798bool isDensityOnly(const ResTable_config& config) {
799    if (config.density == ResTable_config::DENSITY_DEFAULT) {
800        return false;
801    }
802
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.
806            return false;
807        }
808    } else if (config.sdkVersion != SDK_DONUT) {
809        return false;
810    }
811
812    const uint32_t mask = ResTable_config::CONFIG_DENSITY | ResTable_config::CONFIG_VERSION;
813    const ConfigDescription nullConfig;
814    return (nullConfig.diff(config) & ~mask) == 0;
815}
816
817} // namespace AaptConfig
818