diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java index 18399addbb..2c4ae0f8f3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java @@ -51,6 +51,8 @@ public class ResConfigFlags { private final byte screenLayout2; private final byte colorMode; + private final char[] localeNumberingSystem; + public final boolean isInvalid; private final String mQualifiers; @@ -80,6 +82,7 @@ public ResConfigFlags() { localeVariant = null; screenLayout2 = 0; colorMode = COLOR_WIDE_UNDEFINED; + localeNumberingSystem = null; isInvalid = false; mQualifiers = ""; size = 0; @@ -92,7 +95,8 @@ public ResConfigFlags(short mcc, short mnc, char[] language, short sdkVersion, byte screenLayout, byte uiMode, short smallestScreenWidthDp, short screenWidthDp, short screenHeightDp, char[] localeScript, char[] localeVariant, - byte screenLayout2, byte colorMode, boolean isInvalid, int size) { + byte screenLayout2, byte colorMode, char[] localeNumberingSystem, + boolean isInvalid, int size) { if (orientation < 0 || orientation > 3) { LOGGER.warning("Invalid orientation value: " + orientation); orientation = 0; @@ -157,6 +161,7 @@ public ResConfigFlags(short mcc, short mnc, char[] language, this.localeVariant = localeVariant; this.screenLayout2 = screenLayout2; this.colorMode = colorMode; + this.localeNumberingSystem = localeNumberingSystem; this.isInvalid = isInvalid; this.size = size; mQualifiers = generateQualifiers(); @@ -466,6 +471,12 @@ private String getLocaleString() { if (localeVariant != null && localeVariant.length >= 5) { sb.append("+").append(toUpper(localeVariant)); } + + // If we have a numbering system - it isn't used in qualifiers for build tools, but AOSP understands it + // So chances are - this may be valid, but aapt 1/2 will not like it. + if (localeNumberingSystem != null && localeNumberingSystem.length > 0) { + sb.append("+u+nu+").append(localeNumberingSystem); + } } return sb.toString(); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java index 88fc3af6e7..b117780a1f 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java @@ -497,8 +497,8 @@ private ResConfigFlags readConfigFlags() throws IOException, AndrolibException { char[] localeScript = null; char[] localeVariant = null; if (size >= 48) { - localeScript = readScriptOrVariantChar(4).toCharArray(); - localeVariant = readScriptOrVariantChar(8).toCharArray(); + localeScript = readVariantLengthString(4).toCharArray(); + localeVariant = readVariantLengthString(8).toCharArray(); read = 48; } @@ -511,16 +511,16 @@ private ResConfigFlags readConfigFlags() throws IOException, AndrolibException { read = 52; } - if (size > 52) { - int length = size - read; - mIn.skipBytes(length); // localeNumberingSystem - read += length; + char[] localeNumberingSystem = null; + if (size >= 60) { + localeNumberingSystem = readVariantLengthString(8).toCharArray(); + read = 60; } - int exceedingSize = size - KNOWN_CONFIG_BYTES; - if (exceedingSize > 0) { - byte[] buf = new byte[exceedingSize]; - read += exceedingSize; + int exceedingKnownSize = size - KNOWN_CONFIG_BYTES; + if (exceedingKnownSize > 0) { + byte[] buf = new byte[exceedingKnownSize]; + read += exceedingKnownSize; mIn.readFully(buf); BigInteger exceedingBI = new BigInteger(1, buf); @@ -545,7 +545,7 @@ private ResConfigFlags readConfigFlags() throws IOException, AndrolibException { inputFlags, screenWidth, screenHeight, sdkVersion, screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp, screenHeightDp, localeScript, localeVariant, screenLayout2, - colorMode, isInvalid, size); + colorMode, localeNumberingSystem, isInvalid, size); } private char[] unpackLanguageOrRegion(byte in0, byte in1, char base) { @@ -562,17 +562,17 @@ private char[] unpackLanguageOrRegion(byte in0, byte in1, char base) { return new char[] { (char) in0, (char) in1 }; } - private String readScriptOrVariantChar(int length) throws IOException { + private String readVariantLengthString(int maxLength) throws IOException { StringBuilder string = new StringBuilder(16); - while (length-- != 0) { + while (maxLength-- != 0) { short ch = mIn.readByte(); if (ch == 0) { break; } string.append((char) ch); } - mIn.skipBytes(length); + mIn.skipBytes(maxLength); return string.toString(); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java index 248452fc8e..9f1d972b17 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java @@ -76,6 +76,17 @@ public void valuesMaxLengthTest() throws BrutException { compareValuesFiles("values-es/strings.xml"); } + @Test + public void valuesBcp47LanguageVariantTest() throws BrutException { + compareValuesFiles("values-b+iw+660/strings.xml"); + } + + @Test + public void valuesBcp47LanguageScriptRegionVariantTest() throws BrutException { + compareValuesFiles("values-b+ast+Latn+IT+AREVELA/strings.xml"); + compareValuesFiles("values-b+ast+Hant+IT+ARABEXT/strings.xml"); + } + @Test public void confirmZeroByteFileExtensionIsNotStored() throws BrutException { ApkInfo apkInfo = ApkInfo.load(sTestNewDir); diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+ast+Hant+IT+ARABEXT/strings.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+ast+Hant+IT+ARABEXT/strings.xml new file mode 100644 index 0000000000..8a56a54a26 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+ast+Hant+IT+ARABEXT/strings.xml @@ -0,0 +1,4 @@ + + + testapp + diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+ast+Latn+IT+AREVELA/strings.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+ast+Latn+IT+AREVELA/strings.xml new file mode 100644 index 0000000000..8a56a54a26 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+ast+Latn+IT+AREVELA/strings.xml @@ -0,0 +1,4 @@ + + + testapp + diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+de+CH+1901/strings.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+de+CH+1901/strings.xml new file mode 100644 index 0000000000..8a56a54a26 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+de+CH+1901/strings.xml @@ -0,0 +1,4 @@ + + + testapp + diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+iw+660/strings.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+iw+660/strings.xml new file mode 100644 index 0000000000..8a56a54a26 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/res/values-b+iw+660/strings.xml @@ -0,0 +1,4 @@ + + + testapp +