diff --git a/INTERNAL.md b/INTERNAL.md index 24e79772..57ea9cd0 100644 --- a/INTERNAL.md +++ b/INTERNAL.md @@ -200,7 +200,7 @@ to get the source downloaded. This is no small download, expect to use 40-60GB. After that, you need to build AOSP via this [documentation](https://source.android.com/source/building.html) guide. Now we aren't building the entire AOSP package, the initial build is to just see if you are capable of building it. -We check out a certain tag. Currently we use `android-7.1.1_r4`. +We check out a certain tag. Currently we use `android-7.1.2_r11`. ### Including our modified `frameworks/base` package. @@ -239,3 +239,22 @@ we lose the ability to quickly build just the aapt binary. So the Windows proced 1. `source build/envsetup.sh` 2. `lunch sdk-eng` 3. `make OUT_DIR=out-x64 LOCAL_MULTILIB=64 USE_NINJA=false aapt` + +# Gradle Tips n Tricks + + ./gradlew build shadowJar proguard -x test + +This skips the testing suite (which currently takes 2-4 minutes). Use this when making quick builds and save the testing +suite before pushing to GitHub. + + ./gradlew build shadowJar proguard -Dtest.debug + +This enables debugging on the test suite. This starts the debugger on port 5005 which you can connect with IntelliJ. + + ./gradlew :brut.apktool:apktool-lib:test ---tests "*BuildAndDecodeTest" + +This runs the library project of Apktool, selecting a specific test to run. Comes in handy when writing a new test and +only wanting to run that one. The asterisk is used to the full path to the test can be ignored. You can additionally +match this with the debugging parameter to debug a specific test. This command can be found below. + + ./gradlew :brut.apktool:apktool-lib:test --tests "*BuildAndDecodeTest" -Dtest.debug diff --git a/README.md b/README.md index e42097a3..4f04b00c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ It is NOT intended for piracy and other non-legal uses. It could be used for loc - [Project Page](http://ibotpeaches.github.io/Apktool/) - [#apktool on freenode](http://webchat.freenode.net/?channels=apktool) +#### Sponsored by + +* [Sourcetoad](https://www.sourcetoad.com/cool-tools/apktool/) - helping with a weekly sponsorship for continued improvement and maintenance of the project. + #### Links - [Downloads](https://bitbucket.org/iBotPeaches/apktool/downloads) - [Downloads Mirror](http://connortumbleson.com/apktool/) diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java index d51cb8d4..9c61d964 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java @@ -19,6 +19,7 @@ package brut.androlib; import brut.androlib.meta.MetaInfo; import brut.androlib.meta.UsesFramework; import brut.androlib.res.AndrolibResources; +import brut.androlib.res.data.ResConfigFlags; import brut.androlib.res.data.ResPackage; import brut.androlib.res.data.ResTable; import brut.androlib.res.data.ResUnknownFiles; @@ -279,7 +280,22 @@ public class Androlib { mAndRes.setSharedLibrary(meta.sharedLibrary); if (meta.sdkInfo != null && meta.sdkInfo.get("minSdkVersion") != null) { - mMinSdkVersion = Integer.parseInt(meta.sdkInfo.get("minSdkVersion")); + String minSdkVersion = meta.sdkInfo.get("minSdkVersion"); + + // Preview builds use short letter for API versions + switch (minSdkVersion) { + case "M": + mMinSdkVersion = ResConfigFlags.SDK_MNC; + break; + case "N": + mMinSdkVersion = ResConfigFlags.SDK_NOUGAT; + break; + case "O": + mMinSdkVersion = ResConfigFlags.SDK_O; + break; + default: + mMinSdkVersion = Integer.parseInt(meta.sdkInfo.get("minSdkVersion")); + } } if (outFile == null) { 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 0c4aaf5f..333c3aa7 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 @@ -507,6 +507,9 @@ public class ResConfigFlags { public final static byte SDK_LOLLIPOP = 21; public final static byte SDK_LOLLIPOP_MR1 = 22; public final static byte SDK_MNC = 23; + public final static byte SDK_NOUGAT = 24; + public final static byte SDK_NOUGAT_MR1 = 25; + public final static byte SDK_O = 26; public final static byte ORIENTATION_ANY = 0; public final static byte ORIENTATION_PORT = 1; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java index aa0741d1..875142b0 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java @@ -90,7 +90,8 @@ public class ResValueFactory { if (key == ResAttr.BAG_KEY_ATTR_TYPE) { return ResAttr.factory(parentVal, items, this, mPackage); } - if (key == ResArrayValue.BAG_KEY_ARRAY_START) { + // Android O Preview added an unknown enum for ResTable_map. This is hardcoded as 0 for now. + if (key == ResArrayValue.BAG_KEY_ARRAY_START || key == 0) { return new ResArrayValue(parentVal, items); } if (key >= ResPluralsValue.BAG_KEY_PLURALS_START && key <= ResPluralsValue.BAG_KEY_PLURALS_END) { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java index 9ce1be8a..130c590e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java @@ -20,6 +20,8 @@ import brut.androlib.AndrolibException; import brut.androlib.err.CantFind9PatchChunk; import brut.util.ExtDataInput; import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; import java.io.*; import javax.imageio.ImageIO; import javax.imageio.ImageTypeSpecifier; @@ -39,8 +41,24 @@ public class Res9patchStreamDecoder implements ResStreamDecoder { BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); int w = im.getWidth(), h = im.getHeight(); - BufferedImage im2 = new BufferedImage(w+2, h+2, BufferedImage.TYPE_INT_ARGB); - im2.createGraphics().drawImage(im, 1, 1, w, h, null); + BufferedImage im2 = new BufferedImage(w + 2, h + 2, BufferedImage.TYPE_INT_ARGB); + if (im.getType() == BufferedImage.TYPE_CUSTOM) { + //TODO: Ensure this is gray + alpha case? + Raster srcRaster = im.getRaster(); + WritableRaster dstRaster = im2.getRaster(); + int[] gray = null, alpha = null; + for (int y = 0; y < im.getHeight(); y++) { + gray = srcRaster.getSamples(0, y, w, 1, 0, gray); + alpha = srcRaster.getSamples(0, y, w, 1, 1, alpha); + + dstRaster.setSamples(1, y + 1, w, 1, 0, gray); + dstRaster.setSamples(1, y + 1, w, 1, 1, gray); + dstRaster.setSamples(1, y + 1, w, 1, 2, gray); + dstRaster.setSamples(1, y + 1, w, 1, 3, alpha); + } + } else { + im2.createGraphics().drawImage(im, 1, 1, w, h, null); + } NinePatch np = getNinePatch(data); drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); @@ -56,6 +74,34 @@ public class Res9patchStreamDecoder implements ResStreamDecoder { drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); } + // Some images additionally use Optical Bounds + // https://developer.android.com/about/versions/android-4.3.html#OpticalBounds + try { + OpticalInset oi = getOpticalInset(data); + + for (int i = 0; i < oi.layoutBoundsLeft; i++) { + int x = 1 + i; + im2.setRGB(x, h + 1, OI_COLOR); + } + + for (int i = 0; i < oi.layoutBoundsRight; i++) { + int x = w - i; + im2.setRGB(x, h + 1, OI_COLOR); + } + + for (int i = 0; i < oi.layoutBoundsTop; i++) { + int y = 1 + i; + im2.setRGB(w + 1, y, OI_COLOR); + } + + for (int i = 0; i < oi.layoutBoundsBottom; i++) { + int y = h - i; + im2.setRGB(w + 1, y, OI_COLOR); + } + } catch (CantFind9PatchChunk t) { + // This chunk might not exist + } + ImageIO.write(im2, "png", out); } catch (IOException ex) { throw new AndrolibException(ex); @@ -70,11 +116,18 @@ public class Res9patchStreamDecoder implements ResStreamDecoder { private NinePatch getNinePatch(byte[] data) throws AndrolibException, IOException { ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); - find9patchChunk(di); + find9patchChunk(di, NP_CHUNK_TYPE); return NinePatch.decode(di); } - private void find9patchChunk(DataInput di) throws AndrolibException, + private OpticalInset getOpticalInset(byte[] data) throws AndrolibException, + IOException { + ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); + find9patchChunk(di, OI_CHUNK_TYPE); + return OpticalInset.decode(di); + } + + private void find9patchChunk(DataInput di, int magic) throws AndrolibException, IOException { di.skipBytes(8); while (true) { @@ -84,7 +137,7 @@ public class Res9patchStreamDecoder implements ResStreamDecoder { } catch (IOException ex) { throw new CantFind9PatchChunk("Cant find nine patch chunk", ex); } - if (di.readInt() == NP_CHUNK_TYPE) { + if (di.readInt() == magic) { return; } di.skipBytes(size + 4); @@ -104,7 +157,9 @@ public class Res9patchStreamDecoder implements ResStreamDecoder { } private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc + private static final int OI_CHUNK_TYPE = 0x6e704c62; // npLb private static final int NP_COLOR = 0xff000000; + private static final int OI_COLOR = 0xffff0000; private static class NinePatch { public final int padLeft, padRight, padTop, padBottom; @@ -121,16 +176,16 @@ public class Res9patchStreamDecoder implements ResStreamDecoder { } public static NinePatch decode(ExtDataInput di) throws IOException { - di.skipBytes(1); + di.skipBytes(1); // wasDeserialized byte numXDivs = di.readByte(); byte numYDivs = di.readByte(); - di.skipBytes(1); - di.skipBytes(8); + di.skipBytes(1); // numColors + di.skipBytes(8); // xDivs/yDivs offset int padLeft = di.readInt(); int padRight = di.readInt(); int padTop = di.readInt(); int padBottom = di.readInt(); - di.skipBytes(4); + di.skipBytes(4); // colorsOffset int[] xDivs = di.readIntArray(numXDivs); int[] yDivs = di.readIntArray(numYDivs); @@ -138,4 +193,25 @@ public class Res9patchStreamDecoder implements ResStreamDecoder { yDivs); } } + + private static class OpticalInset { + public final int layoutBoundsLeft, layoutBoundsTop, layoutBoundsRight, layoutBoundsBottom; + + public OpticalInset(int layoutBoundsLeft, int layoutBoundsTop, + int layoutBoundsRight, int layoutBoundsBottom) { + this.layoutBoundsLeft = layoutBoundsLeft; + this.layoutBoundsTop = layoutBoundsTop; + this.layoutBoundsRight = layoutBoundsRight; + this.layoutBoundsBottom = layoutBoundsBottom; + } + + public static OpticalInset decode(ExtDataInput di) throws IOException { + int layoutBoundsLeft = Integer.reverseBytes(di.readInt()); + int layoutBoundsTop = Integer.reverseBytes(di.readInt()); + int layoutBoundsRight = Integer.reverseBytes(di.readInt()); + int layoutBoundsBottom = Integer.reverseBytes(di.readInt()); + return new OpticalInset(layoutBoundsLeft, layoutBoundsTop, + layoutBoundsRight, layoutBoundsBottom); + } + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java index d6f03c8a..dd692f41 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java @@ -131,8 +131,14 @@ public class StringBlock { if (style == null) { return ResXmlEncoders.escapeXmlChars(raw); } + + // If the returned style is further in string, than string length. Lets skip it. + if (style[1] > raw.length()) { + return ResXmlEncoders.escapeXmlChars(raw); + } StringBuilder html = new StringBuilder(raw.length() + 32); int[] opened = new int[style.length / 3]; + boolean[] unclosed = new boolean[style.length / 3]; int offset = 0, depth = 0; while (true) { int i = -1, j; @@ -149,6 +155,9 @@ public class StringBlock { int last = opened[j]; int end = style[last + 2]; if (end >= start) { + if (style[last + 1] == -1 && end != -1) { + unclosed[j] = true; + } break; } if (offset <= end) { @@ -160,6 +169,11 @@ public class StringBlock { depth = j + 1; if (offset < start) { html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset, start))); + if (j >= 0 && unclosed.length >= j && unclosed[j]) { + if (unclosed.length > (j + 1) && unclosed[j + 1] || unclosed.length == 1) { + outputStyleTag(getString(style[opened[j]]), html, true); + } + } offset = start; } if (i == -1) { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java index 5d91e433..4b5036a3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java @@ -246,6 +246,8 @@ public final class ResXmlPatcher { throws IOException, SAXException, ParserConfigurationException { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + docFactory.setFeature(FEATURE_DISABLE_DOCTYPE_DECL, true); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); return docBuilder.parse(file); } @@ -264,10 +266,10 @@ public final class ResXmlPatcher { TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.STANDALONE,"yes"); DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult(file); transformer.transform(source, result); } + + private static final String FEATURE_DISABLE_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl"; } diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/32/aapt b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/32/aapt index 72ad574b..85915dd6 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/32/aapt and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/32/aapt differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/64/aapt b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/64/aapt index d09b6145..8848ff93 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/64/aapt and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/64/aapt differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/32/aapt b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/32/aapt index e0fc2525..ffca404a 100644 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/32/aapt and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/32/aapt differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/64/aapt b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/64/aapt index e31b2821..79a74413 100644 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/64/aapt and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/64/aapt differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/windows/aapt.exe b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/windows/aapt.exe index 10ca6130..8e701201 100755 Binary files a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/windows/aapt.exe and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/windows/aapt.exe differ diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/AndResGuardTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/AndResGuardTest.java index 129db300..50a8c3ea 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/AndResGuardTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/AndResGuardTest.java @@ -21,7 +21,6 @@ import brut.common.BrutException; import brut.util.OS; import java.io.File; import java.io.IOException; -import java.util.logging.Logger; import org.junit.*; import static org.junit.Assert.*; @@ -29,10 +28,10 @@ import static org.junit.Assert.*; public class AndResGuardTest { @BeforeClass - public static void beforeClass() throws Exception, BrutException { + public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); sTmpDir = new ExtFile(OS.createTempDirectory()); - TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/issue1170/", sTmpDir); + TestUtils.copyResourceDir(AndResGuardTest.class, "brut/apktool/issue1170/", sTmpDir); } @AfterClass @@ -62,6 +61,4 @@ public class AndResGuardTest { private static ExtFile sTmpDir; private static ExtFile sTestOrigDir; - - private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeTest.class.getName()); -} +} \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeJarTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeJarTest.java index 5ed1fa72..e53a142c 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeJarTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeJarTest.java @@ -33,7 +33,7 @@ import static org.junit.Assert.assertTrue; public class BuildAndDecodeJarTest { @BeforeClass - public static void beforeClass() throws Exception, BrutException { + public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); sTmpDir = new ExtFile(OS.createTempDirectory()); sTestOrigDir = new ExtFile(sTmpDir, "testjar-orig"); @@ -66,4 +66,4 @@ public class BuildAndDecodeJarTest { private static ExtFile sTestNewDir; private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName()); -} +} \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java index da251858..0d0622a0 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java @@ -44,7 +44,7 @@ import javax.imageio.ImageIO; public class BuildAndDecodeTest { @BeforeClass - public static void beforeClass() throws Exception, BrutException { + public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); sTmpDir = new ExtFile(OS.createTempDirectory()); @@ -253,6 +253,11 @@ public class BuildAndDecodeTest { compareValuesFiles("values-ast/strings.xml"); } + @Test + public void androidOStringTest() throws BrutException, IOException { + compareValuesFiles("values-ast/strings.xml"); + } + @Test public void twoLetterNotHandledAsBcpTest() throws BrutException, IOException { checkFolderExists("res/values-fr"); @@ -345,6 +350,89 @@ public class BuildAndDecodeTest { assertEquals(controlImage.getRGB(30, 30), testImage.getRGB(30, 30)); } + @Test + public void issue1508Test() throws BrutException, IOException { + char slash = File.separatorChar; + String location = slash + "res" + slash + "drawable-xhdpi" + slash; + + File control = new File((sTestOrigDir + location), "btn_zoom_up_normal.9.png"); + File test = new File((sTestNewDir + location), "btn_zoom_up_normal.9.png"); + + BufferedImage controlImage = ImageIO.read(control); + BufferedImage testImage = ImageIO.read(test); + + // 0, 0 = clear + assertEquals(controlImage.getRGB(0, 0), testImage.getRGB(0, 0)); + + // 30, 0 = black line + assertEquals(controlImage.getRGB(0, 30), testImage.getRGB(0, 30)); + + // 30, 30 = greyish button + assertEquals(controlImage.getRGB(30, 30), testImage.getRGB(30, 30)); + } + + @Test + public void issue1511Test() throws BrutException, IOException { + char slash = File.separatorChar; + String location = slash + "res" + slash + "drawable-xxhdpi" + slash; + + File control = new File((sTestOrigDir + location), "textfield_activated_holo_dark.9.png"); + File test = new File((sTestNewDir + location), "textfield_activated_holo_dark.9.png"); + + BufferedImage controlImage = ImageIO.read(control); + BufferedImage testImage = ImageIO.read(test); + + // Check entire image as we cannot mess this up + final int w = controlImage.getWidth(), + h = controlImage.getHeight(); + + final int[] controlImageGrid = controlImage.getRGB(0, 0, w, h, null, 0, w); + final int[] testImageGrid = testImage.getRGB(0, 0, w, h, null, 0, w); + + for (int i = 0; i < controlImageGrid.length; i++) { + assertEquals("Image lost Optical Bounds at i = " + i, controlImageGrid[i], testImageGrid[i]); + } + } + + @Test + public void robust9patchTest() throws BrutException, IOException { + String[] ninePatches = {"ic_notification_overlay.9.png", "status_background.9.png", + "search_bg_transparent.9.png", "screenshot_panel.9.png", "recents_lower_gradient.9.png"}; + + char slash = File.separatorChar; + String location = slash + "res" + slash + "drawable-xxhdpi" + slash; + + for (String ninePatch : ninePatches) { + File control = new File((sTestOrigDir + location), ninePatch); + File test = new File((sTestNewDir + location), ninePatch); + + BufferedImage controlImage = ImageIO.read(control); + BufferedImage testImage = ImageIO.read(test); + + int w = controlImage.getWidth(), h = controlImage.getHeight(); + + // Check the entire horizontal line + for (int i = 1; i < w; i++) { + if (isTransparent(controlImage.getRGB(i, 0))) { + assertTrue(isTransparent(testImage.getRGB(i, 0))); + } else { + assertEquals("Image lost npTc chunk on image " + ninePatch + " at (x, y) (" + i + "," + 0 + ")", + controlImage.getRGB(i, 0), testImage.getRGB(i, 0)); + } + } + + // Check the entire vertical line + for (int i = 1; i < h; i++) { + if (isTransparent(controlImage.getRGB(0, i))) { + assertTrue(isTransparent(testImage.getRGB(0, i))); + } else { + assertEquals("Image lost npTc chunk on image " + ninePatch + " at (x, y) (" + 0 + "," + i + ")", + controlImage.getRGB(0, i), testImage.getRGB(0, i)); + } + } + } + } + @Test public void drawableXxhdpiTest() throws BrutException, IOException { compareResFolder("drawable-xxhdpi"); @@ -465,6 +553,10 @@ public class BuildAndDecodeTest { assertTrue(f.isDirectory()); } + private boolean isTransparent(int pixel) { + return pixel >> 24 == 0x00; + } + private void compareXmlFiles(String path, ElementQualifier qualifier) throws BrutException { DetailedDiff diff; try { diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/DebugTagRetainedTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/DebugTagRetainedTest.java index fb77867a..e1ebaf0c 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/DebugTagRetainedTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/DebugTagRetainedTest.java @@ -37,7 +37,7 @@ import static org.junit.Assert.assertTrue; public class DebugTagRetainedTest { @BeforeClass - public static void beforeClass() throws Exception, BrutException { + public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); sTmpDir = new ExtFile(OS.createTempDirectory()); sTestOrigDir = new ExtFile(sTmpDir, "issue1235-orig"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/DefaultBaksmaliVariableTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/DefaultBaksmaliVariableTest.java index f601ced3..e3a50767 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/DefaultBaksmaliVariableTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/DefaultBaksmaliVariableTest.java @@ -18,12 +18,12 @@ import static org.junit.Assert.assertEquals; public class DefaultBaksmaliVariableTest { @BeforeClass - public static void beforeClass() throws Exception, BrutException { + public static void beforeClass() throws Exception { sTmpDir = new ExtFile(OS.createTempDirectory()); sTestOrigDir = new ExtFile(sTmpDir, "testjar-orig"); sTestNewDir = new ExtFile(sTmpDir, "testjar-new"); LOGGER.info("Unpacking testjar..."); - TestUtils.copyResourceDir(BuildAndDecodeJarTest.class, "brut/apktool/issue1481/", sTestOrigDir); + TestUtils.copyResourceDir(DefaultBaksmaliVariableTest.class, "brut/apktool/issue1481/", sTestOrigDir); LOGGER.info("Building issue1481.jar..."); File testJar = new File(sTmpDir, "issue1481.jar"); @@ -99,5 +99,5 @@ public class DefaultBaksmaliVariableTest { private static ExtFile sTestOrigDir; private static ExtFile sTestNewDir; - private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName()); + private final static Logger LOGGER = Logger.getLogger(DefaultBaksmaliVariableTest.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/DoubleExtensionUnknownFileTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/DoubleExtensionUnknownFileTest.java index 55b745ce..d5f5c6ea 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/DoubleExtensionUnknownFileTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/DoubleExtensionUnknownFileTest.java @@ -27,7 +27,6 @@ import org.junit.Test; import java.io.File; import java.io.IOException; -import java.util.logging.Logger; import static org.junit.Assert.assertTrue; @@ -37,10 +36,10 @@ import static org.junit.Assert.assertTrue; public class DoubleExtensionUnknownFileTest { @BeforeClass - public static void beforeClass() throws Exception, BrutException { + public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); sTmpDir = new ExtFile(OS.createTempDirectory()); - TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/issue1244/", sTmpDir); + TestUtils.copyResourceDir(DoubleExtensionUnknownFileTest.class, "brut/apktool/issue1244/", sTmpDir); } @AfterClass @@ -67,6 +66,4 @@ public class DoubleExtensionUnknownFileTest { } private static ExtFile sTmpDir; - - private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName()); } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/ExternalEntityTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ExternalEntityTest.java new file mode 100644 index 00000000..4fb56f43 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ExternalEntityTest.java @@ -0,0 +1,76 @@ +/** + * Copyright 2014 Ryszard Wiśniewski + * Copyright 2016 Connor Tumbleson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package brut.androlib; + +import brut.directory.ExtFile; +import brut.common.BrutException; +import brut.util.OS; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.logging.Logger; + +import static org.junit.Assert.assertEquals; + +/** + * @author Connor Tumbleson + */ +public class ExternalEntityTest { + + @BeforeClass + public static void beforeClass() throws Exception { + sOrigDir = new ExtFile(OS.createTempDirectory()); + TestUtils.copyResourceDir(ExternalEntityTest.class, "brut/apktool/doctype/", sOrigDir); + + LOGGER.info("Building doctype.apk..."); + File testApk = new File(sOrigDir, "doctype.apk"); + new Androlib().build(sOrigDir, testApk); + + LOGGER.info("Decoding doctype.apk..."); + ApkDecoder apkDecoder = new ApkDecoder(testApk); + apkDecoder.setOutDir(new File(sOrigDir + File.separator + "output")); + apkDecoder.decode(); + } + + @AfterClass + public static void afterClass() throws BrutException { + OS.rmdir(sOrigDir); + } + + @Test + public void doctypeTest() throws BrutException, IOException { + + String expected = TestUtils.replaceNewlines("\n" + + "\n" + + " \n" + + ""); + + byte[] encoded = Files.readAllBytes(Paths.get(sOrigDir + File.separator + "output" + File.separator + "AndroidManifest.xml")); + String obtained = TestUtils.replaceNewlines(new String(encoded)); + assertEquals(expected, obtained); + } + + private static ExtFile sOrigDir; + + private final static Logger LOGGER = Logger.getLogger(ExternalEntityTest.class.getName()); +} \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/LargeIntsInManifestTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/LargeIntsInManifestTest.java index 74e03c99..6af2c0d1 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/LargeIntsInManifestTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/LargeIntsInManifestTest.java @@ -22,7 +22,6 @@ import brut.util.OS; import java.io.*; import java.io.File; import java.io.IOException; -import java.util.logging.Logger; import org.custommonkey.xmlunit.*; import org.junit.*; @@ -32,7 +31,7 @@ import org.xml.sax.SAXException; public class LargeIntsInManifestTest { @BeforeClass - public static void beforeClass() throws Exception, BrutException { + public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); sTmpDir = new ExtFile(OS.createTempDirectory()); TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/issue767/", sTmpDir); @@ -94,6 +93,4 @@ public class LargeIntsInManifestTest { private static ExtFile sTmpDir; private static ExtFile sTestOrigDir; private static ExtFile sTestNewDir; - - private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeTest.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/MissingVersionManifestTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/MissingVersionManifestTest.java index e93e1dc9..71298ab3 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/MissingVersionManifestTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/MissingVersionManifestTest.java @@ -35,10 +35,10 @@ import static org.junit.Assert.assertEquals; public class MissingVersionManifestTest { @BeforeClass - public static void beforeClass() throws Exception, BrutException { + public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); sTmpDir = new ExtFile(OS.createTempDirectory()); - TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/issue1264/", sTmpDir); + TestUtils.copyResourceDir(MissingVersionManifestTest.class, "brut/apktool/issue1264/", sTmpDir); } @AfterClass @@ -61,6 +61,4 @@ public class MissingVersionManifestTest { } private static ExtFile sTmpDir; - - private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName()); } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/PositionalEnumerationTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/PositionalEnumerationTest.java index e16ef0a7..ad134f2b 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/PositionalEnumerationTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/PositionalEnumerationTest.java @@ -17,7 +17,6 @@ package brut.androlib; import brut.androlib.res.xml.ResXmlEncoders; import org.junit.Test; -import java.util.logging.Logger; import static org.junit.Assert.assertEquals; @@ -54,6 +53,4 @@ public class PositionalEnumerationTest { private String enumerateArguments(String value) { return ResXmlEncoders.enumerateNonPositionalSubstitutionsIfRequired(value); } - - private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName()); } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/ProviderAttributeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ProviderAttributeTest.java index 242b4af1..8d075e82 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/ProviderAttributeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ProviderAttributeTest.java @@ -84,4 +84,4 @@ public class ProviderAttributeTest { } private static ExtFile sTmpDir; -} +} \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/ReferenceVersionCodeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ReferenceVersionCodeTest.java index 533a739d..18e51f28 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/ReferenceVersionCodeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ReferenceVersionCodeTest.java @@ -25,7 +25,6 @@ import org.junit.Test; import java.io.File; import java.io.IOException; -import java.util.logging.Logger; import static org.junit.Assert.assertEquals; @@ -35,10 +34,10 @@ import static org.junit.Assert.assertEquals; public class ReferenceVersionCodeTest { @BeforeClass - public static void beforeClass() throws Exception, BrutException { + public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); sTmpDir = new ExtFile(OS.createTempDirectory()); - TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/issue1234/", sTmpDir); + TestUtils.copyResourceDir(ReferenceVersionCodeTest.class, "brut/apktool/issue1234/", sTmpDir); } @AfterClass @@ -61,6 +60,4 @@ public class ReferenceVersionCodeTest { } private static ExtFile sTmpDir; - - private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName()); } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/UnknownCompressionTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/UnknownCompressionTest.java index 39e5e2bc..5b64b0c9 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/UnknownCompressionTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/UnknownCompressionTest.java @@ -25,7 +25,6 @@ import org.junit.Test; import java.io.File; import java.io.IOException; -import java.util.logging.Logger; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; @@ -36,10 +35,10 @@ import static org.junit.Assert.assertNotSame; public class UnknownCompressionTest { @BeforeClass - public static void beforeClass() throws Exception, BrutException { + public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); sTmpDir = new ExtFile(OS.createTempDirectory()); - TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/unknown_compression/", sTmpDir); + TestUtils.copyResourceDir(UnknownCompressionTest.class, "brut/apktool/unknown_compression/", sTmpDir); String apk = "deflated_unknowns.apk"; ApkOptions apkOptions = new ApkOptions(); @@ -90,6 +89,4 @@ public class UnknownCompressionTest { private static ExtFile sOriginalFile; private static ExtFile sBuiltFile; - - private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName()); } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/VectorDrawableTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/VectorDrawableTest.java index bc229b1f..64e93061 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/VectorDrawableTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/VectorDrawableTest.java @@ -25,7 +25,6 @@ import org.junit.Test; import java.io.File; import java.io.IOException; -import java.util.logging.Logger; import static org.junit.Assert.assertTrue; @@ -66,6 +65,4 @@ public class VectorDrawableTest { private static ExtFile sTmpDir; private static ExtFile sTestOrigDir; - - private final static Logger LOGGER = Logger.getLogger(VectorDrawableTest.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/doctype/AndroidManifest.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/doctype/AndroidManifest.xml new file mode 100644 index 00000000..0d8c43fa --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/doctype/AndroidManifest.xml @@ -0,0 +1,6 @@ + +]> + + &e1; + + \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/doctype/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/doctype/apktool.yml new file mode 100644 index 00000000..f8744a9b --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/doctype/apktool.yml @@ -0,0 +1,12 @@ +version: 2.0.0 +apkFileName: doctype.apk +isFrameworkApk: false +usesFramework: + ids: + - 1 +packageInfo: + forced-package-id: '127' +versionInfo: + versionCode: '1' + versionName: '1.0' +compressionType: false \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/btn_zoom_up_normal.9.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/btn_zoom_up_normal.9.png new file mode 100644 index 00000000..7f73df09 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/btn_zoom_up_normal.9.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/ic_notification_overlay.9.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/ic_notification_overlay.9.png new file mode 100644 index 00000000..fa7de0ed Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/ic_notification_overlay.9.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/recents_lower_gradient.9.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/recents_lower_gradient.9.png new file mode 100644 index 00000000..427cad9f Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/recents_lower_gradient.9.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/screenshot_panel.9.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/screenshot_panel.9.png new file mode 100644 index 00000000..45259d7f Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/screenshot_panel.9.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/search_bg_transparent.9.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/search_bg_transparent.9.png new file mode 100644 index 00000000..c0bf31d0 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/search_bg_transparent.9.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/status_background.9.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/status_background.9.png new file mode 100644 index 00000000..727c8040 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/status_background.9.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/textfield_activated_holo_dark.9.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/textfield_activated_holo_dark.9.png new file mode 100644 index 00000000..e3139b48 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/textfield_activated_holo_dark.9.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-ar-rXB/strings.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-ar-rXB/strings.xml new file mode 100644 index 00000000..687db601 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-ar-rXB/strings.xml @@ -0,0 +1,9 @@ + + + "‏‮Forgot‬‏ ‏‮your‬‏ ‏‮username‬‏ ‏‮or‬‏ ‏‮password?‬‏\n‏‮Visit‬‏ ‏‮google.com/accounts/recover‬‏‏‮y‬‏‏‮.‬‏" + ‏‮Forgot‬‏ ‏‮your‬‏ ‏‮username‬‏ ‏‮or‬‏ ‏‮password?‬\n.‏‮Visit‬‏ ‏‮google.com/accounts/recover‬‏‏‮y‬‏ + (string8) "‏‮Forgot‬‏ ‏‮your‬‏ ‏‮username‬‏ ‏‮or‬‏ ‏‮password?‬‏\n‏‮Visit‬‏ ‏‮google.com/accounts/recover‬‏‏‮y‬‏‏‮.‬‏" + Forgot your username or password?\nVisit google.com/accounts/recovery. + ‏‮Forgot‬‏ ‏‮your‬‏ ‏‮username‬‏ ‏‮or‬‏ ‏‮password?‬‏ + ‏‮Visit‬‏ ‏‮google.com/accounts/recover‬‏‏‮y‬‏‏‮.‬‏ + \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml index a9adad9a..65ccbe69 100644 --- a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml @@ -31,4 +31,14 @@ bar" category=temp%temp%foo res/foo/ res/foo + [TEST STRING] + [TEST STRING] + [TEST STRING] + [TEST STRING] + TEST STRING + [Ţåþ ţö ţýþé þåššŵöŕð one two three] + [Ţåþ ţö ţýþé þåššŵöŕð one two three] + [Ţåþ ţö ţýþé þåššŵöŕð one two three] + []Ţåþ ţö ţýþé þåššŵöŕð one two three + [Ţåþ ţö ţýþé þåššŵöŕð one two three] diff --git a/build.gradle b/build.gradle index ba3f344f..868dc2f1 100644 --- a/build.gradle +++ b/build.gradle @@ -82,7 +82,7 @@ subprojects { ext { depends = [ - baksmali: 'org.smali:baksmali:2.2.0', + baksmali: 'org.smali:baksmali:2.2.1', commons_cli: 'commons-cli:commons-cli:1.4', commons_io: 'commons-io:commons-io:2.4', commons_lang: 'org.apache.commons:commons-lang3:3.1', @@ -91,7 +91,7 @@ subprojects { junit: 'junit:junit:4.12', proguard_gradle: 'net.sf.proguard:proguard-gradle:5.2.1', snakeyaml: 'org.yaml:snakeyaml:1.17', - smali: 'org.smali:smali:2.2.0', + smali: 'org.smali:smali:2.2.1', xmlpull: 'xpp3:xpp3:1.1.4c', xmlunit: 'xmlunit:xmlunit:1.3', ]