OSDN Git Service

am a120e9fc: Merge "Add resources to recyclerview dependency." into lmp-mr1-ub-dev
[android-x86/frameworks-base.git] / tools / aapt / AaptConfig.cpp
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
26 using android::String8;
27 using android::Vector;
28 using android::ResTable_config;
29
30 namespace AaptConfig {
31
32 static const char* kWildcardName = "any";
33
34 bool 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
217 success:
218     if (out != NULL) {
219         applyVersionForCompatibility(&config);
220         *out = config;
221     }
222     return true;
223 }
224
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)) {
231             return false;
232         }
233         outSet->insert(config);
234     }
235     return true;
236 }
237
238 void 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
268 bool 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
298 bool 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
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;
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
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;
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
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;
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
402 bool 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
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;
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
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;
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
477 bool 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
557 bool 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
575 bool 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
600 bool 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
618 bool 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
640 bool 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
661 bool 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
695 bool 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
719 bool 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
741 bool 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
763 bool 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
790 String8 getVersion(const ResTable_config& config) {
791     return String8::format("v%u", config.sdkVersion);
792 }
793
794 bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
795     return a.diff(b) == axisMask;
796 }
797
798 bool 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