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
+