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