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