1 /*
2  * Copyright (C) 2015 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/ConfigDescription.h"
18 #include "androidfw/Locale.h"
19 #include "androidfw/ResourceTypes.h"
20 #include "androidfw/StringPiece.h"
21 #include "androidfw/Util.h"
22 
23 #include <string>
24 #include <string_view>
25 #include <vector>
26 
27 namespace android {
28 
29 static const char* kWildcardName = "any";
30 
DefaultConfig()31 const ConfigDescription& ConfigDescription::DefaultConfig() {
32   static ConfigDescription config = {};
33   return config;
34 }
35 
parseMcc(const char * name,ResTable_config * out)36 static bool parseMcc(const char* name, ResTable_config* out) {
37   if (strcmp(name, kWildcardName) == 0) {
38     if (out) out->mcc = 0;
39     return true;
40   }
41   const char* c = name;
42   if (*c != 'm') return false;
43   c++;
44   if (*c != 'c') return false;
45   c++;
46   if (*c != 'c') return false;
47   c++;
48 
49   const char* val = c;
50 
51   while (*c >= '0' && *c <= '9') {
52     c++;
53   }
54   if (*c != 0) return false;
55   if (c - val != 3) return false;
56 
57   int d = atoi(val);
58   if (d != 0) {
59     if (out) out->mcc = d;
60     return true;
61   }
62 
63   return false;
64 }
65 
parseMnc(const char * name,ResTable_config * out)66 static bool parseMnc(const char* name, ResTable_config* out) {
67   if (strcmp(name, kWildcardName) == 0) {
68     if (out) out->mnc = 0;
69     return true;
70   }
71   const char* c = name;
72   if (*c != 'm') return false;
73   c++;
74   if (*c != 'n') return false;
75   c++;
76   if (*c != 'c') return false;
77   c++;
78 
79   const char* val = c;
80 
81   while (*c >= '0' && *c <= '9') {
82     c++;
83   }
84   if (*c != 0) return false;
85   if (c - val == 0 || c - val > 3) return false;
86 
87   if (out) {
88     out->mnc = atoi(val);
89     if (out->mnc == 0) {
90       out->mnc = ACONFIGURATION_MNC_ZERO;
91     }
92   }
93 
94   return true;
95 }
96 
parseGrammaticalInflection(const std::string & name,ResTable_config * out)97 static bool parseGrammaticalInflection(const std::string& name, ResTable_config* out) {
98   using namespace std::literals;
99   if (name == "feminine"sv) {
100     if (out) out->grammaticalInflection = ResTable_config::GRAMMATICAL_GENDER_FEMININE;
101     return true;
102   }
103   if (name == "masculine"sv) {
104     if (out) out->grammaticalInflection = ResTable_config::GRAMMATICAL_GENDER_MASCULINE;
105     return true;
106   }
107   if (name == "neuter"sv) {
108     if (out) out->grammaticalInflection = ResTable_config::GRAMMATICAL_GENDER_NEUTER;
109     return true;
110   }
111   return false;
112 }
113 
parseLayoutDirection(const char * name,ResTable_config * out)114 static bool parseLayoutDirection(const char* name, ResTable_config* out) {
115   if (strcmp(name, kWildcardName) == 0) {
116     if (out)
117       out->screenLayout =
118           (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) |
119           ResTable_config::LAYOUTDIR_ANY;
120     return true;
121   } else if (strcmp(name, "ldltr") == 0) {
122     if (out)
123       out->screenLayout =
124           (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) |
125           ResTable_config::LAYOUTDIR_LTR;
126     return true;
127   } else if (strcmp(name, "ldrtl") == 0) {
128     if (out)
129       out->screenLayout =
130           (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) |
131           ResTable_config::LAYOUTDIR_RTL;
132     return true;
133   }
134 
135   return false;
136 }
137 
parseScreenLayoutSize(const char * name,ResTable_config * out)138 static bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
139   if (strcmp(name, kWildcardName) == 0) {
140     if (out)
141       out->screenLayout =
142           (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
143           ResTable_config::SCREENSIZE_ANY;
144     return true;
145   } else if (strcmp(name, "small") == 0) {
146     if (out)
147       out->screenLayout =
148           (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
149           ResTable_config::SCREENSIZE_SMALL;
150     return true;
151   } else if (strcmp(name, "normal") == 0) {
152     if (out)
153       out->screenLayout =
154           (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
155           ResTable_config::SCREENSIZE_NORMAL;
156     return true;
157   } else if (strcmp(name, "large") == 0) {
158     if (out)
159       out->screenLayout =
160           (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
161           ResTable_config::SCREENSIZE_LARGE;
162     return true;
163   } else if (strcmp(name, "xlarge") == 0) {
164     if (out)
165       out->screenLayout =
166           (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
167           ResTable_config::SCREENSIZE_XLARGE;
168     return true;
169   }
170 
171   return false;
172 }
173 
parseScreenLayoutLong(const char * name,ResTable_config * out)174 static bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
175   if (strcmp(name, kWildcardName) == 0) {
176     if (out)
177       out->screenLayout =
178           (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) |
179           ResTable_config::SCREENLONG_ANY;
180     return true;
181   } else if (strcmp(name, "long") == 0) {
182     if (out)
183       out->screenLayout =
184           (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) |
185           ResTable_config::SCREENLONG_YES;
186     return true;
187   } else if (strcmp(name, "notlong") == 0) {
188     if (out)
189       out->screenLayout =
190           (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) |
191           ResTable_config::SCREENLONG_NO;
192     return true;
193   }
194 
195   return false;
196 }
197 
parseScreenRound(const char * name,ResTable_config * out)198 static bool parseScreenRound(const char* name, ResTable_config* out) {
199   if (strcmp(name, kWildcardName) == 0) {
200     if (out)
201       out->screenLayout2 =
202           (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) |
203           ResTable_config::SCREENROUND_ANY;
204     return true;
205   } else if (strcmp(name, "round") == 0) {
206     if (out)
207       out->screenLayout2 =
208           (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) |
209           ResTable_config::SCREENROUND_YES;
210     return true;
211   } else if (strcmp(name, "notround") == 0) {
212     if (out)
213       out->screenLayout2 =
214           (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) |
215           ResTable_config::SCREENROUND_NO;
216     return true;
217   }
218   return false;
219 }
220 
parseWideColorGamut(const char * name,ResTable_config * out)221 static bool parseWideColorGamut(const char* name, ResTable_config* out) {
222   if (strcmp(name, kWildcardName) == 0) {
223     if (out)
224       out->colorMode =
225           (out->colorMode & ~ResTable_config::MASK_WIDE_COLOR_GAMUT) |
226           ResTable_config::WIDE_COLOR_GAMUT_ANY;
227     return true;
228   } else if (strcmp(name, "widecg") == 0) {
229     if (out)
230       out->colorMode =
231           (out->colorMode & ~ResTable_config::MASK_WIDE_COLOR_GAMUT) |
232           ResTable_config::WIDE_COLOR_GAMUT_YES;
233     return true;
234   } else if (strcmp(name, "nowidecg") == 0) {
235     if (out)
236       out->colorMode =
237           (out->colorMode & ~ResTable_config::MASK_WIDE_COLOR_GAMUT) |
238           ResTable_config::WIDE_COLOR_GAMUT_NO;
239     return true;
240   }
241   return false;
242 }
243 
parseHdr(const char * name,ResTable_config * out)244 static bool parseHdr(const char* name, ResTable_config* out) {
245   if (strcmp(name, kWildcardName) == 0) {
246     if (out)
247       out->colorMode =
248           (out->colorMode & ~ResTable_config::MASK_HDR) |
249           ResTable_config::HDR_ANY;
250     return true;
251   } else if (strcmp(name, "highdr") == 0) {
252     if (out)
253       out->colorMode =
254           (out->colorMode & ~ResTable_config::MASK_HDR) |
255           ResTable_config::HDR_YES;
256     return true;
257   } else if (strcmp(name, "lowdr") == 0) {
258     if (out)
259       out->colorMode =
260           (out->colorMode & ~ResTable_config::MASK_HDR) |
261           ResTable_config::HDR_NO;
262     return true;
263   }
264   return false;
265 }
266 
parseOrientation(const char * name,ResTable_config * out)267 static bool parseOrientation(const char* name, ResTable_config* out) {
268   if (strcmp(name, kWildcardName) == 0) {
269     if (out) out->orientation = out->ORIENTATION_ANY;
270     return true;
271   } else if (strcmp(name, "port") == 0) {
272     if (out) out->orientation = out->ORIENTATION_PORT;
273     return true;
274   } else if (strcmp(name, "land") == 0) {
275     if (out) out->orientation = out->ORIENTATION_LAND;
276     return true;
277   } else if (strcmp(name, "square") == 0) {
278     if (out) out->orientation = out->ORIENTATION_SQUARE;
279     return true;
280   }
281 
282   return false;
283 }
284 
parseUiModeType(const char * name,ResTable_config * out)285 static bool parseUiModeType(const char* name, ResTable_config* out) {
286   if (strcmp(name, kWildcardName) == 0) {
287     if (out)
288       out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
289                     ResTable_config::UI_MODE_TYPE_ANY;
290     return true;
291   } else if (strcmp(name, "desk") == 0) {
292     if (out)
293       out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
294                     ResTable_config::UI_MODE_TYPE_DESK;
295     return true;
296   } else if (strcmp(name, "car") == 0) {
297     if (out)
298       out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
299                     ResTable_config::UI_MODE_TYPE_CAR;
300     return true;
301   } else if (strcmp(name, "television") == 0) {
302     if (out)
303       out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
304                     ResTable_config::UI_MODE_TYPE_TELEVISION;
305     return true;
306   } else if (strcmp(name, "appliance") == 0) {
307     if (out)
308       out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
309                     ResTable_config::UI_MODE_TYPE_APPLIANCE;
310     return true;
311   } else if (strcmp(name, "watch") == 0) {
312     if (out)
313       out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
314                     ResTable_config::UI_MODE_TYPE_WATCH;
315     return true;
316   } else if (strcmp(name, "vrheadset") == 0) {
317     if (out)
318       out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
319                     ResTable_config::UI_MODE_TYPE_VR_HEADSET;
320     return true;
321   }
322 
323   return false;
324 }
325 
parseUiModeNight(const char * name,ResTable_config * out)326 static bool parseUiModeNight(const char* name, ResTable_config* out) {
327   if (strcmp(name, kWildcardName) == 0) {
328     if (out)
329       out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
330                     ResTable_config::UI_MODE_NIGHT_ANY;
331     return true;
332   } else if (strcmp(name, "night") == 0) {
333     if (out)
334       out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
335                     ResTable_config::UI_MODE_NIGHT_YES;
336     return true;
337   } else if (strcmp(name, "notnight") == 0) {
338     if (out)
339       out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
340                     ResTable_config::UI_MODE_NIGHT_NO;
341     return true;
342   }
343 
344   return false;
345 }
346 
parseDensity(const char * name,ResTable_config * out)347 static bool parseDensity(const char* name, ResTable_config* out) {
348   if (strcmp(name, kWildcardName) == 0) {
349     if (out) out->density = ResTable_config::DENSITY_DEFAULT;
350     return true;
351   }
352 
353   if (strcmp(name, "anydpi") == 0) {
354     if (out) out->density = ResTable_config::DENSITY_ANY;
355     return true;
356   }
357 
358   if (strcmp(name, "nodpi") == 0) {
359     if (out) out->density = ResTable_config::DENSITY_NONE;
360     return true;
361   }
362 
363   if (strcmp(name, "ldpi") == 0) {
364     if (out) out->density = ResTable_config::DENSITY_LOW;
365     return true;
366   }
367 
368   if (strcmp(name, "mdpi") == 0) {
369     if (out) out->density = ResTable_config::DENSITY_MEDIUM;
370     return true;
371   }
372 
373   if (strcmp(name, "tvdpi") == 0) {
374     if (out) out->density = ResTable_config::DENSITY_TV;
375     return true;
376   }
377 
378   if (strcmp(name, "hdpi") == 0) {
379     if (out) out->density = ResTable_config::DENSITY_HIGH;
380     return true;
381   }
382 
383   if (strcmp(name, "xhdpi") == 0) {
384     if (out) out->density = ResTable_config::DENSITY_XHIGH;
385     return true;
386   }
387 
388   if (strcmp(name, "xxhdpi") == 0) {
389     if (out) out->density = ResTable_config::DENSITY_XXHIGH;
390     return true;
391   }
392 
393   if (strcmp(name, "xxxhdpi") == 0) {
394     if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
395     return true;
396   }
397 
398   char* c = (char*)name;
399   while (*c >= '0' && *c <= '9') {
400     c++;
401   }
402 
403   // check that we have 'dpi' after the last digit.
404   if (toupper(c[0]) != 'D' || toupper(c[1]) != 'P' || toupper(c[2]) != 'I' ||
405       c[3] != 0) {
406     return false;
407   }
408 
409   // temporarily replace the first letter with \0 to
410   // use atoi.
411   char tmp = c[0];
412   c[0] = '\0';
413 
414   int d = atoi(name);
415   c[0] = tmp;
416 
417   if (d != 0) {
418     if (out) out->density = d;
419     return true;
420   }
421 
422   return false;
423 }
424 
parseTouchscreen(const char * name,ResTable_config * out)425 static bool parseTouchscreen(const char* name, ResTable_config* out) {
426   if (strcmp(name, kWildcardName) == 0) {
427     if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
428     return true;
429   } else if (strcmp(name, "notouch") == 0) {
430     if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
431     return true;
432   } else if (strcmp(name, "stylus") == 0) {
433     if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
434     return true;
435   } else if (strcmp(name, "finger") == 0) {
436     if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
437     return true;
438   }
439 
440   return false;
441 }
442 
parseKeysHidden(const char * name,ResTable_config * out)443 static bool parseKeysHidden(const char* name, ResTable_config* out) {
444   uint8_t mask = 0;
445   uint8_t value = 0;
446   if (strcmp(name, kWildcardName) == 0) {
447     mask = ResTable_config::MASK_KEYSHIDDEN;
448     value = ResTable_config::KEYSHIDDEN_ANY;
449   } else if (strcmp(name, "keysexposed") == 0) {
450     mask = ResTable_config::MASK_KEYSHIDDEN;
451     value = ResTable_config::KEYSHIDDEN_NO;
452   } else if (strcmp(name, "keyshidden") == 0) {
453     mask = ResTable_config::MASK_KEYSHIDDEN;
454     value = ResTable_config::KEYSHIDDEN_YES;
455   } else if (strcmp(name, "keyssoft") == 0) {
456     mask = ResTable_config::MASK_KEYSHIDDEN;
457     value = ResTable_config::KEYSHIDDEN_SOFT;
458   }
459 
460   if (mask != 0) {
461     if (out) out->inputFlags = (out->inputFlags & ~mask) | value;
462     return true;
463   }
464 
465   return false;
466 }
467 
parseKeyboard(const char * name,ResTable_config * out)468 static bool parseKeyboard(const char* name, ResTable_config* out) {
469   if (strcmp(name, kWildcardName) == 0) {
470     if (out) out->keyboard = out->KEYBOARD_ANY;
471     return true;
472   } else if (strcmp(name, "nokeys") == 0) {
473     if (out) out->keyboard = out->KEYBOARD_NOKEYS;
474     return true;
475   } else if (strcmp(name, "qwerty") == 0) {
476     if (out) out->keyboard = out->KEYBOARD_QWERTY;
477     return true;
478   } else if (strcmp(name, "12key") == 0) {
479     if (out) out->keyboard = out->KEYBOARD_12KEY;
480     return true;
481   }
482 
483   return false;
484 }
485 
parseNavHidden(const char * name,ResTable_config * out)486 static bool parseNavHidden(const char* name, ResTable_config* out) {
487   uint8_t mask = 0;
488   uint8_t value = 0;
489   if (strcmp(name, kWildcardName) == 0) {
490     mask = ResTable_config::MASK_NAVHIDDEN;
491     value = ResTable_config::NAVHIDDEN_ANY;
492   } else if (strcmp(name, "navexposed") == 0) {
493     mask = ResTable_config::MASK_NAVHIDDEN;
494     value = ResTable_config::NAVHIDDEN_NO;
495   } else if (strcmp(name, "navhidden") == 0) {
496     mask = ResTable_config::MASK_NAVHIDDEN;
497     value = ResTable_config::NAVHIDDEN_YES;
498   }
499 
500   if (mask != 0) {
501     if (out) out->inputFlags = (out->inputFlags & ~mask) | value;
502     return true;
503   }
504 
505   return false;
506 }
507 
parseNavigation(const char * name,ResTable_config * out)508 static bool parseNavigation(const char* name, ResTable_config* out) {
509   if (strcmp(name, kWildcardName) == 0) {
510     if (out) out->navigation = out->NAVIGATION_ANY;
511     return true;
512   } else if (strcmp(name, "nonav") == 0) {
513     if (out) out->navigation = out->NAVIGATION_NONAV;
514     return true;
515   } else if (strcmp(name, "dpad") == 0) {
516     if (out) out->navigation = out->NAVIGATION_DPAD;
517     return true;
518   } else if (strcmp(name, "trackball") == 0) {
519     if (out) out->navigation = out->NAVIGATION_TRACKBALL;
520     return true;
521   } else if (strcmp(name, "wheel") == 0) {
522     if (out) out->navigation = out->NAVIGATION_WHEEL;
523     return true;
524   }
525 
526   return false;
527 }
528 
parseScreenSize(const char * name,ResTable_config * out)529 static bool parseScreenSize(const char* name, ResTable_config* out) {
530   if (strcmp(name, kWildcardName) == 0) {
531     if (out) {
532       out->screenWidth = out->SCREENWIDTH_ANY;
533       out->screenHeight = out->SCREENHEIGHT_ANY;
534     }
535     return true;
536   }
537 
538   const char* x = name;
539   while (*x >= '0' && *x <= '9') x++;
540   if (x == name || *x != 'x') return false;
541   std::string xName(name, x - name);
542   x++;
543 
544   const char* y = x;
545   while (*y >= '0' && *y <= '9') y++;
546   if (y == name || *y != 0) return false;
547   std::string yName(x, y - x);
548 
549   uint16_t w = (uint16_t)atoi(xName.c_str());
550   uint16_t h = (uint16_t)atoi(yName.c_str());
551   if (w < h) {
552     return false;
553   }
554 
555   if (out) {
556     out->screenWidth = w;
557     out->screenHeight = h;
558   }
559 
560   return true;
561 }
562 
parseSmallestScreenWidthDp(const char * name,ResTable_config * out)563 static bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
564   if (strcmp(name, kWildcardName) == 0) {
565     if (out) {
566       out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
567     }
568     return true;
569   }
570 
571   if (*name != 's') return false;
572   name++;
573   if (*name != 'w') return false;
574   name++;
575   const char* x = name;
576   while (*x >= '0' && *x <= '9') x++;
577   if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
578   std::string xName(name, x - name);
579 
580   if (out) {
581     out->smallestScreenWidthDp = (uint16_t)atoi(xName.c_str());
582   }
583 
584   return true;
585 }
586 
parseScreenWidthDp(const char * name,ResTable_config * out)587 static bool parseScreenWidthDp(const char* name, ResTable_config* out) {
588   if (strcmp(name, kWildcardName) == 0) {
589     if (out) {
590       out->screenWidthDp = out->SCREENWIDTH_ANY;
591     }
592     return true;
593   }
594 
595   if (*name != 'w') return false;
596   name++;
597   const char* x = name;
598   while (*x >= '0' && *x <= '9') x++;
599   if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
600   std::string xName(name, x - name);
601 
602   if (out) {
603     out->screenWidthDp = (uint16_t)atoi(xName.c_str());
604   }
605 
606   return true;
607 }
608 
parseScreenHeightDp(const char * name,ResTable_config * out)609 static bool parseScreenHeightDp(const char* name, ResTable_config* out) {
610   if (strcmp(name, kWildcardName) == 0) {
611     if (out) {
612       out->screenHeightDp = out->SCREENWIDTH_ANY;
613     }
614     return true;
615   }
616 
617   if (*name != 'h') return false;
618   name++;
619   const char* x = name;
620   while (*x >= '0' && *x <= '9') x++;
621   if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
622   std::string xName(name, x - name);
623 
624   if (out) {
625     out->screenHeightDp = (uint16_t)atoi(xName.c_str());
626   }
627 
628   return true;
629 }
630 
parseVersion(const char * name,ResTable_config * out)631 static bool parseVersion(const char* name, ResTable_config* out) {
632   if (strcmp(name, kWildcardName) == 0) {
633     if (out) {
634       out->sdkVersion = out->SDKVERSION_ANY;
635       out->minorVersion = out->MINORVERSION_ANY;
636     }
637     return true;
638   }
639 
640   if (*name != 'v') {
641     return false;
642   }
643 
644   name++;
645   const char* s = name;
646   while (*s >= '0' && *s <= '9') s++;
647   if (s == name || *s != 0) return false;
648   std::string sdkName(name, s - name);
649 
650   if (out) {
651     out->sdkVersion = (uint16_t)atoi(sdkName.c_str());
652     out->minorVersion = 0;
653   }
654 
655   return true;
656 }
657 
Parse(StringPiece str,ConfigDescription * out)658 bool ConfigDescription::Parse(StringPiece str, ConfigDescription* out) {
659   std::vector<std::string> parts = util::SplitAndLowercase(str, '-');
660 
661   ConfigDescription config;
662   ssize_t parts_consumed = 0;
663   LocaleValue locale;
664 
665   const auto parts_end = parts.end();
666   auto part_iter = parts.begin();
667 
668   if (str.size() == 0) {
669     goto success;
670   }
671 
672   if (parseMcc(part_iter->c_str(), &config)) {
673     ++part_iter;
674     if (part_iter == parts_end) {
675       goto success;
676     }
677   }
678 
679   if (parseMnc(part_iter->c_str(), &config)) {
680     ++part_iter;
681     if (part_iter == parts_end) {
682       goto success;
683     }
684   }
685 
686   // Locale spans a few '-' separators, so we let it
687   // control the index.
688   parts_consumed = locale.InitFromParts(part_iter, parts_end);
689   if (parts_consumed < 0) {
690     return false;
691   } else {
692     locale.WriteTo(&config);
693     part_iter += parts_consumed;
694     if (part_iter == parts_end) {
695       goto success;
696     }
697   }
698 
699   if (parseGrammaticalInflection(*part_iter, &config)) {
700     ++part_iter;
701     if (part_iter == parts_end) {
702       goto success;
703     }
704   }
705 
706   if (parseLayoutDirection(part_iter->c_str(), &config)) {
707     ++part_iter;
708     if (part_iter == parts_end) {
709       goto success;
710     }
711   }
712 
713   if (parseSmallestScreenWidthDp(part_iter->c_str(), &config)) {
714     ++part_iter;
715     if (part_iter == parts_end) {
716       goto success;
717     }
718   }
719 
720   if (parseScreenWidthDp(part_iter->c_str(), &config)) {
721     ++part_iter;
722     if (part_iter == parts_end) {
723       goto success;
724     }
725   }
726 
727   if (parseScreenHeightDp(part_iter->c_str(), &config)) {
728     ++part_iter;
729     if (part_iter == parts_end) {
730       goto success;
731     }
732   }
733 
734   if (parseScreenLayoutSize(part_iter->c_str(), &config)) {
735     ++part_iter;
736     if (part_iter == parts_end) {
737       goto success;
738     }
739   }
740 
741   if (parseScreenLayoutLong(part_iter->c_str(), &config)) {
742     ++part_iter;
743     if (part_iter == parts_end) {
744       goto success;
745     }
746   }
747 
748   if (parseScreenRound(part_iter->c_str(), &config)) {
749     ++part_iter;
750     if (part_iter == parts_end) {
751       goto success;
752     }
753   }
754 
755   if (parseWideColorGamut(part_iter->c_str(), &config)) {
756     ++part_iter;
757     if (part_iter == parts_end) {
758       goto success;
759     }
760   }
761 
762   if (parseHdr(part_iter->c_str(), &config)) {
763     ++part_iter;
764     if (part_iter == parts_end) {
765       goto success;
766     }
767   }
768 
769   if (parseOrientation(part_iter->c_str(), &config)) {
770     ++part_iter;
771     if (part_iter == parts_end) {
772       goto success;
773     }
774   }
775 
776   if (parseUiModeType(part_iter->c_str(), &config)) {
777     ++part_iter;
778     if (part_iter == parts_end) {
779       goto success;
780     }
781   }
782 
783   if (parseUiModeNight(part_iter->c_str(), &config)) {
784     ++part_iter;
785     if (part_iter == parts_end) {
786       goto success;
787     }
788   }
789 
790   if (parseDensity(part_iter->c_str(), &config)) {
791     ++part_iter;
792     if (part_iter == parts_end) {
793       goto success;
794     }
795   }
796 
797   if (parseTouchscreen(part_iter->c_str(), &config)) {
798     ++part_iter;
799     if (part_iter == parts_end) {
800       goto success;
801     }
802   }
803 
804   if (parseKeysHidden(part_iter->c_str(), &config)) {
805     ++part_iter;
806     if (part_iter == parts_end) {
807       goto success;
808     }
809   }
810 
811   if (parseKeyboard(part_iter->c_str(), &config)) {
812     ++part_iter;
813     if (part_iter == parts_end) {
814       goto success;
815     }
816   }
817 
818   if (parseNavHidden(part_iter->c_str(), &config)) {
819     ++part_iter;
820     if (part_iter == parts_end) {
821       goto success;
822     }
823   }
824 
825   if (parseNavigation(part_iter->c_str(), &config)) {
826     ++part_iter;
827     if (part_iter == parts_end) {
828       goto success;
829     }
830   }
831 
832   if (parseScreenSize(part_iter->c_str(), &config)) {
833     ++part_iter;
834     if (part_iter == parts_end) {
835       goto success;
836     }
837   }
838 
839   if (parseVersion(part_iter->c_str(), &config)) {
840     ++part_iter;
841     if (part_iter == parts_end) {
842       goto success;
843     }
844   }
845 
846   // Unrecognized.
847   return false;
848 
849 success:
850   if (out != NULL) {
851     ApplyVersionForCompatibility(&config);
852     *out = config;
853   }
854   return true;
855 }
856 
ApplyVersionForCompatibility(ConfigDescription * config)857 void ConfigDescription::ApplyVersionForCompatibility(
858     ConfigDescription* config) {
859   uint16_t min_sdk = 0;
860   if (config->grammaticalInflection != 0) {
861     min_sdk = SDK_U;
862   } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
863                 == ResTable_config::UI_MODE_TYPE_VR_HEADSET ||
864             config->colorMode & ResTable_config::MASK_WIDE_COLOR_GAMUT ||
865             config->colorMode & ResTable_config::MASK_HDR) {
866     min_sdk = SDK_O;
867   } else if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
868     min_sdk = SDK_MARSHMALLOW;
869   } else if (config->density == ResTable_config::DENSITY_ANY) {
870     min_sdk = SDK_LOLLIPOP;
871   } else if (config->smallestScreenWidthDp !=
872                  ResTable_config::SCREENWIDTH_ANY ||
873              config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY ||
874              config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
875     min_sdk = SDK_HONEYCOMB_MR2;
876   } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) !=
877                  ResTable_config::UI_MODE_TYPE_ANY ||
878              (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT) !=
879                  ResTable_config::UI_MODE_NIGHT_ANY) {
880     min_sdk = SDK_FROYO;
881   } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE) !=
882                  ResTable_config::SCREENSIZE_ANY ||
883              (config->screenLayout & ResTable_config::MASK_SCREENLONG) !=
884                  ResTable_config::SCREENLONG_ANY ||
885              config->density != ResTable_config::DENSITY_DEFAULT) {
886     min_sdk = SDK_DONUT;
887   }
888 
889   if (min_sdk > config->sdkVersion) {
890     config->sdkVersion = min_sdk;
891   }
892 }
893 
CopyWithoutSdkVersion() const894 ConfigDescription ConfigDescription::CopyWithoutSdkVersion() const {
895   ConfigDescription copy = *this;
896   copy.sdkVersion = 0;
897   return copy;
898 }
899 
GetBcp47LanguageTag(bool canonicalize) const900 std::string ConfigDescription::GetBcp47LanguageTag(bool canonicalize) const {
901   char locale[RESTABLE_MAX_LOCALE_LEN];
902   getBcp47Locale(locale, canonicalize);
903   return std::string(locale);
904 }
905 
to_string() const906 std::string ConfigDescription::to_string() const {
907   const String8 str = toString();
908   return std::string(str.c_str(), str.size());
909 }
910 
Dominates(const ConfigDescription & o) const911 bool ConfigDescription::Dominates(const ConfigDescription& o) const {
912   if (*this == o) {
913     return true;
914   }
915 
916   // Locale de-duping is not-trivial, disable for now (b/62409213).
917   // We must also disable de-duping for all configuration qualifiers with precedence higher than
918   // locale (b/171892595)
919   if (diff(o) & (CONFIG_LOCALE | CONFIG_MCC | CONFIG_MNC)) {
920     return false;
921   }
922 
923   if (*this == DefaultConfig()) {
924     return true;
925   }
926 
927   return MatchWithDensity(o) && !o.MatchWithDensity(*this) &&
928          !isMoreSpecificThan(o) && !o.HasHigherPrecedenceThan(*this);
929 }
930 
HasHigherPrecedenceThan(const ConfigDescription & o) const931 bool ConfigDescription::HasHigherPrecedenceThan(
932     const ConfigDescription& o) const {
933   // The order of the following tests defines the importance of one
934   // configuration parameter over another. Those tests first are more
935   // important, trumping any values in those following them.
936   // The ordering should be the same as ResTable_config#isBetterThan.
937   if (mcc || o.mcc) return (!o.mcc);
938   if (mnc || o.mnc) return (!o.mnc);
939   if (language[0] || o.language[0]) return (!o.language[0]);
940   if (country[0] || o.country[0]) return (!o.country[0]);
941   // Script and variant require either a language or country, both of which
942   // have higher precedence.
943   if (grammaticalInflection || o.grammaticalInflection) return !o.grammaticalInflection;
944   if ((screenLayout | o.screenLayout) & MASK_LAYOUTDIR) {
945     return !(o.screenLayout & MASK_LAYOUTDIR);
946   }
947   if (smallestScreenWidthDp || o.smallestScreenWidthDp)
948     return (!o.smallestScreenWidthDp);
949   if (screenWidthDp || o.screenWidthDp) return (!o.screenWidthDp);
950   if (screenHeightDp || o.screenHeightDp) return (!o.screenHeightDp);
951   if ((screenLayout | o.screenLayout) & MASK_SCREENSIZE) {
952     return !(o.screenLayout & MASK_SCREENSIZE);
953   }
954   if ((screenLayout | o.screenLayout) & MASK_SCREENLONG) {
955     return !(o.screenLayout & MASK_SCREENLONG);
956   }
957   if ((screenLayout2 | o.screenLayout2) & MASK_SCREENROUND) {
958     return !(o.screenLayout2 & MASK_SCREENROUND);
959   }
960   if ((colorMode | o.colorMode) & MASK_HDR) {
961     return !(o.colorMode & MASK_HDR);
962   }
963   if ((colorMode | o.colorMode) & MASK_WIDE_COLOR_GAMUT) {
964     return !(o.colorMode & MASK_WIDE_COLOR_GAMUT);
965   }
966   if (orientation || o.orientation) return (!o.orientation);
967   if ((uiMode | o.uiMode) & MASK_UI_MODE_TYPE) {
968     return !(o.uiMode & MASK_UI_MODE_TYPE);
969   }
970   if ((uiMode | o.uiMode) & MASK_UI_MODE_NIGHT) {
971     return !(o.uiMode & MASK_UI_MODE_NIGHT);
972   }
973   if (density || o.density) return (!o.density);
974   if (touchscreen || o.touchscreen) return (!o.touchscreen);
975   if ((inputFlags | o.inputFlags) & MASK_KEYSHIDDEN) {
976     return !(o.inputFlags & MASK_KEYSHIDDEN);
977   }
978   if ((inputFlags | o.inputFlags) & MASK_NAVHIDDEN) {
979     return !(o.inputFlags & MASK_NAVHIDDEN);
980   }
981   if (keyboard || o.keyboard) return (!o.keyboard);
982   if (navigation || o.navigation) return (!o.navigation);
983   if (screenWidth || o.screenWidth) return (!o.screenWidth);
984   if (screenHeight || o.screenHeight) return (!o.screenHeight);
985   if (sdkVersion || o.sdkVersion) return (!o.sdkVersion);
986   if (minorVersion || o.minorVersion) return (!o.minorVersion);
987   // Both configurations have nothing defined except some possible future
988   // value. Returning the comparison of the two configurations is a
989   // "best effort" at this point to protect against incorrect dominations.
990   return *this != o;
991 }
992 
ConflictsWith(const ConfigDescription & o) const993 bool ConfigDescription::ConflictsWith(const ConfigDescription& o) const {
994   // This method should be updated as new configuration parameters are
995   // introduced (e.g. screenConfig2).
996   auto pred = [](const uint32_t a, const uint32_t b) -> bool {
997     return a == 0 || b == 0 || a == b;
998   };
999   // The values here can be found in ResTable_config#match. Density and range
1000   // values can't lead to conflicts, and are ignored.
1001   return !pred(mcc, o.mcc) || !pred(mnc, o.mnc) || !pred(locale, o.locale) ||
1002          !pred(grammaticalInflection, o.grammaticalInflection) ||
1003          !pred(screenLayout & MASK_LAYOUTDIR,
1004                o.screenLayout & MASK_LAYOUTDIR) ||
1005          !pred(screenLayout & MASK_SCREENLONG,
1006                o.screenLayout & MASK_SCREENLONG) ||
1007          !pred(uiMode & MASK_UI_MODE_TYPE, o.uiMode & MASK_UI_MODE_TYPE) ||
1008          !pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT) ||
1009          !pred(screenLayout2 & MASK_SCREENROUND,
1010                o.screenLayout2 & MASK_SCREENROUND) ||
1011          !pred(colorMode & MASK_HDR, o.colorMode & MASK_HDR) ||
1012          !pred(colorMode & MASK_WIDE_COLOR_GAMUT,
1013                o.colorMode & MASK_WIDE_COLOR_GAMUT) ||
1014          !pred(orientation, o.orientation) ||
1015          !pred(touchscreen, o.touchscreen) ||
1016          !pred(inputFlags & MASK_KEYSHIDDEN, o.inputFlags & MASK_KEYSHIDDEN) ||
1017          !pred(inputFlags & MASK_NAVHIDDEN, o.inputFlags & MASK_NAVHIDDEN) ||
1018          !pred(keyboard, o.keyboard) || !pred(navigation, o.navigation);
1019 }
1020 
IsCompatibleWith(const ConfigDescription & o) const1021 bool ConfigDescription::IsCompatibleWith(const ConfigDescription& o) const {
1022   return !ConflictsWith(o) && !Dominates(o) && !o.Dominates(*this);
1023 }
1024 
1025 }  // namespace android
1026