• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.res.android;
2 
3 import static java.nio.charset.StandardCharsets.US_ASCII;
4 import static java.nio.charset.StandardCharsets.UTF_8;
5 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_DENSITY_ANY;
6 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_DENSITY_DEFAULT;
7 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_DENSITY_HIGH;
8 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_DENSITY_LOW;
9 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_DENSITY_MEDIUM;
10 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_DENSITY_NONE;
11 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_DENSITY_TV;
12 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_DENSITY_XHIGH;
13 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_DENSITY_XXHIGH;
14 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_DENSITY_XXXHIGH;
15 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_HDR_ANY;
16 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_HDR_NO;
17 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_HDR_YES;
18 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_KEYBOARD_ANY;
19 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_KEYSHIDDEN_ANY;
20 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_KEYSHIDDEN_NO;
21 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_KEYSHIDDEN_SOFT;
22 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_KEYSHIDDEN_YES;
23 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_LAYOUTDIR_ANY;
24 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_LAYOUTDIR_LTR;
25 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_LAYOUTDIR_RTL;
26 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_NAVHIDDEN_ANY;
27 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_NAVHIDDEN_NO;
28 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_NAVHIDDEN_YES;
29 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_NAVIGATION_ANY;
30 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_ORIENTATION_ANY;
31 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_ORIENTATION_LAND;
32 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_ORIENTATION_PORT;
33 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_ORIENTATION_SQUARE;
34 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENLONG_ANY;
35 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENLONG_NO;
36 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENLONG_YES;
37 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENROUND_ANY;
38 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENROUND_NO;
39 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENROUND_YES;
40 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENSIZE_ANY;
41 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENSIZE_LARGE;
42 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENSIZE_NORMAL;
43 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENSIZE_SMALL;
44 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_SCREENSIZE_XLARGE;
45 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_TOUCHSCREEN_ANY;
46 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_UI_MODE_NIGHT_ANY;
47 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_UI_MODE_TYPE_ANY;
48 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_UI_MODE_TYPE_NORMAL;
49 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_WIDE_COLOR_GAMUT_ANY;
50 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_WIDE_COLOR_GAMUT_NO;
51 import static org.robolectric.res.android.AConfiguration.ACONFIGURATION_WIDE_COLOR_GAMUT_YES;
52 import static org.robolectric.res.android.LocaleData.localeDataCompareRegions;
53 import static org.robolectric.res.android.LocaleData.localeDataComputeScript;
54 import static org.robolectric.res.android.LocaleData.localeDataIsCloseToUsEnglish;
55 import static org.robolectric.res.android.ResTable.kDebugTableSuperNoisy;
56 import static org.robolectric.res.android.Util.ALOGI;
57 import static org.robolectric.res.android.Util.dtohl;
58 import static org.robolectric.res.android.Util.dtohs;
59 import static org.robolectric.res.android.Util.isTruthy;
60 
61 import com.google.common.base.Charsets;
62 import com.google.common.base.Joiner;
63 import com.google.common.base.Preconditions;
64 import com.google.common.primitives.Bytes;
65 import com.google.common.primitives.UnsignedBytes;
66 import java.nio.ByteBuffer;
67 import java.util.Arrays;
68 import java.util.Collection;
69 import java.util.Collections;
70 import java.util.HashMap;
71 import java.util.LinkedHashMap;
72 import java.util.Map;
73 import javax.annotation.Nonnull;
74 
75 /**
76  * Describes a particular resource configuration.
77  *
78  * Transliterated from:
79  * * https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ResourceTypes.cpp
80  * * https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/include/ResourceTypes.h (struct ResTable_config)
81  *
82  * Changes from 8.0.0_r4 partially applied.
83  */
84 @SuppressWarnings("NewApi")
85 public class ResTable_config {
86 
87   // The most specific locale can consist of:
88   //
89   // - a 3 char language code
90   // - a 3 char region code prefixed by a 'r'
91   // - a 4 char script code prefixed by a 's'
92   // - a 8 char variant code prefixed by a 'v'
93   //
94   // each separated by a single char separator, which sums up to a total of 24
95   // chars, (25 include the string terminator) rounded up to 28 to be 4 byte
96   // aligned.
97   public static final int RESTABLE_MAX_LOCALE_LEN = 28;
98 
99   /** The minimum size in bytes that this configuration must be to contain screen config info. */
100   private static final int SCREEN_CONFIG_MIN_SIZE = 32;
101 
102   /** The minimum size in bytes that this configuration must be to contain screen dp info. */
103   private static final int SCREEN_DP_MIN_SIZE = 36;
104 
105   /** The minimum size in bytes that this configuration must be to contain locale info. */
106   private static final int LOCALE_MIN_SIZE = 48;
107 
108   /** The minimum size in bytes that this config must be to contain the screenConfig extension. */
109   private static final int SCREEN_CONFIG_EXTENSION_MIN_SIZE = 52;
110 
111   public static final int SIZEOF = SCREEN_CONFIG_EXTENSION_MIN_SIZE;
112 
113   // Codes for specially handled languages and regions
114   static final byte[] kEnglish = new byte[] {'e', 'n'};  // packed version of "en"
115   static final byte[] kUnitedStates = new byte[] {'U', 'S'};  // packed version of "US"
116   static final byte[] kFilipino = new byte[] {(byte)0xAD, 0x05};  // packed version of "fil" ported from C {'\xAD', '\x05'}
117   static final byte[] kTagalog = new byte[] {'t', 'l'};  // packed version of "tl"
118 
createConfig(ByteBuffer buffer)119   static ResTable_config createConfig(ByteBuffer buffer) {
120     int startPosition = buffer.position();  // The starting buffer position to calculate bytes read.
121     int size = buffer.getInt();
122     int mcc = buffer.getShort() & 0xFFFF;
123     int mnc = buffer.getShort() & 0xFFFF;
124     byte[] language = new byte[2];
125     buffer.get(language);
126     byte[] region = new byte[2];
127     buffer.get(region);
128     int orientation = UnsignedBytes.toInt(buffer.get());
129     int touchscreen = UnsignedBytes.toInt(buffer.get());
130     int density = buffer.getShort() & 0xFFFF;
131     int keyboard = UnsignedBytes.toInt(buffer.get());
132     int navigation = UnsignedBytes.toInt(buffer.get());
133     int inputFlags = UnsignedBytes.toInt(buffer.get());
134     buffer.get();  // 1 byte of padding
135     int screenWidth = buffer.getShort() & 0xFFFF;
136     int screenHeight = buffer.getShort() & 0xFFFF;
137     int sdkVersion = buffer.getShort() & 0xFFFF;
138     int minorVersion = buffer.getShort() & 0xFFFF;
139 
140     // At this point, the configuration's size needs to be taken into account as not all
141     // configurations have all values.
142     int screenLayout = 0;
143     int uiMode = 0;
144     int smallestScreenWidthDp = 0;
145     int screenWidthDp = 0;
146     int screenHeightDp = 0;
147     byte[] localeScript = new byte[4];
148     byte[] localeVariant = new byte[8];
149     byte screenLayout2 = 0;
150     byte screenConfigPad1 = 0;
151     short screenConfigPad2 = 0;
152 
153     if (size >= SCREEN_CONFIG_MIN_SIZE) {
154       screenLayout = UnsignedBytes.toInt(buffer.get());
155       uiMode = UnsignedBytes.toInt(buffer.get());
156       smallestScreenWidthDp = buffer.getShort() & 0xFFFF;
157     }
158 
159     if (size >= SCREEN_DP_MIN_SIZE) {
160       screenWidthDp = buffer.getShort() & 0xFFFF;
161       screenHeightDp = buffer.getShort() & 0xFFFF;
162     }
163 
164     if (size >= LOCALE_MIN_SIZE) {
165       buffer.get(localeScript);
166       buffer.get(localeVariant);
167     }
168 
169     if (size >= SCREEN_CONFIG_EXTENSION_MIN_SIZE) {
170       screenLayout2 = (byte) UnsignedBytes.toInt(buffer.get());
171       screenConfigPad1 = buffer.get();  // Reserved padding
172       screenConfigPad2 = buffer.getShort();  // More reserved padding
173     }
174 
175     // After parsing everything that's known, account for anything that's unknown.
176     int bytesRead = buffer.position() - startPosition;
177     byte[] unknown = new byte[size - bytesRead];
178     buffer.get(unknown);
179 
180     return new ResTable_config(size, mcc, mnc, language, region, orientation,
181         touchscreen, density, keyboard, navigation, inputFlags, screenWidth, screenHeight,
182         sdkVersion, minorVersion, screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
183         screenHeightDp, localeScript, localeVariant, screenLayout2, screenConfigPad1, screenConfigPad2, unknown);
184   }
185 
186   /**
187    * The different types of configs that can be present in a {@link ResTable_config}.
188    *
189    * The ordering of these types is roughly the same as {@code #isBetterThan}, but is not
190    * guaranteed to be the same.
191    */
192   public enum Type {
193     MCC,
194     MNC,
195     LANGUAGE_STRING,
196     LOCALE_SCRIPT_STRING,
197     REGION_STRING,
198     LOCALE_VARIANT_STRING,
199     SCREEN_LAYOUT_DIRECTION,
200     SMALLEST_SCREEN_WIDTH_DP,
201     SCREEN_WIDTH_DP,
202     SCREEN_HEIGHT_DP,
203     SCREEN_LAYOUT_SIZE,
204     SCREEN_LAYOUT_LONG,
205     SCREEN_LAYOUT_ROUND,
206     COLOR_MODE_WIDE_COLOR_GAMUT, // NB: COLOR_GAMUT takes priority over HDR in #isBetterThan.
207     COLOR_MODE_HDR,
208     ORIENTATION,
209     UI_MODE_TYPE,
210     UI_MODE_NIGHT,
211     DENSITY_DPI,
212     TOUCHSCREEN,
213     KEYBOARD_HIDDEN,
214     KEYBOARD,
215     NAVIGATION_HIDDEN,
216     NAVIGATION,
217     SCREEN_SIZE,
218     SDK_VERSION
219   }
220 
221   // screenLayout bits for layout direction.
222 //  public static final int MASK_LAYOUTDIR = 0xC0;
223   public static final int SHIFT_LAYOUTDIR = 6;
224   public static final int LAYOUTDIR_ANY = ACONFIGURATION_LAYOUTDIR_ANY << SHIFT_LAYOUTDIR;
225   public static final int LAYOUTDIR_LTR = ACONFIGURATION_LAYOUTDIR_LTR << SHIFT_LAYOUTDIR;
226   public static final int LAYOUTDIR_RTL = ACONFIGURATION_LAYOUTDIR_RTL << SHIFT_LAYOUTDIR;
227 
228   public static final int SCREENWIDTH_ANY = 0;
229 //  public static final int MASK_SCREENSIZE = 0x0f;
230   public static final int SCREENSIZE_ANY = ACONFIGURATION_SCREENSIZE_ANY;
231   public static final int SCREENSIZE_SMALL = ACONFIGURATION_SCREENSIZE_SMALL;
232   public static final int SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL;
233   public static final int SCREENSIZE_LARGE = ACONFIGURATION_SCREENSIZE_LARGE;
234   public static final int SCREENSIZE_XLARGE = ACONFIGURATION_SCREENSIZE_XLARGE;
235 
236   // uiMode bits for the mode type.
237   public static final int MASK_UI_MODE_TYPE = 0x0f;
238   public static final int UI_MODE_TYPE_ANY = ACONFIGURATION_UI_MODE_TYPE_ANY;
239   public static final int UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL;
240 
241   // uiMode bits for the night switch;
242   public static final int MASK_UI_MODE_NIGHT = 0x30;
243   public static final int SHIFT_UI_MODE_NIGHT = 4;
244   public static final int UI_MODE_NIGHT_ANY = ACONFIGURATION_UI_MODE_NIGHT_ANY << SHIFT_UI_MODE_NIGHT;
245 
246   public static final int DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT;
247   public static final int DENSITY_LOW = ACONFIGURATION_DENSITY_LOW;
248   public static final int DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM;
249   public static final int DENSITY_TV = ACONFIGURATION_DENSITY_TV;
250   public static final int DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH;
251   public static final int DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH;
252   public static final int DENSITY_XXHIGH = ACONFIGURATION_DENSITY_XXHIGH;
253   public static final int DENSITY_XXXHIGH = ACONFIGURATION_DENSITY_XXXHIGH;
254   public static final int DENSITY_ANY = ACONFIGURATION_DENSITY_ANY;
255   public static final int DENSITY_NONE = ACONFIGURATION_DENSITY_NONE;
256 
257   public static final int TOUCHSCREEN_ANY  = ACONFIGURATION_TOUCHSCREEN_ANY;
258 
259   public static final int MASK_KEYSHIDDEN = 0x0003;
260   public static final byte KEYSHIDDEN_ANY = ACONFIGURATION_KEYSHIDDEN_ANY;
261   public static final byte KEYSHIDDEN_NO = ACONFIGURATION_KEYSHIDDEN_NO;
262   public static final byte KEYSHIDDEN_YES = ACONFIGURATION_KEYSHIDDEN_YES;
263   public static final byte KEYSHIDDEN_SOFT = ACONFIGURATION_KEYSHIDDEN_SOFT;
264 
265   public static final int KEYBOARD_ANY  = ACONFIGURATION_KEYBOARD_ANY;
266 
267   public static final int MASK_NAVHIDDEN = 0x000c;
268   public static final int SHIFT_NAVHIDDEN = 2;
269   public static final byte NAVHIDDEN_ANY = ACONFIGURATION_NAVHIDDEN_ANY << SHIFT_NAVHIDDEN;
270   public static final byte NAVHIDDEN_NO = ACONFIGURATION_NAVHIDDEN_NO << SHIFT_NAVHIDDEN;
271   public static final byte NAVHIDDEN_YES = ACONFIGURATION_NAVHIDDEN_YES << SHIFT_NAVHIDDEN;
272 
273   public static final int NAVIGATION_ANY  = ACONFIGURATION_NAVIGATION_ANY;
274 
275   public static final int SCREENHEIGHT_ANY = 0;
276 
277   public static final int SDKVERSION_ANY = 0;
278   public static final int MINORVERSION_ANY = 0;
279 
280   // from https://github.com/google/android-arscblamer/blob/master/java/com/google/devrel/gmscore/tools/apk/arsc/ResourceConfiguration.java
281   /** The below constants are from android.content.res.Configuration. */
282   static final int COLOR_MODE_WIDE_COLOR_GAMUT_MASK = 0x03;
283 
284   public static final int WIDE_COLOR_GAMUT_ANY = ACONFIGURATION_WIDE_COLOR_GAMUT_ANY;
285   public static final int WIDE_COLOR_GAMUT_NO = ACONFIGURATION_WIDE_COLOR_GAMUT_NO;
286   public static final int WIDE_COLOR_GAMUT_YES = ACONFIGURATION_WIDE_COLOR_GAMUT_YES;
287   public static final int MASK_WIDE_COLOR_GAMUT = 0x03;
288   static final int COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED = 0;
289   static final int COLOR_MODE_WIDE_COLOR_GAMUT_NO = 0x01;
290   static final int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 0x02;
291 
292   private static final Map<Integer, String> COLOR_MODE_WIDE_COLOR_GAMUT_VALUES;
293 
294   static {
295     Map<Integer, String> map = new HashMap<>();
map.put(COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED, "")296     map.put(COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED, "");
map.put(COLOR_MODE_WIDE_COLOR_GAMUT_NO, "nowidecg")297     map.put(COLOR_MODE_WIDE_COLOR_GAMUT_NO, "nowidecg");
map.put(COLOR_MODE_WIDE_COLOR_GAMUT_YES, "widecg")298     map.put(COLOR_MODE_WIDE_COLOR_GAMUT_YES, "widecg");
299     COLOR_MODE_WIDE_COLOR_GAMUT_VALUES = Collections.unmodifiableMap(map);
300   }
301 
302   public static final int HDR_ANY = ACONFIGURATION_HDR_ANY;
303   public static final int HDR_NO = ACONFIGURATION_HDR_NO << 2;
304   public static final int HDR_YES = ACONFIGURATION_HDR_YES << 2;
305   public static final int MASK_HDR = 0x0c;
306   static final int COLOR_MODE_HDR_MASK = 0x0C;
307   static final int COLOR_MODE_HDR_UNDEFINED = 0;
308   static final int COLOR_MODE_HDR_NO = 0x04;
309   static final int COLOR_MODE_HDR_YES = 0x08;
310 
311   private static final Map<Integer, String> COLOR_MODE_HDR_VALUES;
312 
313   static {
314     Map<Integer, String> map = new HashMap<>();
map.put(COLOR_MODE_HDR_UNDEFINED, "")315     map.put(COLOR_MODE_HDR_UNDEFINED, "");
map.put(COLOR_MODE_HDR_NO, "lowdr")316     map.put(COLOR_MODE_HDR_NO, "lowdr");
map.put(COLOR_MODE_HDR_YES, "highdr")317     map.put(COLOR_MODE_HDR_YES, "highdr");
318     COLOR_MODE_HDR_VALUES = Collections.unmodifiableMap(map);
319   }
320 
321   public static final int DENSITY_DPI_UNDEFINED = 0;
322   static final int DENSITY_DPI_LDPI = 120;
323   public static final int DENSITY_DPI_MDPI = 160;
324   static final int DENSITY_DPI_TVDPI = 213;
325   static final int DENSITY_DPI_HDPI = 240;
326   static final int DENSITY_DPI_XHDPI = 320;
327   static final int DENSITY_DPI_XXHDPI = 480;
328   static final int DENSITY_DPI_XXXHDPI = 640;
329   public static final int DENSITY_DPI_ANY  = 0xFFFE;
330   public static final int DENSITY_DPI_NONE = 0xFFFF;
331 
332   private static final Map<Integer, String> DENSITY_DPI_VALUES;
333 
334   static {
335     Map<Integer, String> map = new HashMap<>();
map.put(DENSITY_DPI_UNDEFINED, "")336     map.put(DENSITY_DPI_UNDEFINED, "");
map.put(DENSITY_DPI_LDPI, "ldpi")337     map.put(DENSITY_DPI_LDPI, "ldpi");
map.put(DENSITY_DPI_MDPI, "mdpi")338     map.put(DENSITY_DPI_MDPI, "mdpi");
map.put(DENSITY_DPI_TVDPI, "tvdpi")339     map.put(DENSITY_DPI_TVDPI, "tvdpi");
map.put(DENSITY_DPI_HDPI, "hdpi")340     map.put(DENSITY_DPI_HDPI, "hdpi");
map.put(DENSITY_DPI_XHDPI, "xhdpi")341     map.put(DENSITY_DPI_XHDPI, "xhdpi");
map.put(DENSITY_DPI_XXHDPI, "xxhdpi")342     map.put(DENSITY_DPI_XXHDPI, "xxhdpi");
map.put(DENSITY_DPI_XXXHDPI, "xxxhdpi")343     map.put(DENSITY_DPI_XXXHDPI, "xxxhdpi");
map.put(DENSITY_DPI_ANY, "anydpi")344     map.put(DENSITY_DPI_ANY, "anydpi");
map.put(DENSITY_DPI_NONE, "nodpi")345     map.put(DENSITY_DPI_NONE, "nodpi");
346     DENSITY_DPI_VALUES = Collections.unmodifiableMap(map);
347   }
348 
349   static final int KEYBOARD_NOKEYS = 1;
350   static final int KEYBOARD_QWERTY = 2;
351   static final int KEYBOARD_12KEY  = 3;
352 
353   private static final Map<Integer, String> KEYBOARD_VALUES;
354 
355   static {
356     Map<Integer, String> map = new HashMap<>();
map.put(KEYBOARD_NOKEYS, "nokeys")357     map.put(KEYBOARD_NOKEYS, "nokeys");
map.put(KEYBOARD_QWERTY, "qwerty")358     map.put(KEYBOARD_QWERTY, "qwerty");
map.put(KEYBOARD_12KEY, "12key")359     map.put(KEYBOARD_12KEY, "12key");
360     KEYBOARD_VALUES = Collections.unmodifiableMap(map);
361   }
362 
363   static final int KEYBOARDHIDDEN_MASK = 0x03;
364   static final int KEYBOARDHIDDEN_NO   = 1;
365   static final int KEYBOARDHIDDEN_YES  = 2;
366   static final int KEYBOARDHIDDEN_SOFT = 3;
367 
368   private static final Map<Integer, String> KEYBOARDHIDDEN_VALUES;
369 
370   static {
371     Map<Integer, String> map = new HashMap<>();
map.put(KEYBOARDHIDDEN_NO, "keysexposed")372     map.put(KEYBOARDHIDDEN_NO, "keysexposed");
map.put(KEYBOARDHIDDEN_YES, "keyshidden")373     map.put(KEYBOARDHIDDEN_YES, "keyshidden");
map.put(KEYBOARDHIDDEN_SOFT, "keyssoft")374     map.put(KEYBOARDHIDDEN_SOFT, "keyssoft");
375     KEYBOARDHIDDEN_VALUES = Collections.unmodifiableMap(map);
376   }
377 
378   static final int NAVIGATION_NONAV     = 1;
379   static final int NAVIGATION_DPAD      = 2;
380   static final int NAVIGATION_TRACKBALL = 3;
381   static final int NAVIGATION_WHEEL     = 4;
382 
383   private static final Map<Integer, String> NAVIGATION_VALUES;
384 
385   static {
386     Map<Integer, String> map = new HashMap<>();
map.put(NAVIGATION_NONAV, "nonav")387     map.put(NAVIGATION_NONAV, "nonav");
map.put(NAVIGATION_DPAD, "dpad")388     map.put(NAVIGATION_DPAD, "dpad");
map.put(NAVIGATION_TRACKBALL, "trackball")389     map.put(NAVIGATION_TRACKBALL, "trackball");
map.put(NAVIGATION_WHEEL, "wheel")390     map.put(NAVIGATION_WHEEL, "wheel");
391     NAVIGATION_VALUES = Collections.unmodifiableMap(map);
392   }
393 
394   static final int NAVIGATIONHIDDEN_MASK  = 0x0C;
395   static final int NAVIGATIONHIDDEN_NO    = 0x04;
396   static final int NAVIGATIONHIDDEN_YES   = 0x08;
397 
398   private static final Map<Integer, String> NAVIGATIONHIDDEN_VALUES;
399 
400   static {
401     Map<Integer, String> map = new HashMap<>();
map.put(NAVIGATIONHIDDEN_NO, "navexposed")402     map.put(NAVIGATIONHIDDEN_NO, "navexposed");
map.put(NAVIGATIONHIDDEN_YES, "navhidden")403     map.put(NAVIGATIONHIDDEN_YES, "navhidden");
404     NAVIGATIONHIDDEN_VALUES = Collections.unmodifiableMap(map);
405   }
406 
407   public static final int ORIENTATION_ANY  = ACONFIGURATION_ORIENTATION_ANY;
408   public static final int ORIENTATION_PORT = ACONFIGURATION_ORIENTATION_PORT;
409   public static final int ORIENTATION_LAND = ACONFIGURATION_ORIENTATION_LAND;
410   public static final int ORIENTATION_SQUARE = ACONFIGURATION_ORIENTATION_SQUARE;
411   static final int ORIENTATION_PORTRAIT  = 0x01;
412   static final int ORIENTATION_LANDSCAPE = 0x02;
413 
414   private static final Map<Integer, String> ORIENTATION_VALUES;
415 
416   static {
417     Map<Integer, String> map = new HashMap<>();
map.put(ORIENTATION_PORTRAIT, "port")418     map.put(ORIENTATION_PORTRAIT, "port");
map.put(ORIENTATION_LANDSCAPE, "land")419     map.put(ORIENTATION_LANDSCAPE, "land");
420     ORIENTATION_VALUES = Collections.unmodifiableMap(map);
421   }
422 
423   static final int SCREENLAYOUT_LAYOUTDIR_MASK = 0xC0;
424   static final int SCREENLAYOUT_LAYOUTDIR_LTR  = 0x40;
425   static final int SCREENLAYOUT_LAYOUTDIR_RTL  = 0x80;
426 
427   private static final Map<Integer, String> SCREENLAYOUT_LAYOUTDIR_VALUES;
428 
429   static {
430     Map<Integer, String> map = new HashMap<>();
map.put(SCREENLAYOUT_LAYOUTDIR_LTR, "ldltr")431     map.put(SCREENLAYOUT_LAYOUTDIR_LTR, "ldltr");
map.put(SCREENLAYOUT_LAYOUTDIR_RTL, "ldrtl")432     map.put(SCREENLAYOUT_LAYOUTDIR_RTL, "ldrtl");
433     SCREENLAYOUT_LAYOUTDIR_VALUES = Collections.unmodifiableMap(map);
434   }
435 
436   // screenLayout bits for wide/long screen variation.
437   public static final int MASK_SCREENLONG = 0x30;
438   public static final int SHIFT_SCREENLONG = 4;
439   public static final int SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG;
440   public static final int SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG;
441   public static final int SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG;
442   static final int SCREENLAYOUT_LONG_MASK = 0x30;
443   static final int SCREENLAYOUT_LONG_NO   = 0x10;
444   static final int SCREENLAYOUT_LONG_YES  = 0x20;
445 
446   private static final Map<Integer, String> SCREENLAYOUT_LONG_VALUES;
447 
448   static {
449     Map<Integer, String> map = new HashMap<>();
map.put(SCREENLAYOUT_LONG_NO, "notlong")450     map.put(SCREENLAYOUT_LONG_NO, "notlong");
map.put(SCREENLAYOUT_LONG_YES, "long")451     map.put(SCREENLAYOUT_LONG_YES, "long");
452     SCREENLAYOUT_LONG_VALUES = Collections.unmodifiableMap(map);
453   }
454 
455   // screenLayout2 bits for round/notround.
456   static final int MASK_SCREENROUND = 0x03;
457   public static final int SCREENROUND_ANY = ACONFIGURATION_SCREENROUND_ANY;
458   public static final int SCREENROUND_NO = ACONFIGURATION_SCREENROUND_NO;
459   public static final int SCREENROUND_YES = ACONFIGURATION_SCREENROUND_YES;
460 
461   static final int SCREENLAYOUT_ROUND_MASK = 0x03;
462   static final int SCREENLAYOUT_ROUND_NO   = 0x01;
463   static final int SCREENLAYOUT_ROUND_YES  = 0x02;
464 
465   private static final Map<Integer, String> SCREENLAYOUT_ROUND_VALUES;
466 
467   static {
468     Map<Integer, String> map = new HashMap<>();
map.put(SCREENLAYOUT_ROUND_NO, "notround")469     map.put(SCREENLAYOUT_ROUND_NO, "notround");
map.put(SCREENLAYOUT_ROUND_YES, "round")470     map.put(SCREENLAYOUT_ROUND_YES, "round");
471     SCREENLAYOUT_ROUND_VALUES = Collections.unmodifiableMap(map);
472   }
473 
474   static final int SCREENLAYOUT_SIZE_MASK   = 0x0F;
475   static final int SCREENLAYOUT_SIZE_SMALL  = 0x01;
476   static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
477   static final int SCREENLAYOUT_SIZE_LARGE  = 0x03;
478   static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
479 
480   private static final Map<Integer, String> SCREENLAYOUT_SIZE_VALUES;
481 
482   static {
483     Map<Integer, String> map = new HashMap<>();
map.put(SCREENLAYOUT_SIZE_SMALL, "small")484     map.put(SCREENLAYOUT_SIZE_SMALL, "small");
map.put(SCREENLAYOUT_SIZE_NORMAL, "normal")485     map.put(SCREENLAYOUT_SIZE_NORMAL, "normal");
map.put(SCREENLAYOUT_SIZE_LARGE, "large")486     map.put(SCREENLAYOUT_SIZE_LARGE, "large");
map.put(SCREENLAYOUT_SIZE_XLARGE, "xlarge")487     map.put(SCREENLAYOUT_SIZE_XLARGE, "xlarge");
488     SCREENLAYOUT_SIZE_VALUES = Collections.unmodifiableMap(map);
489   }
490 
491   static final int TOUCHSCREEN_NOTOUCH = 1;
492   @Deprecated static final int TOUCHSCREEN_STYLUS  = 2;
493   public static final int TOUCHSCREEN_FINGER  = 3;
494 
495   private static final Map<Integer, String> TOUCHSCREEN_VALUES;
496 
497   static {
498     Map<Integer, String> map = new HashMap<>();
map.put(TOUCHSCREEN_NOTOUCH, "notouch")499     map.put(TOUCHSCREEN_NOTOUCH, "notouch");
map.put(TOUCHSCREEN_FINGER, "finger")500     map.put(TOUCHSCREEN_FINGER, "finger");
501     TOUCHSCREEN_VALUES = Collections.unmodifiableMap(map);
502   }
503 
504   static final int UI_MODE_NIGHT_MASK = 0x30;
505   public static final int UI_MODE_NIGHT_NO   = 0x10;
506   static final int UI_MODE_NIGHT_YES  = 0x20;
507 
508   private static final Map<Integer, String> UI_MODE_NIGHT_VALUES;
509 
510   static {
511     Map<Integer, String> map = new HashMap<>();
map.put(UI_MODE_NIGHT_NO, "notnight")512     map.put(UI_MODE_NIGHT_NO, "notnight");
map.put(UI_MODE_NIGHT_YES, "night")513     map.put(UI_MODE_NIGHT_YES, "night");
514     UI_MODE_NIGHT_VALUES = Collections.unmodifiableMap(map);
515   }
516 
517   static final int UI_MODE_TYPE_MASK       = 0x0F;
518   static final int UI_MODE_TYPE_DESK       = 0x02;
519   static final int UI_MODE_TYPE_CAR        = 0x03;
520   static final int UI_MODE_TYPE_TELEVISION = 0x04;
521   static final int UI_MODE_TYPE_APPLIANCE  = 0x05;
522   static final int UI_MODE_TYPE_WATCH      = 0x06;
523   static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
524 
525   private static final Map<Integer, String> UI_MODE_TYPE_VALUES;
526 
527   static {
528     Map<Integer, String> map = new HashMap<>();
map.put(UI_MODE_TYPE_DESK, "desk")529     map.put(UI_MODE_TYPE_DESK, "desk");
map.put(UI_MODE_TYPE_CAR, "car")530     map.put(UI_MODE_TYPE_CAR, "car");
map.put(UI_MODE_TYPE_TELEVISION, "television")531     map.put(UI_MODE_TYPE_TELEVISION, "television");
map.put(UI_MODE_TYPE_APPLIANCE, "appliance")532     map.put(UI_MODE_TYPE_APPLIANCE, "appliance");
map.put(UI_MODE_TYPE_WATCH, "watch")533     map.put(UI_MODE_TYPE_WATCH, "watch");
map.put(UI_MODE_TYPE_VR_HEADSET, "vrheadset")534     map.put(UI_MODE_TYPE_VR_HEADSET, "vrheadset");
535     UI_MODE_TYPE_VALUES = Collections.unmodifiableMap(map);
536   }
537 
538   /** The number of bytes that this resource configuration takes up. */
539   int size;
540 
541   public int mcc;
542   public int mnc;
543 
544   /** Returns a packed 2-byte language code. */
545   @SuppressWarnings("mutable")
546   public final byte[] language;
547 
548   /** Returns {@link #language} as an unpacked string representation. */
549   @Nonnull
languageString()550   public final String languageString() {
551     return unpackLanguage();
552   }
553 
554   /** Returns the {@link #localeScript} as a string. */
localeScriptString()555   public final String localeScriptString() {
556     return byteArrayToString(localeScript);
557   }
558 
559   /** Returns the {@link #localeVariant} as a string. */
localeVariantString()560   public final String localeVariantString() {
561     return byteArrayToString(localeVariant);
562   }
563 
byteArrayToString(byte[] data)564   private String byteArrayToString(byte[] data) {
565     int length = Bytes.indexOf(data, (byte) 0);
566     return new String(data, 0, length >= 0 ? length : data.length, Charsets.US_ASCII);
567   }
568 
569   /** Returns the wide color gamut section of {@link #colorMode}. */
colorModeWideColorGamut()570   public final int colorModeWideColorGamut() {
571     return colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK;
572   }
573 
574   /** Returns the HDR section of {@link #colorMode}. */
colorModeHdr()575   public final int colorModeHdr() {
576     return colorMode & COLOR_MODE_HDR_MASK;
577   }
578 
579   /** Returns a packed 2-byte country code. */
580   @SuppressWarnings("mutable")
581   public final byte[] country;
582 
583   /** Returns {@link #country} as an unpacked string representation. */
584   @Nonnull
regionString()585   public final String regionString() {
586     return unpackRegion();
587   }
588 
scriptString()589   public final String scriptString() {
590     if (localeScript[0] != '\0') {
591       return new String(localeScript, Charsets.UTF_8);
592     } else {
593       return null;
594     }
595   }
596 
597   public int orientation;
598   public int touchscreen;
599   public int density;
600   public int keyboard;
601   public int navigation;
602   public int inputFlags;
603 
keyboardHidden()604   public final int keyboardHidden() {
605     return inputFlags & KEYBOARDHIDDEN_MASK;
606   }
607 
keyboardHidden(int value)608   public final void keyboardHidden(int value) {
609     inputFlags = (inputFlags & ~KEYBOARDHIDDEN_MASK) | value;
610   }
611 
navigationHidden()612   public final int navigationHidden() {
613     return (inputFlags & NAVIGATIONHIDDEN_MASK) >> 2;
614   }
615 
navigationHidden(int value)616   public final void navigationHidden(int value) {
617     inputFlags = (inputFlags & ~NAVIGATIONHIDDEN_MASK) | value;
618   }
619 
620   public int screenWidth;
621   public int screenHeight;
622   public int sdkVersion;
623 
624   /**
625    * Returns a copy of this resource configuration with a different {@link #sdkVersion}, or this
626    * configuration if the {@code sdkVersion} is the same.
627    *
628    * @param sdkVersion The SDK version of the returned configuration.
629    * @return A copy of this configuration with the only difference being #sdkVersion.
630    */
withSdkVersion(int sdkVersion)631   public final ResTable_config withSdkVersion(int sdkVersion) {
632     if (sdkVersion == this.sdkVersion) {
633       return this;
634     }
635     return new ResTable_config(size, mcc, mnc, language, country,
636         orientation, touchscreen, density, keyboard, navigation, inputFlags,
637         screenWidth, screenHeight, sdkVersion, minorVersion, screenLayout, uiMode,
638         smallestScreenWidthDp, screenWidthDp, screenHeightDp, localeScript, localeVariant,
639         screenLayout2, colorMode, screenConfigPad2, unknown);
640   }
641 
ResTable_config(int size, int mcc, int mnc, byte[] language, byte[] country, int orientation, int touchscreen, int density, int keyboard, int navigation, int inputFlags, int screenWidth, int screenHeight, int sdkVersion, int minorVersion, int screenLayout, int uiMode, int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, byte[] localeScript, byte[] localeVariant, byte screenLayout2, byte colorMode, short screenConfigPad2, byte[] unknown)642   public ResTable_config(int size, int mcc, int mnc, byte[] language, byte[] country,
643       int orientation, int touchscreen, int density, int keyboard, int navigation, int inputFlags,
644       int screenWidth, int screenHeight, int sdkVersion, int minorVersion, int screenLayout,
645       int uiMode, int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
646       byte[] localeScript, byte[] localeVariant, byte screenLayout2, byte colorMode,
647       short screenConfigPad2, byte[] unknown) {
648     this.size = size;
649     this.mcc = mcc;
650     this.mnc = mnc;
651     this.language = language;
652     this.country = country;
653     this.orientation = orientation;
654     this.touchscreen = touchscreen;
655     this.density = density;
656     this.keyboard = keyboard;
657     this.navigation = navigation;
658     this.inputFlags = inputFlags;
659     this.screenWidth = screenWidth;
660     this.screenHeight = screenHeight;
661     this.sdkVersion = sdkVersion;
662     this.minorVersion = minorVersion;
663     this.screenLayout = screenLayout;
664     this.uiMode = uiMode;
665     this.smallestScreenWidthDp = smallestScreenWidthDp;
666     this.screenWidthDp = screenWidthDp;
667     this.screenHeightDp = screenHeightDp;
668     this.localeScript = localeScript;
669     this.localeVariant = localeVariant;
670     this.screenLayout2 = screenLayout2;
671     this.colorMode = colorMode;
672     this.screenConfigPad2 = screenConfigPad2;
673     this.unknown = unknown;
674   }
675 
ResTable_config()676   public ResTable_config() {
677     this.language = new byte[2];
678     this.country = new byte[2];
679     this.localeScript = new byte[LocaleData.SCRIPT_LENGTH];
680     this.localeVariant = new byte[8];
681   }
682 
683   public int minorVersion;
684   public int screenLayout;
685 
screenLayoutDirection()686   public final int screenLayoutDirection() {
687     return screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
688   }
689 
screenLayoutDirection(int value)690   public final void screenLayoutDirection(int value) {
691     screenLayout = (screenLayout & ~SCREENLAYOUT_LAYOUTDIR_MASK) | value;
692   }
693 
screenLayoutSize()694   public final int screenLayoutSize() {
695     return screenLayout & SCREENLAYOUT_SIZE_MASK;
696   }
697 
screenLayoutSize(int value)698   public final void screenLayoutSize(int value) {
699     screenLayout = (screenLayout & ~SCREENLAYOUT_SIZE_MASK) | value;
700   }
701 
screenLayoutLong()702   public final int screenLayoutLong() {
703     return screenLayout & SCREENLAYOUT_LONG_MASK;
704   }
705 
screenLayoutLong(int value)706   public final void screenLayoutLong(int value) {
707     screenLayout = (screenLayout & ~SCREENLAYOUT_LONG_MASK) | value;
708   }
709 
screenLayoutRound()710   public final int screenLayoutRound() {
711     return screenLayout2 & SCREENLAYOUT_ROUND_MASK;
712   }
713 
screenLayoutRound(int value)714   public final void screenLayoutRound(int value) {
715     screenLayout2 = (byte) ((screenLayout2 & ~SCREENLAYOUT_ROUND_MASK) | value);
716   }
717 
718   public int uiMode;
719 
uiModeType()720   public final int uiModeType() {
721     return uiMode & UI_MODE_TYPE_MASK;
722   }
723 
uiModeType(int value)724   public final void uiModeType(int value) {
725     uiMode = (uiMode & ~UI_MODE_TYPE_MASK) | value;
726   }
727 
uiModeNight()728   public final int uiModeNight() {
729     return uiMode & UI_MODE_NIGHT_MASK;
730   }
731 
uiModeNight(int value)732   public final void uiModeNight(int value) {
733     uiMode = (uiMode & ~UI_MODE_NIGHT_MASK) | value;
734   }
735 
736   public int smallestScreenWidthDp;
737   public int screenWidthDp;
738   public int screenHeightDp;
739 
740   /** The ISO-15924 short name for the script corresponding to this configuration. */
741   @SuppressWarnings("mutable")
742   public final byte[] localeScript;
743 
744   /** A single BCP-47 variant subtag. */
745   @SuppressWarnings("mutable")
746   public final byte[] localeVariant;
747 
748   /** An extension to {@link #screenLayout}. Contains round/notround qualifier. */
749   public byte screenLayout2;        // Contains round/notround qualifier.
750   public byte colorMode;            // Wide-gamut, HDR, etc.
751   public short screenConfigPad2;    // Reserved padding.
752 
753   /** Any remaining bytes in this resource configuration that are unaccounted for. */
754   @SuppressWarnings("mutable")
755   public byte[] unknown;
756 
757 
758   /**
759    *     // An extension of screenConfig.
760    union {
761    struct {
762    uint8_t screenLayout2;      // Contains round/notround qualifier.
763    uint8_t screenConfigPad1;   // Reserved padding.
764    uint16_t screenConfigPad2;  // Reserved padding.
765    };
766    uint32_t screenConfig2;
767    };
768    */
screenConfig2()769   private int screenConfig2() {
770     return ((screenLayout2 & 0xff) << 24) | ((colorMode * 0xff) << 16) | (screenConfigPad2 & 0xffff);
771   }
772 
773   // If false and localeScript is set, it means that the script of the locale
774   // was explicitly provided.
775   //
776   // If true, it means that localeScript was automatically computed.
777   // localeScript may still not be set in this case, which means that we
778   // tried but could not compute a script.
779   boolean localeScriptWasComputed;
780 
781   // The value of BCP 47 Unicode extension for key 'nu' (numbering system).
782   // Varies in length from 3 to 8 chars. Zero-filled value.
783   byte[] localeNumberingSystem = new byte[8];
784 
785 // --------------------------------------------------------------------
786 // --------------------------------------------------------------------
787 // --------------------------------------------------------------------
788 
789 //  void copyFromDeviceNoSwap(final ResTable_config o) {
790 //    final int size = dtohl(o.size);
791 //    if (size >= sizeof(ResTable_config)) {
792 //        *this = o;
793 //    } else {
794 //      memcpy(this, &o, size);
795 //      memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size);
796 //    }
797 //  }
798 
799   @Nonnull
unpackLanguageOrRegion(byte[] value, int base)800   private String unpackLanguageOrRegion(byte[] value, int base) {
801     Preconditions.checkState(value.length == 2, "Language or country value must be 2 bytes.");
802     if (value[0] == 0 && value[1] == 0) {
803       return "";
804     }
805     if (isTruthy(UnsignedBytes.toInt(value[0]) & 0x80)) {
806       byte[] result = new byte[3];
807       result[0] = (byte) (base + (value[1] & 0x1F));
808       result[1] = (byte) (base + ((value[1] & 0xE0) >>> 5) + ((value[0] & 0x03) << 3));
809       result[2] = (byte) (base + ((value[0] & 0x7C) >>> 2));
810       return new String(result, US_ASCII);
811     }
812     return new String(value, US_ASCII);
813   }
814 
packLanguageOrRegion(final String in, final byte base, final byte[] out)815   /* static */ void packLanguageOrRegion(final String in, final byte base,
816       final byte[] out) {
817     if (in == null) {
818       out[0] = 0;
819       out[1] = 0;
820     } else if (in.length() < 3 || in.charAt(2) == 0 || in.charAt(2) == '-') {
821       out[0] = (byte) in.charAt(0);
822       out[1] = (byte) in.charAt(1);
823     } else {
824       byte first = (byte) ((in.charAt(0) - base) & 0x007f);
825       byte second = (byte) ((in.charAt(1) - base) & 0x007f);
826       byte third = (byte) ((in.charAt(2) - base) & 0x007f);
827 
828       out[0] = (byte) (0x80 | (third << 2) | (second >> 3));
829       out[1] = (byte) ((second << 5) | first);
830     }
831   }
832 
packLanguage(final String language)833   public void packLanguage(final String language) {
834     packLanguageOrRegion(language, (byte) 'a', this.language);
835   }
836 
packRegion(final String region)837   public void packRegion(final String region) {
838     packLanguageOrRegion(region, (byte) '0', this.country);
839   }
840 
841   @Nonnull
unpackLanguage()842   private String unpackLanguage() {
843     return unpackLanguageOrRegion(language, 0x61);
844   }
845 
unpackRegion()846   private String unpackRegion() {
847     return unpackLanguageOrRegion(country, 0x30);
848   }
849 
850 //  void copyFromDtoH(final ResTable_config o) {
851 //    copyFromDeviceNoSwap(o);
852 //    size = sizeof(ResTable_config);
853 //    mcc = dtohs(mcc);
854 //    mnc = dtohs(mnc);
855 //    density = dtohs(density);
856 //    screenWidth = dtohs(screenWidth);
857 //    screenHeight = dtohs(screenHeight);
858 //    sdkVersion = dtohs(sdkVersion);
859 //    minorVersion = dtohs(minorVersion);
860 //    smallestScreenWidthDp = dtohs(smallestScreenWidthDp);
861 //    screenWidthDp = dtohs(screenWidthDp);
862 //    screenHeightDp = dtohs(screenHeightDp);
863 //  }
864 
865 //  void ResTable_config::copyFromDtoH(const ResTable_config& o) {
fromDtoH(final ResTable_config o)866   static ResTable_config fromDtoH(final ResTable_config o) {
867     return new ResTable_config(
868         0 /*sizeof(ResTable_config)*/,
869         dtohs((short) o.mcc),
870         dtohs((short) o.mnc),
871         o.language,
872         o.country,
873         o.orientation,
874         o.touchscreen,
875         dtohl(o.density),
876         o.keyboard,
877         o.navigation,
878         o.inputFlags,
879         dtohs((short) o.screenWidth),
880         dtohs((short) o.screenHeight),
881         dtohs((short) o.sdkVersion),
882         dtohs((short) o.minorVersion),
883         o.screenLayout,
884         o.uiMode,
885         dtohs((short) o.smallestScreenWidthDp),
886         dtohs((short) o.screenWidthDp),
887         dtohs((short) o.screenHeightDp),
888         o.localeScript,
889         o.localeVariant,
890         o.screenLayout2,
891         o.colorMode,
892         o.screenConfigPad2,
893         o.unknown
894     );
895   }
896 
swapHtoD()897   void swapHtoD() {
898 //    size = htodl(size);
899 //    mcc = htods(mcc);
900 //    mnc = htods(mnc);
901 //    density = htods(density);
902 //    screenWidth = htods(screenWidth);
903 //    screenHeight = htods(screenHeight);
904 //    sdkVersion = htods(sdkVersion);
905 //    minorVersion = htods(minorVersion);
906 //    smallestScreenWidthDp = htods(smallestScreenWidthDp);
907 //    screenWidthDp = htods(screenWidthDp);
908 //    screenHeightDp = htods(screenHeightDp);
909   }
910 
compareLocales(final ResTable_config l, final ResTable_config r)911   static final int compareLocales(final ResTable_config l, final ResTable_config r) {
912     if (l.locale() != r.locale()) {
913       // NOTE: This is the old behaviour with respect to comparison orders.
914       // The diff value here doesn't make much sense (given our bit packing scheme)
915       // but it's stable, and that's all we need.
916       return (l.locale() > r.locale()) ? 1 : -1;
917     }
918 
919     // The language & region are equal, so compare the scripts, variants and
920     // numbering systms in this order. Comparison of variants and numbering
921     // systems should happen very infrequently (if at all.)
922     // The comparison code relies on memcmp low-level optimizations that make it
923     // more efficient than strncmp.
924     final byte emptyScript[] = {'\0', '\0', '\0', '\0'};
925     final byte[] lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript;
926     final byte[] rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript;
927 //    int script = memcmp(lScript, rScript);
928 //    if (script) {
929 //      return script;
930 //    }
931     int d = arrayCompare(lScript, rScript);
932     if (d != 0) return d;
933 
934     int variant = arrayCompare(l.localeVariant, r.localeVariant);
935     if (isTruthy(variant)) {
936       return variant;
937     }
938 
939     return arrayCompare(l.localeNumberingSystem, r.localeNumberingSystem);
940   }
941 
arrayCompare(byte[] l, byte[] r)942   private static int arrayCompare(byte[] l, byte[] r) {
943     for (int i = 0; i < l.length; i++) {
944       byte l0 = l[i];
945       byte r0 = r[i];
946       int d = l0 - r0;
947       if (d != 0) return d;
948     }
949     return 0;
950   }
951 
952   // Flags indicating a set of config values.  These flag constants must
953   // match the corresponding ones in android.content.pm.ActivityInfo and
954   // attrs_manifest.xml.
955   private static final int CONFIG_MCC = AConfiguration.ACONFIGURATION_MCC;
956   private static final int CONFIG_MNC = AConfiguration.ACONFIGURATION_MNC;
957   private static final int CONFIG_LOCALE = AConfiguration.ACONFIGURATION_LOCALE;
958   private static final int CONFIG_TOUCHSCREEN = AConfiguration.ACONFIGURATION_TOUCHSCREEN;
959   private static final int CONFIG_KEYBOARD = AConfiguration.ACONFIGURATION_KEYBOARD;
960   private static final int CONFIG_KEYBOARD_HIDDEN = AConfiguration.ACONFIGURATION_KEYBOARD_HIDDEN;
961   private static final int CONFIG_NAVIGATION = AConfiguration.ACONFIGURATION_NAVIGATION;
962   private static final int CONFIG_ORIENTATION = AConfiguration.ACONFIGURATION_ORIENTATION;
963   private static final int CONFIG_DENSITY = AConfiguration.ACONFIGURATION_DENSITY;
964   private static final int CONFIG_SCREEN_SIZE = AConfiguration.ACONFIGURATION_SCREEN_SIZE;
965   private static final int CONFIG_SMALLEST_SCREEN_SIZE = AConfiguration.ACONFIGURATION_SMALLEST_SCREEN_SIZE;
966   private static final int CONFIG_VERSION = AConfiguration.ACONFIGURATION_VERSION;
967   private static final int CONFIG_SCREEN_LAYOUT = AConfiguration.ACONFIGURATION_SCREEN_LAYOUT;
968   private static final int CONFIG_UI_MODE = AConfiguration.ACONFIGURATION_UI_MODE;
969   private static final int CONFIG_LAYOUTDIR = AConfiguration.ACONFIGURATION_LAYOUTDIR;
970   private static final int CONFIG_SCREEN_ROUND = AConfiguration.ACONFIGURATION_SCREEN_ROUND;
971   private static final int CONFIG_COLOR_MODE = AConfiguration.ACONFIGURATION_COLOR_MODE;
972 
973   // Compare two configuration, returning CONFIG_* flags set for each value
974   // that is different.
diff(final ResTable_config o)975   int diff(final ResTable_config o) {
976     int diffs = 0;
977     if (mcc != o.mcc) diffs |= CONFIG_MCC;
978     if (mnc != o.mnc) diffs |= CONFIG_MNC;
979     if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION;
980     if (density != o.density) diffs |= CONFIG_DENSITY;
981     if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN;
982     if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0)
983       diffs |= CONFIG_KEYBOARD_HIDDEN;
984     if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD;
985     if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION;
986     if (screenSize() != o.screenSize()) diffs |= CONFIG_SCREEN_SIZE;
987     if (version() != o.version()) diffs |= CONFIG_VERSION;
988     if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) diffs |= CONFIG_LAYOUTDIR;
989     if ((screenLayout & ~MASK_LAYOUTDIR) != (o.screenLayout & ~MASK_LAYOUTDIR)) diffs |= CONFIG_SCREEN_LAYOUT;
990     if ((screenLayout2 & MASK_SCREENROUND) != (o.screenLayout2 & MASK_SCREENROUND)) diffs |= CONFIG_SCREEN_ROUND;
991     if ((colorMode & MASK_WIDE_COLOR_GAMUT) != (o.colorMode & MASK_WIDE_COLOR_GAMUT)) diffs |= CONFIG_COLOR_MODE;
992     if ((colorMode & MASK_HDR) != (o.colorMode & MASK_HDR)) diffs |= CONFIG_COLOR_MODE;
993     if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE;
994     if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE;
995     if (screenSizeDp() != o.screenSizeDp()) diffs |= CONFIG_SCREEN_SIZE;
996 
997     int diff = compareLocales(this, o);
998     if (isTruthy(diff)) diffs |= CONFIG_LOCALE;
999 
1000     return diffs;
1001   }
1002 
1003   // There isn't a well specified "importance" order between variants and
1004   // scripts. We can't easily tell whether, say "en-Latn-US" is more or less
1005   // specific than "en-US-POSIX".
1006   //
1007   // We therefore arbitrarily decide to give priority to variants over
1008   // scripts since it seems more useful to do so. We will consider
1009   // "en-US-POSIX" to be more specific than "en-Latn-US".
1010   //
1011   // Unicode extension keywords are considered to be less important than
1012   // scripts and variants.
getImportanceScoreOfLocale()1013   int getImportanceScoreOfLocale() {
1014     return (isTruthy(localeVariant[0]) ? 4 : 0)
1015         + (isTruthy(localeScript[0]) && !localeScriptWasComputed ? 2: 0)
1016         + (isTruthy(localeNumberingSystem[0]) ? 1: 0);
1017   }
1018 
compare(final ResTable_config o)1019   int compare(final ResTable_config o) {
1020        if (imsi() != o.imsi()) {
1021        return (imsi() > o.imsi()) ? 1 : -1;
1022    }
1023 
1024    int diff = compareLocales(this, o);
1025    if (diff < 0) {
1026        return -1;
1027    }
1028    if (diff > 0) {
1029        return 1;
1030    }
1031 
1032    if (screenType() != o.screenType()) {
1033        return (screenType() > o.screenType()) ? 1 : -1;
1034    }
1035    if (input() != o.input()) {
1036        return (input() > o.input()) ? 1 : -1;
1037    }
1038    if (screenSize() != o.screenSize()) {
1039        return (screenSize() > o.screenSize()) ? 1 : -1;
1040    }
1041    if (version() != o.version()) {
1042        return (version() > o.version()) ? 1 : -1;
1043    }
1044    if (screenLayout != o.screenLayout) {
1045        return (screenLayout > o.screenLayout) ? 1 : -1;
1046    }
1047    if (screenLayout2 != o.screenLayout2) {
1048        return (screenLayout2 > o.screenLayout2) ? 1 : -1;
1049    }
1050    if (colorMode != o.colorMode) {
1051        return (colorMode > o.colorMode) ? 1 : -1;
1052    }
1053    if (uiMode != o.uiMode) {
1054        return (uiMode > o.uiMode) ? 1 : -1;
1055    }
1056    if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
1057        return (smallestScreenWidthDp > o.smallestScreenWidthDp) ? 1 : -1;
1058    }
1059    if (screenSizeDp() != o.screenSizeDp()) {
1060        return (screenSizeDp() > o.screenSizeDp()) ? 1 : -1;
1061    }
1062    return 0;
1063   }
1064 
1065 
1066   /** Returns true if this is the default "any" configuration. */
isDefault()1067   public final boolean isDefault() {
1068     return mcc == 0
1069         && mnc == 0
1070         && isZeroes(language)
1071         && isZeroes(country)
1072         && orientation == 0
1073         && touchscreen == 0
1074         && density == 0
1075         && keyboard == 0
1076         && navigation == 0
1077         && inputFlags == 0
1078         && screenWidth == 0
1079         && screenHeight == 0
1080         && sdkVersion == 0
1081         && minorVersion == 0
1082         && screenLayout == 0
1083         && uiMode == 0
1084         && smallestScreenWidthDp == 0
1085         && screenWidthDp == 0
1086         && screenHeightDp == 0
1087         && isZeroes(localeScript)
1088         && isZeroes(localeVariant)
1089         && screenLayout2 == 0
1090         && colorMode == 0
1091         ;
1092   }
1093 
isZeroes(byte[] bytes1)1094   private boolean isZeroes(byte[] bytes1) {
1095     for (byte b : bytes1) {
1096       if (b != 0) {
1097         return false;
1098       }
1099     }
1100     return true;
1101   }
1102 
1103   @Override
toString()1104   public final String toString() {
1105     if (isDefault()) {  // Prevent the default configuration from returning the empty string
1106       return "default";
1107     }
1108     Collection<String> parts = toStringParts().values();
1109     parts.removeAll(Collections.singleton(""));
1110     return Joiner.on('-').join(parts);
1111   }
1112 
1113   /**
1114    * Returns a map of the configuration parts for {@link #toString}.
1115    *
1116    * If a configuration part is not defined for this {@link ResTable_config}, its value
1117    * will be the empty string.
1118    */
toStringParts()1119   public final Map<Type, String> toStringParts() {
1120     Map<Type, String> result = new LinkedHashMap<>();  // Preserve order for #toString().
1121     result.put(Type.MCC, mcc != 0 ? "mcc" + mcc : "");
1122     result.put(Type.MNC, mnc != 0 ? "mnc" + mnc : "");
1123     result.put(Type.LANGUAGE_STRING, languageString());
1124     result.put(Type.LOCALE_SCRIPT_STRING, localeScriptString());
1125     result.put(Type.REGION_STRING, !regionString().isEmpty() ? "r" + regionString() : "");
1126     result.put(Type.LOCALE_VARIANT_STRING, localeVariantString());
1127     result.put(Type.SCREEN_LAYOUT_DIRECTION,
1128         getOrDefault(SCREENLAYOUT_LAYOUTDIR_VALUES, screenLayoutDirection(), ""));
1129     result.put(Type.SMALLEST_SCREEN_WIDTH_DP,
1130         smallestScreenWidthDp != 0 ? "sw" + smallestScreenWidthDp + "dp" : "");
1131     result.put(Type.SCREEN_WIDTH_DP, screenWidthDp != 0 ? "w" + screenWidthDp + "dp" : "");
1132     result.put(Type.SCREEN_HEIGHT_DP, screenHeightDp != 0 ? "h" + screenHeightDp + "dp" : "");
1133     result.put(Type.SCREEN_LAYOUT_SIZE,
1134         getOrDefault(SCREENLAYOUT_SIZE_VALUES, screenLayoutSize(), ""));
1135     result.put(Type.SCREEN_LAYOUT_LONG,
1136         getOrDefault(SCREENLAYOUT_LONG_VALUES, screenLayoutLong(), ""));
1137     result.put(Type.SCREEN_LAYOUT_ROUND,
1138         getOrDefault(SCREENLAYOUT_ROUND_VALUES, screenLayoutRound(), ""));
1139     result.put(Type.COLOR_MODE_HDR, getOrDefault(COLOR_MODE_HDR_VALUES, colorModeHdr(), ""));
1140     result.put(
1141         Type.COLOR_MODE_WIDE_COLOR_GAMUT,
1142         getOrDefault(COLOR_MODE_WIDE_COLOR_GAMUT_VALUES, colorModeWideColorGamut(), ""));
1143     result.put(Type.ORIENTATION, getOrDefault(ORIENTATION_VALUES, orientation, ""));
1144     result.put(Type.UI_MODE_TYPE, getOrDefault(UI_MODE_TYPE_VALUES, uiModeType(), ""));
1145     result.put(Type.UI_MODE_NIGHT, getOrDefault(UI_MODE_NIGHT_VALUES, uiModeNight(), ""));
1146     result.put(Type.DENSITY_DPI, getOrDefault(DENSITY_DPI_VALUES, density, density + "dpi"));
1147     result.put(Type.TOUCHSCREEN, getOrDefault(TOUCHSCREEN_VALUES, touchscreen, ""));
1148     result.put(Type.KEYBOARD_HIDDEN, getOrDefault(KEYBOARDHIDDEN_VALUES, keyboardHidden(), ""));
1149     result.put(Type.KEYBOARD, getOrDefault(KEYBOARD_VALUES, keyboard, ""));
1150     result.put(Type.NAVIGATION_HIDDEN,
1151         getOrDefault(NAVIGATIONHIDDEN_VALUES, navigationHidden(), ""));
1152     result.put(Type.NAVIGATION, getOrDefault(NAVIGATION_VALUES, navigation, ""));
1153     result.put(Type.SCREEN_SIZE,
1154         screenWidth != 0 || screenHeight != 0 ? screenWidth + "x" + screenHeight : "");
1155 
1156     String sdkVersion = "";
1157     if (this.sdkVersion != 0) {
1158       sdkVersion = "v" + this.sdkVersion;
1159       if (minorVersion != 0) {
1160         sdkVersion += "." + minorVersion;
1161       }
1162     }
1163     result.put(Type.SDK_VERSION, sdkVersion);
1164     return result;
1165   }
1166 
getOrDefault(Map<K, V> map, K key, V defaultValue)1167   private <K, V> V getOrDefault(Map<K, V> map, K key, V defaultValue) {
1168     // TODO(acornwall): Remove this when Java 8's Map#getOrDefault is available.
1169     // Null is not returned, even if the map contains a key whose value is null. This is intended.
1170     V value = map.get(key);
1171     return value != null ? value : defaultValue;
1172   }
1173 
1174 
1175   // constants for isBetterThan...
1176   public static final int MASK_LAYOUTDIR = SCREENLAYOUT_LAYOUTDIR_MASK;
1177   static final int MASK_SCREENSIZE = SCREENLAYOUT_SIZE_MASK;
1178 
isBetterThan( ResTable_config o, ResTable_config requested)1179   public boolean isBetterThan(
1180       ResTable_config o, ResTable_config requested) {
1181     if (isTruthy(requested)) {
1182       if (isTruthy(imsi()) || isTruthy(o.imsi())) {
1183         if ((mcc != o.mcc) && isTruthy(requested.mcc)) {
1184           return (isTruthy(mcc));
1185         }
1186 
1187         if ((mnc != o.mnc) && isTruthy(requested.mnc)) {
1188           return (isTruthy(mnc));
1189         }
1190       }
1191 
1192       if (isLocaleBetterThan(o, requested)) {
1193         return true;
1194       }
1195 
1196       if (isTruthy(screenLayout) || isTruthy(o.screenLayout)) {
1197         if (isTruthy((screenLayout^o.screenLayout) & MASK_LAYOUTDIR)
1198             && isTruthy(requested.screenLayout & MASK_LAYOUTDIR)) {
1199           int myLayoutDir = screenLayout & MASK_LAYOUTDIR;
1200           int oLayoutDir = o.screenLayout & MASK_LAYOUTDIR;
1201           return (myLayoutDir > oLayoutDir);
1202         }
1203       }
1204 
1205       if (isTruthy(smallestScreenWidthDp) || isTruthy(o.smallestScreenWidthDp)) {
1206         // The configuration closest to the actual size is best.
1207         // We assume that larger configs have already been filtered
1208         // out at this point.  That means we just want the largest one.
1209         if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
1210           return smallestScreenWidthDp > o.smallestScreenWidthDp;
1211         }
1212       }
1213 
1214       if (isTruthy(screenSizeDp()) || isTruthy(o.screenSizeDp())) {
1215         // "Better" is based on the sum of the difference between both
1216         // width and height from the requested dimensions.  We are
1217         // assuming the invalid configs (with smaller dimens) have
1218         // already been filtered.  Note that if a particular dimension
1219         // is unspecified, we will end up with a large value (the
1220         // difference between 0 and the requested dimension), which is
1221         // good since we will prefer a config that has specified a
1222         // dimension value.
1223         int myDelta = 0, otherDelta = 0;
1224         if (isTruthy(requested.screenWidthDp)) {
1225           myDelta += requested.screenWidthDp - screenWidthDp;
1226           otherDelta += requested.screenWidthDp - o.screenWidthDp;
1227         }
1228         if (isTruthy(requested.screenHeightDp)) {
1229           myDelta += requested.screenHeightDp - screenHeightDp;
1230           otherDelta += requested.screenHeightDp - o.screenHeightDp;
1231         }
1232 
1233         if (myDelta != otherDelta) {
1234           return myDelta < otherDelta;
1235         }
1236       }
1237 
1238       if (isTruthy(screenLayout) || isTruthy(o.screenLayout)) {
1239         if (isTruthy((screenLayout^o.screenLayout) & MASK_SCREENSIZE)
1240             && isTruthy(requested.screenLayout & MASK_SCREENSIZE)) {
1241           // A little backwards compatibility here: undefined is
1242           // considered equivalent to normal.  But only if the
1243           // requested size is at least normal; otherwise, small
1244           // is better than the default.
1245           int mySL = (screenLayout & MASK_SCREENSIZE);
1246           int oSL = (o.screenLayout & MASK_SCREENSIZE);
1247           int fixedMySL = mySL;
1248           int fixedOSL = oSL;
1249           if ((requested.screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) {
1250             if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL;
1251             if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL;
1252           }
1253           // For screen size, the best match is the one that is
1254           // closest to the requested screen size, but not over
1255           // (the not over part is dealt with in match() below).
1256           if (fixedMySL == fixedOSL) {
1257             // If the two are the same, but 'this' is actually
1258             // undefined, then the other is really a better match.
1259             if (mySL == 0) return false;
1260             return true;
1261           }
1262           if (fixedMySL != fixedOSL) {
1263             return fixedMySL > fixedOSL;
1264           }
1265         }
1266         if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0
1267             && isTruthy(requested.screenLayout & MASK_SCREENLONG)) {
1268           return isTruthy(screenLayout & MASK_SCREENLONG);
1269         }
1270       }
1271 
1272       if (isTruthy(screenLayout2) || isTruthy(o.screenLayout2)) {
1273         if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0 &&
1274             isTruthy(requested.screenLayout2 & MASK_SCREENROUND)) {
1275           return isTruthy(screenLayout2 & MASK_SCREENROUND);
1276         }
1277       }
1278 
1279       if (isTruthy(colorMode) || isTruthy(o.colorMode)) {
1280         if (((colorMode^o.colorMode) & MASK_WIDE_COLOR_GAMUT) != 0 &&
1281             isTruthy((requested.colorMode & MASK_WIDE_COLOR_GAMUT))) {
1282           return isTruthy(colorMode & MASK_WIDE_COLOR_GAMUT);
1283         }
1284         if (((colorMode^o.colorMode) & MASK_HDR) != 0 &&
1285             isTruthy((requested.colorMode & MASK_HDR))) {
1286           return isTruthy(colorMode & MASK_HDR);
1287         }
1288       }
1289 
1290       if ((orientation != o.orientation) && isTruthy(requested.orientation)) {
1291         return isTruthy(orientation);
1292       }
1293 
1294       if (isTruthy(uiMode) || isTruthy(o.uiMode)) {
1295         if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0
1296             && isTruthy(requested.uiMode & MASK_UI_MODE_TYPE)) {
1297           return isTruthy(uiMode & MASK_UI_MODE_TYPE);
1298         }
1299         if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0
1300             && isTruthy(requested.uiMode & MASK_UI_MODE_NIGHT)) {
1301           return isTruthy(uiMode & MASK_UI_MODE_NIGHT);
1302         }
1303       }
1304 
1305       if (isTruthy(screenType()) || isTruthy(o.screenType())) {
1306         if (density != o.density) {
1307           // Use the system default density (DENSITY_MEDIUM, 160dpi) if none specified.
1308           final int thisDensity = isTruthy(density) ? density : DENSITY_MEDIUM;
1309           final int otherDensity = isTruthy(o.density) ? o.density : DENSITY_MEDIUM;
1310 
1311           // We always prefer DENSITY_ANY over scaling a density bucket.
1312           if (thisDensity == DENSITY_ANY) {
1313             return true;
1314           } else if (otherDensity == DENSITY_ANY) {
1315             return false;
1316           }
1317 
1318           int requestedDensity = requested.density;
1319           if (requested.density == 0 ||
1320               requested.density == DENSITY_ANY) {
1321             requestedDensity = DENSITY_MEDIUM;
1322           }
1323 
1324           // DENSITY_ANY is now dealt with. We should look to
1325           // pick a density bucket and potentially scale it.
1326           // Any density is potentially useful
1327           // because the system will scale it.  Scaling down
1328           // is generally better than scaling up.
1329           int h = thisDensity;
1330           int l = otherDensity;
1331           boolean bImBigger = true;
1332           if (l > h) {
1333             int t = h;
1334             h = l;
1335             l = t;
1336             bImBigger = false;
1337           }
1338 
1339           if (requestedDensity >= h) {
1340             // requested value higher than both l and h, give h
1341             return bImBigger;
1342           }
1343           if (l >= requestedDensity) {
1344             // requested value lower than both l and h, give l
1345             return !bImBigger;
1346           }
1347           // saying that scaling down is 2x better than up
1348           if (((2 * l) - requestedDensity) * h > requestedDensity * requestedDensity) {
1349             return !bImBigger;
1350           } else {
1351             return bImBigger;
1352           }
1353         }
1354 
1355         if ((touchscreen != o.touchscreen) && isTruthy(requested.touchscreen)) {
1356           return isTruthy(touchscreen);
1357         }
1358       }
1359 
1360       if (isTruthy(input()) || isTruthy(o.input())) {
1361             final int keysHidden = inputFlags & MASK_KEYSHIDDEN;
1362             final int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN;
1363         if (keysHidden != oKeysHidden) {
1364                 final int reqKeysHidden =
1365               requested.inputFlags & MASK_KEYSHIDDEN;
1366           if (isTruthy(reqKeysHidden)) {
1367 
1368             if (keysHidden == 0) return false;
1369             if (oKeysHidden == 0) return true;
1370             // For compatibility, we count KEYSHIDDEN_NO as being
1371             // the same as KEYSHIDDEN_SOFT.  Here we disambiguate
1372             // these by making an exact match more specific.
1373             if (reqKeysHidden == keysHidden) return true;
1374             if (reqKeysHidden == oKeysHidden) return false;
1375           }
1376         }
1377 
1378             final int navHidden = inputFlags & MASK_NAVHIDDEN;
1379             final int oNavHidden = o.inputFlags & MASK_NAVHIDDEN;
1380         if (navHidden != oNavHidden) {
1381                 final int reqNavHidden =
1382               requested.inputFlags & MASK_NAVHIDDEN;
1383           if (isTruthy(reqNavHidden)) {
1384 
1385             if (navHidden == 0) return false;
1386             if (oNavHidden == 0) return true;
1387           }
1388         }
1389 
1390         if ((keyboard != o.keyboard) && isTruthy(requested.keyboard)) {
1391           return isTruthy(keyboard);
1392         }
1393 
1394         if ((navigation != o.navigation) && isTruthy(requested.navigation)) {
1395           return isTruthy(navigation);
1396         }
1397       }
1398 
1399       if (isTruthy(screenSize()) || isTruthy(o.screenSize())) {
1400         // "Better" is based on the sum of the difference between both
1401         // width and height from the requested dimensions.  We are
1402         // assuming the invalid configs (with smaller sizes) have
1403         // already been filtered.  Note that if a particular dimension
1404         // is unspecified, we will end up with a large value (the
1405         // difference between 0 and the requested dimension), which is
1406         // good since we will prefer a config that has specified a
1407         // size value.
1408         int myDelta = 0, otherDelta = 0;
1409         if (isTruthy(requested.screenWidth)) {
1410           myDelta += requested.screenWidth - screenWidth;
1411           otherDelta += requested.screenWidth - o.screenWidth;
1412         }
1413         if (isTruthy(requested.screenHeight)) {
1414           myDelta += requested.screenHeight - screenHeight;
1415           otherDelta += requested.screenHeight - o.screenHeight;
1416         }
1417         if (myDelta != otherDelta) {
1418           return myDelta < otherDelta;
1419         }
1420       }
1421 
1422       if (isTruthy(version()) || isTruthy(o.version())) {
1423         if ((sdkVersion != o.sdkVersion) && isTruthy(requested.sdkVersion)) {
1424           return (sdkVersion > o.sdkVersion);
1425         }
1426 
1427         if ((minorVersion != o.minorVersion) &&
1428             isTruthy(requested.minorVersion)) {
1429           return isTruthy(minorVersion);
1430         }
1431       }
1432 
1433       return false;
1434     }
1435     return isMoreSpecificThan(o);
1436   }
1437 
1438 /*
1439   boolean match(final ResTable_config settings) {
1440     System.out.println(this + ".match(" + settings + ")");
1441     boolean result = match_(settings);
1442     System.out.println("    -> " + result);
1443     return result;
1444   }
1445 */
1446 
match(final ResTable_config settings)1447   public boolean match(final ResTable_config settings) {
1448     if (imsi() != 0) {
1449       if (mcc != 0 && mcc != settings.mcc) {
1450         return false;
1451       }
1452       if (mnc != 0 && mnc != settings.mnc) {
1453         return false;
1454       }
1455     }
1456     if (locale() != 0) {
1457       // Don't consider country and variants when deciding matches.
1458       // (Theoretically, the variant can also affect the script. For
1459       // example, "ar-alalc97" probably implies the Latin script, but since
1460       // CLDR doesn't support getting likely scripts for that, we'll assume
1461       // the variant doesn't change the script.)
1462       //
1463       // If two configs differ only in their country and variant,
1464       // they can be weeded out in the isMoreSpecificThan test.
1465       if (!langsAreEquivalent(language, settings.language)) {
1466         return false;
1467       }
1468 
1469       // For backward compatibility and supporting private-use locales, we
1470       // fall back to old behavior if we couldn't determine the script for
1471       // either of the desired locale or the provided locale. But if we could determine
1472       // the scripts, they should be the same for the locales to match.
1473       boolean countriesMustMatch = false;
1474       byte[] computed_script = new byte[4];
1475       byte[] script = null;
1476       if (settings.localeScript[0] == '\0') { // could not determine the request's script
1477         countriesMustMatch = true;
1478       } else {
1479         if (localeScript[0] == '\0' && !localeScriptWasComputed) {
1480           // script was not provided or computed, so we try to compute it
1481           localeDataComputeScript(computed_script, language, country);
1482           if (computed_script[0] == '\0') { // we could not compute the script
1483             countriesMustMatch = true;
1484           } else {
1485             script = computed_script;
1486           }
1487         } else { // script was provided, so just use it
1488           script = localeScript;
1489         }
1490       }
1491 
1492       if (countriesMustMatch) {
1493         if (country[0] != '\0' && !areIdentical(country, settings.country)) {
1494           return false;
1495         }
1496       } else {
1497         if (!Arrays.equals(script, settings.localeScript)) {
1498           return false;
1499         }
1500       }
1501     }
1502 
1503     if (screenConfig() != 0) {
1504         final int layoutDir = screenLayout&MASK_LAYOUTDIR;
1505         final int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR;
1506       if (layoutDir != 0 && layoutDir != setLayoutDir) {
1507         return false;
1508       }
1509 
1510         final int screenSize = screenLayout&MASK_SCREENSIZE;
1511         final int setScreenSize = settings.screenLayout&MASK_SCREENSIZE;
1512       // Any screen sizes for larger screens than the setting do not
1513       // match.
1514       if (screenSize != 0 && screenSize > setScreenSize) {
1515         return false;
1516       }
1517 
1518         final int screenLong = screenLayout&MASK_SCREENLONG;
1519         final int setScreenLong = settings.screenLayout&MASK_SCREENLONG;
1520       if (screenLong != 0 && screenLong != setScreenLong) {
1521         return false;
1522       }
1523 
1524         final int uiModeType = uiMode&MASK_UI_MODE_TYPE;
1525         final int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE;
1526       if (uiModeType != 0 && uiModeType != setUiModeType) {
1527         return false;
1528       }
1529 
1530         final int uiModeNight = uiMode&MASK_UI_MODE_NIGHT;
1531         final int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT;
1532       if (uiModeNight != 0 && uiModeNight != setUiModeNight) {
1533         return false;
1534       }
1535 
1536       if (smallestScreenWidthDp != 0
1537           && smallestScreenWidthDp > settings.smallestScreenWidthDp) {
1538         return false;
1539       }
1540     }
1541 
1542     if (screenConfig2() != 0) {
1543         final int screenRound = screenLayout2 & MASK_SCREENROUND;
1544         final int setScreenRound = settings.screenLayout2 & MASK_SCREENROUND;
1545       if (screenRound != 0 && screenRound != setScreenRound) {
1546         return false;
1547       }
1548     }
1549 
1550     final int hdr = colorMode & MASK_HDR;
1551     final int setHdr = settings.colorMode & MASK_HDR;
1552     if (hdr != 0 && hdr != setHdr) {
1553       return false;
1554     }
1555 
1556     final int wideColorGamut = colorMode & MASK_WIDE_COLOR_GAMUT;
1557     final int setWideColorGamut = settings.colorMode & MASK_WIDE_COLOR_GAMUT;
1558     if (wideColorGamut != 0 && wideColorGamut != setWideColorGamut) {
1559       return false;
1560     }
1561 
1562     if (screenSizeDp() != 0) {
1563       if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) {
1564         if (kDebugTableSuperNoisy) {
1565           ALOGI("Filtering out width %d in requested %d", screenWidthDp,
1566               settings.screenWidthDp);
1567         }
1568         return false;
1569       }
1570       if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) {
1571         if (kDebugTableSuperNoisy) {
1572           ALOGI("Filtering out height %d in requested %d", screenHeightDp,
1573               settings.screenHeightDp);
1574         }
1575         return false;
1576       }
1577     }
1578     if (screenType() != 0) {
1579       if (orientation != 0 && orientation != settings.orientation) {
1580         return false;
1581       }
1582       // density always matches - we can scale it.  See isBetterThan
1583       if (touchscreen != 0 && touchscreen != settings.touchscreen) {
1584         return false;
1585       }
1586     }
1587     if (input() != 0) {
1588         final int keysHidden = inputFlags&MASK_KEYSHIDDEN;
1589         final int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN;
1590       if (keysHidden != 0 && keysHidden != setKeysHidden) {
1591         // For compatibility, we count a request for KEYSHIDDEN_NO as also
1592         // matching the more recent KEYSHIDDEN_SOFT.  Basically
1593         // KEYSHIDDEN_NO means there is some kind of keyboard available.
1594         if (kDebugTableSuperNoisy) {
1595           ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden);
1596         }
1597         if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) {
1598           if (kDebugTableSuperNoisy) {
1599             ALOGI("No match!");
1600           }
1601           return false;
1602         }
1603       }
1604         final int navHidden = inputFlags&MASK_NAVHIDDEN;
1605         final int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN;
1606       if (navHidden != 0 && navHidden != setNavHidden) {
1607         return false;
1608       }
1609       if (keyboard != 0 && keyboard != settings.keyboard) {
1610         return false;
1611       }
1612       if (navigation != 0 && navigation != settings.navigation) {
1613         return false;
1614       }
1615     }
1616     if (screenSize() != 0) {
1617       if (screenWidth != 0 && screenWidth > settings.screenWidth) {
1618         return false;
1619       }
1620       if (screenHeight != 0 && screenHeight > settings.screenHeight) {
1621         return false;
1622       }
1623     }
1624     if (version() != 0) {
1625       if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) {
1626         return false;
1627       }
1628       if (minorVersion != 0 && minorVersion != settings.minorVersion) {
1629         return false;
1630       }
1631     }
1632     return true;
1633   }
1634 
1635 //  void appendDirLocale(String8& out) const {
1636 //    if (!language[0]) {
1637 //      return;
1638 //    }
1639 //    const bool scriptWasProvided = localeScript[0] != '\0' && !localeScriptWasComputed;
1640 //    if (!scriptWasProvided && !localeVariant[0] && !localeNumberingSystem[0]) {
1641 //      // Legacy format.
1642 //      if (out.size() > 0) {
1643 //        out.append("-");
1644 //      }
1645 //
1646 //      char buf[4];
1647 //      size_t len = unpackLanguage(buf);
1648 //      out.append(buf, len);
1649 //
1650 //      if (country[0]) {
1651 //        out.append("-r");
1652 //        len = unpackRegion(buf);
1653 //        out.append(buf, len);
1654 //      }
1655 //      return;
1656 //    }
1657 //
1658 //    // We are writing the modified BCP 47 tag.
1659 //    // It starts with 'b+' and uses '+' as a separator.
1660 //
1661 //    if (out.size() > 0) {
1662 //      out.append("-");
1663 //    }
1664 //    out.append("b+");
1665 //
1666 //    char buf[4];
1667 //    size_t len = unpackLanguage(buf);
1668 //    out.append(buf, len);
1669 //
1670 //    if (scriptWasProvided) {
1671 //      out.append("+");
1672 //      out.append(localeScript, sizeof(localeScript));
1673 //    }
1674 //
1675 //    if (country[0]) {
1676 //      out.append("+");
1677 //      len = unpackRegion(buf);
1678 //      out.append(buf, len);
1679 //    }
1680 //
1681 //    if (localeVariant[0]) {
1682 //      out.append("+");
1683 //      out.append(localeVariant, strnlen(localeVariant, sizeof(localeVariant)));
1684 //    }
1685 //
1686 //    if (localeNumberingSystem[0]) {
1687 //      out.append("+u+nu+");
1688 //      out.append(localeNumberingSystem,
1689 //                 strnlen(localeNumberingSystem, sizeof(localeNumberingSystem)));
1690 //    }
1691 //  }
1692 
1693   // returns string as return value instead of by mutating first arg
1694   // void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool canonicalize) const {
getBcp47Locale(boolean canonicalize)1695   String getBcp47Locale(boolean canonicalize) {
1696     StringBuilder str = new StringBuilder();
1697 
1698     // This represents the "any" locale value, which has traditionally been
1699     // represented by the empty string.
1700     if (language[0] == '\0' && country[0] == '\0') {
1701       return "";
1702     }
1703 
1704     if (language[0] != '\0') {
1705       if (canonicalize && areIdentical(language, kTagalog)) {
1706         // Replace Tagalog with Filipino if we are canonicalizing
1707         str.setLength(0);
1708         str.append("fil");// 3-letter code for Filipino
1709       } else {
1710         str.append(unpackLanguage());
1711       }
1712     }
1713 
1714     if (isTruthy(localeScript[0]) && !localeScriptWasComputed) {
1715       if (str.length() > 0) {
1716         str.append('-');
1717       }
1718       for (byte aLocaleScript : localeScript) {
1719         str.append((char) aLocaleScript);
1720       }
1721     }
1722 
1723     if (country[0] != '\0') {
1724       if (str.length() > 0) {
1725         str.append('-');
1726       }
1727       String regionStr = unpackRegion();
1728       str.append(regionStr);
1729     }
1730 
1731     if (isTruthy(localeVariant[0])) {
1732       if (str.length() > 0) {
1733         str.append('-');
1734       }
1735 
1736       for (byte aLocaleScript : localeVariant) {
1737         str.append((char) aLocaleScript);
1738       }
1739     }
1740 
1741     // Add Unicode extension only if at least one other locale component is present
1742     if (localeNumberingSystem[0] != '\0' && str.length() > 0) {
1743       String NU_PREFIX = "-u-nu-";
1744       str.append(NU_PREFIX);
1745       str.append(new String(localeNumberingSystem, UTF_8));
1746     }
1747 
1748     return str.toString();
1749   }
1750 
1751   enum State {
1752     BASE, UNICODE_EXTENSION, IGNORE_THE_REST
1753   }
1754 
1755   enum UnicodeState {
1756     /* Initial state after the Unicode singleton is detected. Either a keyword
1757      * or an attribute is expected. */
1758     NO_KEY,
1759     /* Unicode extension key (but not attribute) is expected. Next states:
1760      * NO_KEY, IGNORE_KEY or NUMBERING_SYSTEM. */
1761     EXPECT_KEY,
1762     /* A key is detected, however it is not supported for now. Ignore its
1763      * value. Next states: IGNORE_KEY or NUMBERING_SYSTEM. */
1764     IGNORE_KEY,
1765     /* Numbering system key was detected. Store its value in the configuration
1766      * localeNumberingSystem field. Next state: EXPECT_KEY */
1767     NUMBERING_SYSTEM
1768   }
1769 
1770   static class LocaleParserState {
1771     State parserState;
1772     UnicodeState unicodeState;
1773 
1774     // LocaleParserState(): parserState(BASE), unicodeState(NO_KEY) {}
LocaleParserState()1775     public LocaleParserState() {
1776       this.parserState = State.BASE;
1777       this.unicodeState = UnicodeState.NO_KEY;
1778     }
1779   }
1780 
assignLocaleComponent(ResTable_config config, final String start, int size, LocaleParserState state)1781   static LocaleParserState assignLocaleComponent(ResTable_config config,
1782       final String start, int size, LocaleParserState state) {
1783 
1784     /* It is assumed that this function is not invoked with state.parserState
1785      * set to IGNORE_THE_REST. The condition is checked by setBcp47Locale
1786      * function. */
1787 
1788     if (state.parserState == State.UNICODE_EXTENSION) {
1789       switch (size) {
1790         case 1:
1791           /* Other BCP 47 extensions are not supported at the moment */
1792           state.parserState = State.IGNORE_THE_REST;
1793           break;
1794         case 2:
1795           if (state.unicodeState == UnicodeState.NO_KEY ||
1796               state.unicodeState == UnicodeState.EXPECT_KEY) {
1797             /* Analyze Unicode extension key. Currently only 'nu'
1798              * (numbering system) is supported.*/
1799             if ((start.charAt(0) == 'n' || start.charAt(0) == 'N') &&
1800                 (start.charAt(1) == 'u' || start.charAt(1) == 'U')) {
1801               state.unicodeState = UnicodeState.NUMBERING_SYSTEM;
1802             } else {
1803               state.unicodeState = UnicodeState.IGNORE_KEY;
1804             }
1805           } else {
1806             /* Keys are not allowed in other state allowed, ignore the rest. */
1807             state.parserState = State.IGNORE_THE_REST;
1808           }
1809           break;
1810         case 3:
1811         case 4:
1812         case 5:
1813         case 6:
1814         case 7:
1815         case 8:
1816           switch (state.unicodeState) {
1817             case NUMBERING_SYSTEM:
1818               /* Accept only the first occurrence of the numbering system. */
1819               if (config.localeNumberingSystem[0] == '\0') {
1820                 for (int i = 0; i < size; ++i) {
1821                   config.localeNumberingSystem[i] = (byte) Character.toLowerCase(start.charAt(i));
1822                 }
1823                 state.unicodeState = UnicodeState.EXPECT_KEY;
1824               } else {
1825                 state.parserState = State.IGNORE_THE_REST;
1826               }
1827               break;
1828             case IGNORE_KEY:
1829               /* Unsupported Unicode keyword. Ignore. */
1830               state.unicodeState = UnicodeState.EXPECT_KEY;
1831               break;
1832             case EXPECT_KEY:
1833               /* A keyword followed by an attribute is not allowed. */
1834               state.parserState = State.IGNORE_THE_REST;
1835               break;
1836             case NO_KEY:
1837               /* Extension attribute. Do nothing. */
1838               break;
1839           }
1840           break;
1841         default:
1842           /* Unexpected field length - ignore the rest and treat as an error */
1843           state.parserState = State.IGNORE_THE_REST;
1844       }
1845       return state;
1846     }
1847 
1848     switch (size) {
1849       case 0:
1850         state.parserState = State.IGNORE_THE_REST;
1851         break;
1852       case 1:
1853         state.parserState = (start.charAt(0) == 'u' || start.charAt(0) == 'U')
1854             ? State.UNICODE_EXTENSION
1855             : State.IGNORE_THE_REST;
1856         break;
1857       case 2:
1858       case 3:
1859         if (isTruthy(config.language[0])) {
1860           config.packRegion(start);
1861         } else {
1862           config.packLanguage(start);
1863         }
1864         break;
1865       case 4:
1866         char start0 = start.charAt(0);
1867         if ('0' <= start0 && start0 <= '9') {
1868           // this is a variant, so fall through
1869         } else {
1870           config.localeScript[0] = (byte) Character.toUpperCase(start0);
1871           for (int i = 1; i < 4; ++i) {
1872             config.localeScript[i] = (byte) Character.toLowerCase(start.charAt(i));
1873           }
1874           break;
1875         }
1876         // fall through
1877       case 5:
1878       case 6:
1879       case 7:
1880       case 8:
1881         for (int i = 0; i < size; ++i) {
1882           config.localeVariant[i] = (byte) Character.toLowerCase(start.charAt(i));
1883         }
1884         break;
1885       default:
1886         state.parserState = State.IGNORE_THE_REST;
1887     }
1888 
1889     return state;
1890   }
1891 
setBcp47Locale(final String in)1892   public void setBcp47Locale(final String in) {
1893     clearLocale();
1894 
1895     int start = 0;
1896     LocaleParserState state = new LocaleParserState();
1897     int separator;
1898     while ((separator = in.indexOf('-', start)) > 0) {
1899       final int size = separator - start;
1900       state = assignLocaleComponent(this, in.substring(start), size, state);
1901       if (state.parserState == State.IGNORE_THE_REST) {
1902 
1903         System.err.println(String.format("Invalid BCP-47 locale string: %s", in));
1904         break;
1905       }
1906 
1907       start = (separator + 1);
1908     }
1909 
1910     if (state.parserState != State.IGNORE_THE_REST) {
1911       final int size = in.length() - start;
1912       assignLocaleComponent(this, in.substring(start), size, state);
1913     }
1914 
1915     localeScriptWasComputed = (localeScript[0] == '\0');
1916     if (localeScriptWasComputed) {
1917       computeScript();
1918     }
1919   }
1920 
clearLocale()1921   void clearLocale() {
1922 //    locale = 0;
1923     clear(language);
1924     clear(country);
1925 
1926     localeScriptWasComputed = false;
1927     clear(localeScript);
1928     clear(localeVariant);
1929   }
1930 
computeScript()1931   void computeScript() {
1932     localeDataComputeScript(localeScript, language, country);
1933   }
1934 
clear(byte[] bytes)1935   private void clear(byte[] bytes) {
1936     for (int i = 0; i < bytes.length; i++) {
1937       bytes[i] = 0;
1938     }
1939   }
1940 
1941 
1942   /**
1943    *     union {
1944    struct {
1945    // Mobile country code (from SIM).  0 means "any".
1946    uint16_t mcc;
1947    // Mobile network code (from SIM).  0 means "any".
1948    uint16_t mnc;
1949    };
1950    uint32_t imsi;
1951    };
1952    */
imsi()1953   private int imsi() {
1954     return ((mcc & 0xffff) << 16) | (mnc & 0xffff);
1955   }
1956 
1957   /**
1958    *     union {
1959    struct {
1960    uint16_t screenWidth;
1961    uint16_t screenHeight;
1962    };
1963    uint32_t screenSize;
1964    };
1965    */
screenSize()1966   private int screenSize() {
1967     return ((screenWidth & 0xffff) << 16) | (screenHeight & 0xffff);
1968   }
1969 
1970 
1971   /**
1972    union {
1973    struct {
1974    uint8_t screenLayout;
1975    uint8_t uiMode;
1976    uint16_t smallestScreenWidthDp;
1977    };
1978    uint32_t screenConfig;
1979    };
1980    */
screenConfig()1981   private int screenConfig() {
1982     return ((screenLayout & 0xff) << 24) | ((uiMode * 0xff) << 16) | (smallestScreenWidthDp & 0xffff);
1983   }
1984 
1985 
1986   /**
1987    *     union {
1988    struct {
1989    uint16_t screenWidthDp;
1990    uint16_t screenHeightDp;
1991    };
1992    uint32_t screenSizeDp;
1993    };
1994    */
screenSizeDp()1995   private int screenSizeDp() {
1996     // screenWidthDp and screenHeightDp are really shorts...
1997     return (screenWidthDp & 0xffff) << 16 | (screenHeightDp & 0xffff);
1998   }
1999 
2000   /**
2001      union {
2002      struct {
2003      uint8_t orientation;
2004      uint8_t touchscreen;
2005      uint16_t density;
2006      };
2007      uint32_t screenType;
2008      };
2009    */
screenType()2010   private int screenType() {
2011     return ((orientation & 0xff) << 24) | ((touchscreen & 0xff) << 16) | (density & 0xffff);
2012   }
2013 
2014   /**
2015    *
2016    union {
2017    struct {
2018    uint8_t keyboard;
2019    uint8_t navigation;
2020    uint8_t inputFlags;
2021    uint8_t inputPad0;
2022    };
2023    uint32_t input;
2024    };
2025    */
input()2026   private int input() {
2027     // TODO is Pad Zeros?
2028     return ((keyboard & 0xff) << 24) | ((navigation & 0xff) << 16) | ((inputFlags & 0xff) << 8);
2029   }
2030 
2031   /**
2032    *     union {
2033    struct {
2034    uint16_t sdkVersion;
2035    // For now minorVersion must always be 0!!!  Its meaning
2036    // is currently undefined.
2037    uint16_t minorVersion;
2038    };
2039    uint32_t version;
2040    };
2041    */
version()2042   private int version() {
2043     return ((sdkVersion & 0xffff) << 16) | (minorVersion & 0xffff);
2044   }
2045 
2046   /**
2047    union {
2048    struct {
2049    // This field can take three different forms:
2050    // - \0\0 means "any".
2051    //
2052    // - Two 7 bit ascii values interpreted as ISO-639-1 language
2053    //   codes ('fr', 'en' etc. etc.). The high bit for both bytes is
2054    //   zero.
2055    //
2056    // - A single 16 bit little endian packed value representing an
2057    //   ISO-639-2 3 letter language code. This will be of the form:
2058    //
2059    //   {1, t, t, t, t, t, s, s, s, s, s, f, f, f, f, f}
2060    //
2061    //   bit[0, 4] = first letter of the language code
2062    //   bit[5, 9] = second letter of the language code
2063    //   bit[10, 14] = third letter of the language code.
2064    //   bit[15] = 1 always
2065    //
2066    // For backwards compatibility, languages that have unambiguous
2067    // two letter codes are represented in that format.
2068    //
2069    // The layout is always bigendian irrespective of the runtime
2070    // architecture.
2071    char language[2];
2072 
2073    // This field can take three different forms:
2074    // - \0\0 means "any".
2075    //
2076    // - Two 7 bit ascii values interpreted as 2 letter country
2077    //   codes ('US', 'GB' etc.). The high bit for both bytes is zero.
2078    //
2079    // - An UN M.49 3 digit country code. For simplicity, these are packed
2080    //   in the same manner as the language codes, though we should need
2081    //   only 10 bits to represent them, instead of the 15.
2082    //
2083    // The layout is always bigendian irrespective of the runtime
2084    // architecture.
2085    char country[2];
2086    };
2087    uint32_t locale;
2088    };
2089    */
locale()2090   int locale() {
2091     return ((language[0] & 0xff) << 24) | ((language[1] & 0xff) << 16) | ((country[0] & 0xff) << 8) | (country[1] & 0xff);
2092   }
2093 
isLocaleBetterThan(ResTable_config o, ResTable_config requested)2094   private boolean isLocaleBetterThan(ResTable_config o, ResTable_config requested) {
2095     if (requested.locale() == 0) {
2096       // The request doesn't have a locale, so no resource is better
2097       // than the other.
2098       return false;
2099     }
2100 
2101     if (locale() == 0 && o.locale() == 0) {
2102       // The locale part of both resources is empty, so none is better
2103       // than the other.
2104       return false;
2105     }
2106 
2107     // Non-matching locales have been filtered out, so both resources
2108     // match the requested locale.
2109     //
2110     // Because of the locale-related checks in match() and the checks, we know
2111     // that:
2112     // 1) The resource languages are either empty or match the request;
2113     // and
2114     // 2) If the request's script is known, the resource scripts are either
2115     //    unknown or match the request.
2116 
2117     if (!langsAreEquivalent(language, o.language)) {
2118       // The languages of the two resources are not equivalent. If we are
2119       // here, we can only assume that the two resources matched the request
2120       // because one doesn't have a language and the other has a matching
2121       // language.
2122       //
2123       // We consider the one that has the language specified a better match.
2124       //
2125       // The exception is that we consider no-language resources a better match
2126       // for US English and similar locales than locales that are a descendant
2127       // of Internatinal English (en-001), since no-language resources are
2128       // where the US English resource have traditionally lived for most apps.
2129       if (areIdentical(requested.language, kEnglish)) {
2130         if (areIdentical(requested.country, kUnitedStates)) {
2131           // For US English itself, we consider a no-locale resource a
2132           // better match if the other resource has a country other than
2133           // US specified.
2134           if (language[0] != '\0') {
2135             return country[0] == '\0' || areIdentical(country, kUnitedStates);
2136           } else {
2137             return !(o.country[0] == '\0' || areIdentical(o.country, kUnitedStates));
2138           }
2139         } else if (localeDataIsCloseToUsEnglish(requested.country)) {
2140           if (language[0] != '\0') {
2141             return localeDataIsCloseToUsEnglish(country);
2142           } else {
2143             return !localeDataIsCloseToUsEnglish(o.country);
2144           }
2145         }
2146       }
2147       return (language[0] != '\0');
2148     }
2149 
2150     // If we are here, both the resources have an equivalent non-empty language
2151     // to the request.
2152     //
2153     // Because the languages are equivalent, computeScript() always returns a
2154     // non-empty script for languages it knows about, and we have passed the
2155     // script checks in match(), the scripts are either all unknown or are all
2156     // the same. So we can't gain anything by checking the scripts. We need to
2157     // check the country and variant.
2158 
2159     // See if any of the regions is better than the other.
2160     final int region_comparison = localeDataCompareRegions(
2161         country, o.country,
2162         requested.language, str(requested.localeScript), requested.country);
2163     if (region_comparison != 0) {
2164       return (region_comparison > 0);
2165     }
2166 
2167     // The regions are the same. Try the variant.
2168     final boolean localeMatches = Arrays.equals(localeVariant, requested.localeVariant);
2169     final boolean otherMatches = Arrays.equals(o.localeVariant, requested.localeVariant);
2170     if (localeMatches != otherMatches) {
2171       return localeMatches;
2172     }
2173 
2174     // The variants are the same, try numbering system.
2175     boolean localeNumsysMatches = arrayCompare(localeNumberingSystem,
2176                                              requested.localeNumberingSystem
2177                                              ) == 0;
2178     boolean otherNumsysMatches = arrayCompare(o.localeNumberingSystem,
2179                                             requested.localeNumberingSystem
2180                                             ) == 0;
2181 
2182     if (localeNumsysMatches != otherNumsysMatches) {
2183         return localeNumsysMatches;
2184     }
2185 
2186     // Finally, the languages, although equivalent, may still be different
2187     // (like for Tagalog and Filipino). Identical is better than just
2188     // equivalent.
2189     if (areIdentical(language, requested.language)
2190         && !areIdentical(o.language, requested.language)) {
2191       return true;
2192     }
2193 
2194     return false;
2195   }
2196 
str(byte[] country)2197   private String str(byte[] country) {
2198     return new String(country, UTF_8);
2199   }
2200 
langsAreEquivalent(final byte[] lang1, final byte[] lang2)2201   private boolean langsAreEquivalent(final byte[] lang1, final byte[] lang2) {
2202     return areIdentical(lang1, lang2) ||
2203         (areIdentical(lang1, kTagalog) && areIdentical(lang2, kFilipino)) ||
2204         (areIdentical(lang1, kFilipino) && areIdentical(lang2, kTagalog));
2205   }
2206 
2207   // Checks if two language or country codes are identical
areIdentical(final byte[] code1, final byte[] code2)2208   private boolean  areIdentical(final byte[] code1, final byte[] code2) {
2209     return code1[0] == code2[0] && code1[1] == code2[1];
2210   }
2211 
isLocaleMoreSpecificThan(ResTable_config o)2212   int isLocaleMoreSpecificThan(ResTable_config o) {
2213     if (isTruthy(locale()) || isTruthy(o.locale())) {
2214       if (language[0] != o.language[0]) {
2215         if (!isTruthy(language[0])) return -1;
2216         if (!isTruthy(o.language[0])) return 1;
2217       }
2218       if (country[0] != o.country[0]) {
2219         if (!isTruthy(country[0])) return -1;
2220         if (!isTruthy(o.country[0])) return 1;
2221       }
2222     }
2223     return getImportanceScoreOfLocale() - o.getImportanceScoreOfLocale();
2224   }
2225 
isMoreSpecificThan(ResTable_config o)2226   private boolean isMoreSpecificThan(ResTable_config o) {
2227     // The order of the following tests defines the importance of one
2228     // configuration parameter over another.  Those tests first are more
2229     // important, trumping any values in those following them.
2230     if (isTruthy(imsi()) || isTruthy(o.imsi())) {
2231       if (mcc != o.mcc) {
2232         if (!isTruthy(mcc)) return false;
2233         if (!isTruthy(o.mcc)) return true;
2234       }
2235       if (mnc != o.mnc) {
2236         if (!isTruthy(mnc)) return false;
2237         if (!isTruthy(o.mnc)) return true;
2238       }
2239     }
2240     if (isTruthy(locale()) || isTruthy(o.locale())) {
2241       int diff = isLocaleMoreSpecificThan(o);
2242       if (diff < 0) {
2243         return false;
2244       }
2245       if (diff > 0) {
2246         return true;
2247       }
2248     }
2249     if (isTruthy(screenLayout) || isTruthy(o.screenLayout)) {
2250       if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0) {
2251         if (!isTruthy((screenLayout & MASK_LAYOUTDIR))) return false;
2252         if (!isTruthy((o.screenLayout & MASK_LAYOUTDIR))) return true;
2253       }
2254     }
2255     if (isTruthy(smallestScreenWidthDp) || isTruthy(o.smallestScreenWidthDp)) {
2256       if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
2257         if (!isTruthy(smallestScreenWidthDp)) return false;
2258         if (!isTruthy(o.smallestScreenWidthDp)) return true;
2259       }
2260     }
2261     if (isTruthy(screenSizeDp()) || isTruthy(o.screenSizeDp())) {
2262       if (screenWidthDp != o.screenWidthDp) {
2263         if (!isTruthy(screenWidthDp)) return false;
2264         if (!isTruthy(o.screenWidthDp)) return true;
2265       }
2266       if (screenHeightDp != o.screenHeightDp) {
2267         if (!isTruthy(screenHeightDp)) return false;
2268         if (!isTruthy(o.screenHeightDp)) return true;
2269       }
2270     }
2271     if (isTruthy(screenLayout) || isTruthy(o.screenLayout)) {
2272       if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) {
2273         if (!isTruthy((screenLayout & MASK_SCREENSIZE))) return false;
2274         if (!isTruthy((o.screenLayout & MASK_SCREENSIZE))) return true;
2275       }
2276       if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) {
2277         if (!isTruthy((screenLayout & MASK_SCREENLONG))) return false;
2278         if (!isTruthy((o.screenLayout & MASK_SCREENLONG))) return true;
2279       }
2280     }
2281     if (isTruthy(screenLayout2) || isTruthy(o.screenLayout2)) {
2282       if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0) {
2283         if (!isTruthy((screenLayout2 & MASK_SCREENROUND))) return false;
2284         if (!isTruthy((o.screenLayout2 & MASK_SCREENROUND))) return true;
2285       }
2286     }
2287 
2288     if (isTruthy(colorMode) || isTruthy(o.colorMode)) {
2289       if (((colorMode^o.colorMode) & MASK_HDR) != 0) {
2290         if (!isTruthy((colorMode & MASK_HDR))) return false;
2291         if (!isTruthy((o.colorMode & MASK_HDR))) return true;
2292       }
2293       if (((colorMode^o.colorMode) & MASK_WIDE_COLOR_GAMUT) != 0) {
2294         if (!isTruthy((colorMode & MASK_WIDE_COLOR_GAMUT))) return false;
2295         if (!isTruthy((o.colorMode & MASK_WIDE_COLOR_GAMUT))) return true;
2296       }
2297     }
2298 
2299     if (orientation != o.orientation) {
2300       if (!isTruthy(orientation)) return false;
2301       if (!isTruthy(o.orientation)) return true;
2302     }
2303     if (isTruthy(uiMode) || isTruthy(o.uiMode)) {
2304       if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) {
2305         if (!isTruthy((uiMode & MASK_UI_MODE_TYPE))) return false;
2306         if (!isTruthy((o.uiMode & MASK_UI_MODE_TYPE))) return true;
2307       }
2308       if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) {
2309         if (!isTruthy((uiMode & MASK_UI_MODE_NIGHT))) return false;
2310         if (!isTruthy((o.uiMode & MASK_UI_MODE_NIGHT))) return true;
2311       }
2312     }
2313     // density is never 'more specific'
2314     // as the default just equals 160
2315     if (touchscreen != o.touchscreen) {
2316       if (!isTruthy(touchscreen)) return false;
2317       if (!isTruthy(o.touchscreen)) return true;
2318     }
2319     if (isTruthy(input()) || isTruthy(o.input())) {
2320       if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) {
2321         if (!isTruthy((inputFlags & MASK_KEYSHIDDEN))) return false;
2322         if (!isTruthy((o.inputFlags & MASK_KEYSHIDDEN))) return true;
2323       }
2324       if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) {
2325         if (!isTruthy((inputFlags & MASK_NAVHIDDEN))) return false;
2326         if (!isTruthy((o.inputFlags & MASK_NAVHIDDEN))) return true;
2327       }
2328       if (keyboard != o.keyboard) {
2329         if (!isTruthy(keyboard)) return false;
2330         if (!isTruthy(o.keyboard)) return true;
2331       }
2332       if (navigation != o.navigation) {
2333         if (!isTruthy(navigation)) return false;
2334         if (!isTruthy(o.navigation)) return true;
2335       }
2336     }
2337     if (isTruthy(screenSize()) || isTruthy(o.screenSize())) {
2338       if (screenWidth != o.screenWidth) {
2339         if (!isTruthy(screenWidth)) return false;
2340         if (!isTruthy(o.screenWidth)) return true;
2341       }
2342       if (screenHeight != o.screenHeight) {
2343         if (!isTruthy(screenHeight)) return false;
2344         if (!isTruthy(o.screenHeight)) return true;
2345       }
2346     }
2347     if (isTruthy(version()) || isTruthy(o.version())) {
2348       if (sdkVersion != o.sdkVersion) {
2349         if (!isTruthy(sdkVersion)) return false;
2350         if (!isTruthy(o.sdkVersion)) return true;
2351       }
2352       if (minorVersion != o.minorVersion) {
2353         if (!isTruthy(minorVersion)) return false;
2354         if (!isTruthy(o.minorVersion)) return true;
2355       }
2356     }
2357     return false;
2358   }
2359 }