From 542b66cbd0b045ac5739b54f66d41c3994ff9769 Mon Sep 17 00:00:00 2001 From: Igor Eisberg <8811086+IgorEisberg@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:55:13 +0200 Subject: [PATCH] refactor: ExtDataInput rework, source layout and formatting (#3738) * refactor: ExtDataInput rework, source layout and formatting Refactor ExtDataInput classes: ExtDataInput is now the extended interface, ExtDataInputStream is an easy-to-use FilterInputStream implementing ExtDataInput with static creator methods for big-endian and little-endian wrappers. Refactor AaptManager class: unify aapt-related verifications to one class. Replace Apache Commons' deprecated CountingInputStream with Google Guava's equivalent with the same name. Apache's BoundedInputStream is an overkill for our use case and its constructors are deprecated as well. Normalize source layout to have a common and somewhat more standard order: Static fields first, instance fields after, methods last. Fix some formatting, like empty spaces or extra spaces and exception messages. Renamed ResXmlPatcher to ResXmlUtils, as it has more purposes than just patching. Renamed DirUtil to DirUtils, to match other utility classes naming convention. Moved "properties/apktool.properties" to jar's root, to match smali/baksmali. Moved Android Framework to "prebuilt", as it is just a prebuilt, looks out of place among .class files. @SuppressWarnings removed from Duo as there are quite a few unsafe assignments of raw Duo[] instances to parameterized Duo<> variables in the project, this is just Java being the primitive boilerplate it is, no point in fighting it. No end-user changes. Tested against a full ROM decompile/recompile, no issues found. * small tweak * last refinement * missed a stream --- .../src/main/java/brut/apktool/Main.java | 55 +-- brut.apktool/apktool-lib/build.gradle.kts | 5 +- .../content/res/XmlResourceParser.java | 2 +- .../main/java/android/util/TypedValue.java | 9 +- .../main/java/brut/androlib/AaptInvoker.java | 45 +-- .../main/java/brut/androlib/ApkBuilder.java | 48 +-- .../main/java/brut/androlib/ApkDecoder.java | 13 +- .../java/brut/androlib/ApktoolProperties.java | 12 +- .../java/brut/androlib/BackgroundWorker.java | 7 +- .../src/main/java/brut/androlib/Config.java | 41 +-- .../main/java/brut/androlib/apk/ApkInfo.java | 13 +- .../main/java/brut/androlib/apk/YamlLine.java | 3 +- .../java/brut/androlib/apk/YamlReader.java | 3 +- .../brut/androlib/apk/YamlSerializable.java | 1 + .../androlib/apk/YamlStringEscapeUtils.java | 48 +-- .../java/brut/androlib/apk/YamlWriter.java | 4 +- .../exceptions/AXmlDecodingException.java | 1 + .../exceptions/AndrolibException.java | 10 +- .../CantFind9PatchChunkException.java | 1 + .../CantFindFrameworkResException.java | 10 +- .../exceptions/InFileNotFoundException.java | 2 + .../exceptions/OutDirExistsException.java | 2 + .../RawXmlEncounteredException.java | 1 + .../UndefinedResObjectException.java | 1 + .../main/java/brut/androlib/mod/SmaliMod.java | 13 +- .../java/brut/androlib/res/Framework.java | 63 ++-- .../brut/androlib/res/ResourcesDecoder.java | 81 +++-- .../androlib/res/data/ResConfigFlags.java | 324 +++++++++--------- .../java/brut/androlib/res/data/ResID.java | 4 +- .../brut/androlib/res/data/ResPackage.java | 30 +- .../brut/androlib/res/data/ResResSpec.java | 23 +- .../brut/androlib/res/data/ResResource.java | 6 +- .../java/brut/androlib/res/data/ResTable.java | 53 +-- .../java/brut/androlib/res/data/ResType.java | 5 +- .../brut/androlib/res/data/ResTypeSpec.java | 9 +- .../brut/androlib/res/data/ResValuesFile.java | 17 +- .../brut/androlib/res/data/arsc/ARSCData.java | 8 +- .../androlib/res/data/arsc/ARSCHeader.java | 75 ++-- .../androlib/res/data/arsc/EntryData.java | 6 +- .../res/data/axml/NamespaceStack.java | 108 +++--- .../res/data/ninepatch/NinePatchData.java | 27 +- .../res/data/ninepatch/OpticalInset.java | 11 +- .../res/data/value/ResArrayValue.java | 13 +- .../brut/androlib/res/data/value/ResAttr.java | 44 +-- .../androlib/res/data/value/ResBagValue.java | 2 +- .../androlib/res/data/value/ResBoolValue.java | 2 +- .../res/data/value/ResColorValue.java | 1 + .../res/data/value/ResDimenValue.java | 1 + .../res/data/value/ResEmptyValue.java | 6 +- .../androlib/res/data/value/ResEnumAttr.java | 11 +- .../androlib/res/data/value/ResFileValue.java | 2 +- .../androlib/res/data/value/ResFlagsAttr.java | 13 +- .../res/data/value/ResFloatValue.java | 2 +- .../res/data/value/ResFractionValue.java | 1 + .../androlib/res/data/value/ResIdValue.java | 1 + .../androlib/res/data/value/ResIntValue.java | 8 +- .../res/data/value/ResPluralsValue.java | 12 +- .../res/data/value/ResReferenceValue.java | 9 +- .../res/data/value/ResScalarValue.java | 5 +- .../res/data/value/ResStringValue.java | 6 +- .../res/data/value/ResStyleValue.java | 9 +- .../androlib/res/data/value/ResValue.java | 1 + .../res/data/value/ResValueFactory.java | 4 +- .../androlib/res/decoder/ARSCDecoder.java | 123 +++---- .../res/decoder/AXmlResourceParser.java | 133 ++++--- .../AndroidManifestResourceParser.java | 9 +- .../res/decoder/Res9patchStreamDecoder.java | 35 +- .../androlib/res/decoder/ResFileDecoder.java | 33 +- .../res/decoder/ResRawStreamDecoder.java | 1 + .../decoder/ResStreamDecoderContainer.java | 6 +- .../androlib/res/decoder/StringBlock.java | 75 ++-- .../androlib/res/decoder/StyledString.java | 94 ++--- .../res/xml/ResValuesXmlSerializable.java | 4 +- .../brut/androlib/res/xml/ResXmlEncoders.java | 4 + .../{ResXmlPatcher.java => ResXmlUtils.java} | 88 ++--- .../java/brut/androlib/src/SmaliBuilder.java | 29 +- .../java/brut/androlib/src/SmaliDecoder.java | 24 +- .../{properties => }/apktool.properties | 0 .../android-framework.jar | Bin .../src/test/java/brut/androlib/BaseTest.java | 45 +-- .../test/java/brut/androlib/TestUtils.java | 41 ++- .../aapt1/AndroidOreoNotSparseTest.java | 1 + .../androlib/aapt1/AndroidOreoSparseTest.java | 1 + .../aapt1/EmptyResourcesArscTest.java | 12 +- .../androlib/aapt2/NetworkConfigTest.java | 16 +- .../androlib/aapt2/NonStandardPkgIdTest.java | 3 +- .../brut/androlib/apk/ApkInfoReaderTest.java | 18 +- .../apk/ApkInfoSerializationTest.java | 11 +- .../androlib/apk/ConsistentPropertyTest.java | 2 +- .../apk/DoNotCompressHieroglyphTest.java | 2 +- .../brut/androlib/apk/MaliciousYamlTest.java | 2 +- .../ForceManifestDecodeNoResourcesTest.java | 11 +- .../androlib/decode/MissingDiv9PatchTest.java | 24 +- ...tringBlockWithSurrogatePairInUtf8Test.java | 32 +- .../brut/androlib/util/AaptVersionTest.java | 8 +- .../main/java/brut/common/BrutException.java | 16 +- .../common/InvalidUnknownFileException.java | 1 + .../brut/common/RootUnknownFileException.java | 1 + .../common/TraversalUnknownFileException.java | 1 + .../brut/directory/AbstractDirectory.java | 13 +- .../directory/{DirUtil.java => DirUtils.java} | 4 +- .../main/java/brut/directory/Directory.java | 4 +- .../brut/directory/DirectoryException.java | 24 +- .../src/main/java/brut/directory/ExtFile.java | 4 +- .../java/brut/directory/FileDirectory.java | 1 - .../brut/directory/PathAlreadyExists.java | 31 +- .../java/brut/directory/PathNotExist.java | 16 +- .../java/brut/directory/ZipRODirectory.java | 2 - .../main/java/brut/directory/ZipUtils.java | 6 +- .../src/main/java/brut/util/AaptManager.java | 84 ++--- .../src/main/java/brut/util/BrutIO.java | 7 +- .../java/brut/util/DataInputDelegate.java | 88 ----- brut.j.util/src/main/java/brut/util/Duo.java | 13 +- .../java/brut/util/ExtCountingDataInput.java | 72 ---- .../src/main/java/brut/util/ExtDataInput.java | 75 +--- .../java/brut/util/ExtDataInputStream.java | 217 ++++++++++++ brut.j.util/src/main/java/brut/util/Jar.java | 26 +- brut.j.util/src/main/java/brut/util/OS.java | 40 ++- .../src/main/java/brut/util/OSDetection.java | 6 +- .../main/java/brut/xmlpull/XmlPullUtils.java | 6 +- 120 files changed, 1485 insertions(+), 1513 deletions(-) rename brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/{ResXmlPatcher.java => ResXmlUtils.java} (91%) rename brut.apktool/apktool-lib/src/main/resources/{properties => }/apktool.properties (100%) rename brut.apktool/apktool-lib/src/main/resources/{brut/androlib => prebuilt}/android-framework.jar (100%) rename brut.j.dir/src/main/java/brut/directory/{DirUtil.java => DirUtils.java} (98%) delete mode 100644 brut.j.util/src/main/java/brut/util/DataInputDelegate.java delete mode 100644 brut.j.util/src/main/java/brut/util/ExtCountingDataInput.java create mode 100644 brut.j.util/src/main/java/brut/util/ExtDataInputStream.java diff --git a/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java b/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java index da8d52dd..eec8bd9c 100644 --- a/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java +++ b/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java @@ -36,6 +36,19 @@ import java.util.logging.*; * Main entry point of the apktool. */ public class Main { + private enum Verbosity { NORMAL, VERBOSE, QUIET } + + private static final Options normalOptions = new Options(); + private static final Options decodeOptions = new Options(); + private static final Options buildOptions = new Options(); + private static final Options frameOptions = new Options(); + private static final Options allOptions = new Options(); + private static final Options emptyOptions = new Options(); + private static final Options emptyFrameworkOptions = new Options(); + private static final Options listFrameworkOptions = new Options(); + + private static boolean advanceMode = false; + public static void main(String[] args) throws BrutException { // headless @@ -239,7 +252,7 @@ public class Main { System.exit(1); } catch (CantFindFrameworkResException ex) { System.err - .println("Can't find framework resources for package of id: " + .println("Could not find framework resources for package of id: " + ex.getPkgId() + ". You must install proper " + "framework files, see project website for more info."); @@ -271,15 +284,8 @@ public class Main { } try { - String aaptPath = cli.getOptionValue("a"); - int aaptVersion = AaptManager.getAaptVersion(aaptPath); - if (aaptVersion < AaptManager.AAPT_VERSION_MIN && aaptVersion > AaptManager.AAPT_VERSION_MAX) { - System.err.println("AAPT version " + aaptVersion + " is not supported"); - System.exit(1); - } - - config.aaptPath = aaptPath; - config.aaptVersion = aaptVersion; + config.aaptBinary = new File(cli.getOptionValue("a")); + config.aaptVersion = AaptManager.getAaptVersion(config.aaptBinary); } catch (BrutException ex) { System.err.println(ex.getMessage()); System.exit(1); @@ -310,7 +316,7 @@ public class Main { } if (config.netSecConf && config.aaptVersion == 1) { - System.err.println("-n / --net-sec-conf is not supported with legacy AAPT."); + System.err.println("-n / --net-sec-conf is not supported with legacy aapt."); System.exit(1); } @@ -724,31 +730,4 @@ public class Main { private static void setAdvanceMode() { Main.advanceMode = true; } - - private enum Verbosity { - NORMAL, VERBOSE, QUIET - } - - private static boolean advanceMode = false; - - private final static Options normalOptions; - private final static Options decodeOptions; - private final static Options buildOptions; - private final static Options frameOptions; - private final static Options allOptions; - private final static Options emptyOptions; - private final static Options emptyFrameworkOptions; - private final static Options listFrameworkOptions; - - static { - //normal and advance usage output - normalOptions = new Options(); - buildOptions = new Options(); - decodeOptions = new Options(); - frameOptions = new Options(); - allOptions = new Options(); - emptyOptions = new Options(); - emptyFrameworkOptions = new Options(); - listFrameworkOptions = new Options(); - } } diff --git a/brut.apktool/apktool-lib/build.gradle.kts b/brut.apktool/apktool-lib/build.gradle.kts index ed25d5e3..fd0602f1 100644 --- a/brut.apktool/apktool-lib/build.gradle.kts +++ b/brut.apktool/apktool-lib/build.gradle.kts @@ -3,9 +3,8 @@ val apktoolVersion: String by rootProject.extra tasks { processResources { - from("src/main/resources/properties") { - include("**/*.properties") - into("properties") + from("src/main/resources") { + include("apktool.properties") expand("version" to apktoolVersion, "gitrev" to gitRevision) duplicatesStrategy = DuplicatesStrategy.INCLUDE } diff --git a/brut.apktool/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java b/brut.apktool/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java index 3e8d70f8..43685068 100644 --- a/brut.apktool/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java @@ -27,7 +27,7 @@ import org.xmlpull.v1.XmlPullParser; public interface XmlResourceParser extends XmlPullParser, AttributeSet { /** * Close this interface to the resource. Calls on the interface are no - * longer value after this call. + * longer valid after this call. */ void close(); } diff --git a/brut.apktool/apktool-lib/src/main/java/android/util/TypedValue.java b/brut.apktool/apktool-lib/src/main/java/android/util/TypedValue.java index 914f145c..03549609 100644 --- a/brut.apktool/apktool-lib/src/main/java/android/util/TypedValue.java +++ b/brut.apktool/apktool-lib/src/main/java/android/util/TypedValue.java @@ -216,7 +216,7 @@ public class TypedValue { public int type; private static final float MANTISSA_MULT = 1.0f / (1 << TypedValue.COMPLEX_MANTISSA_SHIFT); - private static final float[] RADIX_MULTS = new float[] { + private static final float[] RADIX_MULTS = { MANTISSA_MULT, 1.0f / (1 << 7) * MANTISSA_MULT, 1.0f / (1 << 15) * MANTISSA_MULT, 1.0f / (1 << 23) * MANTISSA_MULT }; @@ -237,9 +237,10 @@ public class TypedValue { & TypedValue.COMPLEX_RADIX_MASK]; } - private static final String[] DIMENSION_UNIT_STRS = new String[] { "px", - "dip", "sp", "pt", "in", "mm" }; - private static final String[] FRACTION_UNIT_STRS = new String[] { "%", "%p" }; + private static final String[] DIMENSION_UNIT_STRS = { + "px", "dip", "sp", "pt", "in", "mm" + }; + private static final String[] FRACTION_UNIT_STRS = { "%", "%p" }; /** * Perform type conversion as per coerceToString on an explicitly diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptInvoker.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptInvoker.java index d2b845db..b8964b73 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptInvoker.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptInvoker.java @@ -27,44 +27,40 @@ import java.util.*; import java.util.logging.Logger; public class AaptInvoker { + private static final Logger LOGGER = Logger.getLogger(AaptInvoker.class.getName()); + private final Config mConfig; private final ApkInfo mApkInfo; - private final static Logger LOGGER = Logger.getLogger(AaptInvoker.class.getName()); - public AaptInvoker(Config config, ApkInfo apkInfo) { mConfig = config; mApkInfo = apkInfo; } - private File getAaptBinaryFile() throws AndrolibException { - try { - switch (mConfig.aaptVersion) { - case 2: - return AaptManager.getAapt2(); - default: - return AaptManager.getAapt1(); - } - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } - public void invoke(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include) throws AndrolibException { + File aaptBinary = mConfig.aaptBinary; - String aaptPath = mConfig.aaptPath; - boolean customAapt = !aaptPath.isEmpty(); List cmd = new ArrayList<>(); + String aaptPath; + boolean customAapt; - try { - String aaptCommand = AaptManager.getAaptExecutionCommand(aaptPath, getAaptBinaryFile()); - cmd.add(aaptCommand); - } catch (BrutException ex) { - LOGGER.warning("aapt: " + ex.getMessage() + " (defaulting to $PATH binary)"); - cmd.add(AaptManager.getAaptBinaryName(mConfig.aaptVersion)); + if (mConfig.aaptBinary != null) { + aaptPath = mConfig.aaptBinary.getPath(); + customAapt = true; + } else { + try { + aaptPath = AaptManager.getAaptBinary(mConfig.aaptVersion).getPath(); + customAapt = false; + } catch (BrutException ex) { + aaptPath = AaptManager.getAaptName(mConfig.aaptVersion); + customAapt = true; + LOGGER.warning(aaptPath + ": " + ex.getMessage() + " (defaulting to $PATH binary)"); + } } + cmd.add(aaptPath); + switch (mConfig.aaptVersion) { case 2: invokeAapt2(apkFile, manifest, resDir, rawDir, assetDir, include, cmd, customAapt); @@ -77,7 +73,6 @@ public class AaptInvoker { private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include, List cmd, boolean customAapt) throws AndrolibException { - List compileCommand = new ArrayList<>(cmd); File resourcesZip = null; @@ -87,7 +82,6 @@ public class AaptInvoker { } if (resDir != null && !resourcesZip.exists()) { - // Compile the files into flat arsc files cmd.add("compile"); @@ -241,7 +235,6 @@ public class AaptInvoker { private void invokeAapt1(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include, List cmd, boolean customAapt) throws AndrolibException { - cmd.add("p"); if (mConfig.verbose) { // output aapt verbose diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkBuilder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkBuilder.java index 8ebd6b04..914797f7 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkBuilder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkBuilder.java @@ -21,7 +21,7 @@ import brut.androlib.apk.ApkInfo; import brut.androlib.apk.UsesFramework; import brut.androlib.res.Framework; import brut.androlib.res.data.ResConfigFlags; -import brut.androlib.res.xml.ResXmlPatcher; +import brut.androlib.res.xml.ResXmlUtils; import brut.androlib.src.SmaliBuilder; import brut.common.BrutException; import brut.common.InvalidUnknownFileException; @@ -47,14 +47,15 @@ import java.util.logging.Logger; import java.util.zip.ZipOutputStream; public class ApkBuilder { - private final static Logger LOGGER = Logger.getLogger(ApkBuilder.class.getName()); + private static final Logger LOGGER = Logger.getLogger(ApkBuilder.class.getName()); private final ExtFile mApkDir; private final Config mConfig; + private final AtomicReference mBuildError; + private ApkInfo mApkInfo; - private int mMinSdkVersion = 0; + private int mMinSdkVersion; private BackgroundWorker mWorker; - private final AtomicReference mBuildError = new AtomicReference<>(null); public ApkBuilder(ExtFile apkDir) { this(apkDir, Config.getDefaultConfig()); @@ -63,6 +64,7 @@ public class ApkBuilder { public ApkBuilder(ExtFile apkDir, Config config) { mApkDir = apkDir; mConfig = config; + mBuildError = new AtomicReference<>(null); } public void build(File outApk) throws AndrolibException { @@ -122,19 +124,19 @@ public class ApkBuilder { LOGGER.info("Building apk file..."); - try (ZipOutputStream outStream = new ZipOutputStream(Files.newOutputStream(outApk.toPath()))) { + try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(outApk.toPath()))) { // zip aapt output files try { - ZipUtils.zipDir(outDir, outStream, mApkInfo.doNotCompress); + ZipUtils.zipDir(outDir, out, mApkInfo.doNotCompress); } catch (IOException ex) { throw new AndrolibException(ex); } // zip remaining standard files - importRawFiles(outStream); + importRawFiles(out); // zip unknown files - importUnknownFiles(outStream); + importUnknownFiles(out); } catch (IOException ex) { throw new AndrolibException(ex); } @@ -242,10 +244,10 @@ public class ApkBuilder { //noinspection ResultOfMethodCallIgnored dex.delete(); - int apiLevel = mConfig.apiLevel > 0 ? mConfig.apiLevel : mMinSdkVersion; - LOGGER.info("Smaling " + dirName + " folder into " + fileName + "..."); - SmaliBuilder.build(smaliDir, dex, apiLevel); + int apiLevel = mConfig.apiLevel > 0 ? mConfig.apiLevel : mMinSdkVersion; + SmaliBuilder builder = new SmaliBuilder(smaliDir, apiLevel); + builder.build(dex); } private void backupManifestFile(File manifest, File manifestOrig) throws AndrolibException { @@ -265,7 +267,7 @@ public class ApkBuilder { try { FileUtils.copyFile(manifest, manifestOrig); - ResXmlPatcher.fixingPublicAttrsInProviderAttributes(manifest); + ResXmlUtils.fixingPublicAttrsInProviderAttributes(manifest); } catch (IOException ex) { throw new AndrolibException(ex); } @@ -327,10 +329,10 @@ public class ApkBuilder { try { if (mConfig.debugMode) { if (mConfig.aaptVersion == 2) { - LOGGER.info("Using aapt2 - setting 'debuggable' attribute to 'true' in AndroidManifest.xml"); - ResXmlPatcher.setApplicationDebugTagTrue(manifest); + LOGGER.info("Setting 'debuggable' attribute to 'true' in AndroidManifest.xml"); + ResXmlUtils.setApplicationDebugTagTrue(manifest); } else { - ResXmlPatcher.removeApplicationDebugTag(manifest); + ResXmlUtils.removeApplicationDebugTag(manifest); } } @@ -343,8 +345,8 @@ public class ApkBuilder { } File netSecConfOrig = new File(mApkDir, "res/xml/network_security_config.xml"); - ResXmlPatcher.modNetworkSecurityConfig(netSecConfOrig); - ResXmlPatcher.setNetworkSecurityConfig(manifest); + ResXmlUtils.modNetworkSecurityConfig(netSecConfOrig); + ResXmlUtils.setNetworkSecurityConfig(manifest); LOGGER.info("Added permissive network security config in manifest"); } } catch (IOException | ParserConfigurationException | TransformerException | SAXException ex) { @@ -366,7 +368,7 @@ public class ApkBuilder { ninePatch = null; } - LOGGER.info("Building resources with " + AaptManager.getAaptBinaryName(mConfig.aaptVersion) + "..."); + LOGGER.info("Building resources with " + AaptManager.getAaptName(mConfig.aaptVersion) + "..."); try { AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo); @@ -406,7 +408,7 @@ public class ApkBuilder { ninePatch = null; } - LOGGER.info("Building AndroidManifest.xml with " + AaptManager.getAaptBinaryName(mConfig.aaptVersion) + "..."); + LOGGER.info("Building AndroidManifest.xml with " + AaptManager.getAaptName(mConfig.aaptVersion) + "..."); try { AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo); @@ -460,7 +462,7 @@ public class ApkBuilder { } } - private void importRawFiles(ZipOutputStream outStream) throws AndrolibException { + private void importRawFiles(ZipOutputStream out) throws AndrolibException { for (String dirName : ApkInfo.RAW_DIRNAMES) { File rawDir = new File(mApkDir, dirName); if (!rawDir.isDirectory()) { @@ -469,14 +471,14 @@ public class ApkBuilder { LOGGER.info("Importing " + dirName + "..."); try { - ZipUtils.zipDir(mApkDir, dirName, outStream, mApkInfo.doNotCompress); + ZipUtils.zipDir(mApkDir, dirName, out, mApkInfo.doNotCompress); } catch (IOException ex) { throw new AndrolibException(ex); } } } - private void importUnknownFiles(ZipOutputStream outStream) throws AndrolibException { + private void importUnknownFiles(ZipOutputStream out) throws AndrolibException { File unknownDir = new File(mApkDir, "unknown"); if (!unknownDir.isDirectory()) { return; @@ -484,7 +486,7 @@ public class ApkBuilder { LOGGER.info("Importing unknown files..."); try { - ZipUtils.zipDir(unknownDir, outStream, mApkInfo.doNotCompress); + ZipUtils.zipDir(unknownDir, out, mApkInfo.doNotCompress); } catch (IOException ex) { throw new AndrolibException(ex); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java index b0e84378..a999052d 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java @@ -37,20 +37,21 @@ import java.util.logging.Logger; import java.util.regex.Pattern; public class ApkDecoder { - private final static Logger LOGGER = Logger.getLogger(ApkDecoder.class.getName()); + private static final Logger LOGGER = Logger.getLogger(ApkDecoder.class.getName()); // extensions of files that are often packed uncompressed - private final static Pattern NO_COMPRESS_EXT_PATTERN = Pattern.compile( + private static final Pattern NO_COMPRESS_EXT_PATTERN = Pattern.compile( "dex|arsc|so|jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|" + "rtttl|imy|xmf|mp4|m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|webp|mkv"); private final ExtFile mApkFile; private final Config mConfig; + private final AtomicReference mBuildError; + private ApkInfo mApkInfo; private ResourcesDecoder mResDecoder; - private volatile int mMinSdkVersion = 0; + private volatile int mMinSdkVersion; private BackgroundWorker mWorker; - private final AtomicReference mBuildError = new AtomicReference<>(null); public ApkDecoder(ExtFile apkFile) { this(apkFile, Config.getDefaultConfig()); @@ -59,6 +60,7 @@ public class ApkDecoder { public ApkDecoder(ExtFile apkFile, Config config) { mApkFile = apkFile; mConfig = config; + mBuildError = new AtomicReference<>(null); } public ApkInfo decode(File outDir) throws AndrolibException { @@ -199,8 +201,9 @@ public class ApkDecoder { smaliDir.mkdirs(); LOGGER.info("Baksmaling " + fileName + "..."); - DexFile dexFile = SmaliDecoder.decode(mApkFile, smaliDir, fileName, + SmaliDecoder decoder = new SmaliDecoder(mApkFile, fileName, mConfig.baksmaliDebugMode, mConfig.apiLevel); + DexFile dexFile = decoder.decode(smaliDir); // record minSdkVersion for jars int minSdkVersion = dexFile.getOpcodes().api; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java index b9caea6f..f27d6579 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java @@ -42,20 +42,20 @@ public class ApktoolProperties { } private static void loadProps() { - InputStream in = ApktoolProperties.class.getResourceAsStream("/properties/apktool.properties"); + InputStream in = ApktoolProperties.class.getResourceAsStream("/apktool.properties"); sProps = new Properties(); try { sProps.load(in); in.close(); } catch (NullPointerException | IOException ex) { - LOGGER.warning("Can't load properties."); + LOGGER.warning("Could not load properties."); } InputStream templateStream = null; try { - templateStream = com.android.tools.smali.baksmali.Main.class.getClassLoader().getResourceAsStream("baksmali.properties"); + templateStream = com.android.tools.smali.baksmali.Main.class.getResourceAsStream("/baksmali.properties"); } catch(NoClassDefFoundError ex) { - LOGGER.warning("Can't load baksmali properties."); + LOGGER.warning("Could not load baksmali properties."); } Properties properties = new Properties(); String version = "(unknown)"; @@ -71,9 +71,9 @@ public class ApktoolProperties { templateStream = null; try { - templateStream = com.android.tools.smali.smali.Main.class.getClassLoader().getResourceAsStream("smali.properties"); + templateStream = com.android.tools.smali.smali.Main.class.getResourceAsStream("/smali.properties"); } catch(NoClassDefFoundError ex) { - LOGGER.warning("Can't load smali properties."); + LOGGER.warning("Could not load smali properties."); } properties = new Properties(); version = "(unknown)"; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/BackgroundWorker.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/BackgroundWorker.java index 1b702a27..783ff74e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/BackgroundWorker.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/BackgroundWorker.java @@ -17,15 +17,18 @@ package brut.androlib; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.*; public class BackgroundWorker { - private final ArrayList> mWorkerFutures = new ArrayList<>(); private final ExecutorService mExecutor; - private volatile boolean mSubmitAllowed = true; + private final List> mWorkerFutures; + private volatile boolean mSubmitAllowed; public BackgroundWorker(int threads) { mExecutor = Executors.newFixedThreadPool(threads); + mWorkerFutures = new ArrayList<>(); + mSubmitAllowed = true; } public void waitForFinish() { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java index 1a3bd126..7ba3ed24 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java @@ -22,26 +22,27 @@ import brut.util.OSDetection; import java.io.File; import java.util.logging.Logger; -public class Config { - private static Config instance = null; - private final static Logger LOGGER = Logger.getLogger(Config.class.getName()); +public final class Config { + private static final Logger LOGGER = Logger.getLogger(Config.class.getName()); - public final static short DECODE_SOURCES_NONE = 0x0000; - public final static short DECODE_SOURCES_SMALI = 0x0001; - public final static short DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES = 0x0010; + public static final short DECODE_SOURCES_NONE = 0x0000; + public static final short DECODE_SOURCES_SMALI = 0x0001; + public static final short DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES = 0x0010; - public final static short DECODE_RESOURCES_NONE = 0x0100; - public final static short DECODE_RESOURCES_FULL = 0x0101; + public static final short DECODE_RESOURCES_NONE = 0x0100; + public static final short DECODE_RESOURCES_FULL = 0x0101; - public final static short FORCE_DECODE_MANIFEST_NONE = 0x0000; - public final static short FORCE_DECODE_MANIFEST_FULL = 0x0001; + public static final short FORCE_DECODE_MANIFEST_NONE = 0x0000; + public static final short FORCE_DECODE_MANIFEST_FULL = 0x0001; - public final static short DECODE_ASSETS_NONE = 0x0000; - public final static short DECODE_ASSETS_FULL = 0x0001; + public static final short DECODE_ASSETS_NONE = 0x0000; + public static final short DECODE_ASSETS_FULL = 0x0001; - public final static short DECODE_RES_RESOLVE_REMOVE = 0x0000; - public final static short DECODE_RES_RESOLVE_DUMMY = 0x0001; - public final static short DECODE_RES_RESOLVE_RETAIN = 0x0002; + public static final short DECODE_RES_RESOLVE_REMOVE = 0x0000; + public static final short DECODE_RES_RESOLVE_DUMMY = 0x0001; + public static final short DECODE_RES_RESOLVE_RETAIN = 0x0002; + + private static Config sInstance; // Build options public boolean forceBuildAll = false; @@ -70,7 +71,7 @@ public class Config { public int jobs = Math.min(Runtime.getRuntime().availableProcessors(), 8); public String frameworkDirectory = null; public String frameworkTag = null; - public String aaptPath = ""; + public File aaptBinary = null; public int aaptVersion = 2; // default to v2 // Utility functions @@ -83,14 +84,14 @@ public class Config { } private Config() { - instance = this; + sInstance = this; } public static Config getInstance() { - if (instance == null) { - instance = new Config(); + if (sInstance == null) { + sInstance = new Config(); } - return instance; + return sInstance; } private void setDefaultFrameworkDirectory() { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java index bd3ac80b..01fb9589 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java @@ -28,13 +28,13 @@ import java.util.*; import java.util.regex.Pattern; public class ApkInfo implements YamlSerializable { - public final static String[] RESOURCES_DIRNAMES = new String[] { "res", "r", "R" }; - public final static String[] RAW_DIRNAMES = new String[] { "assets", "lib", "libs", "kotlin", "META-INF/services" }; + public static final String[] RESOURCES_DIRNAMES = { "res", "r", "R" }; + public static final String[] RAW_DIRNAMES = { "assets", "lib", "libs", "kotlin", "META-INF/services" }; - public final static Pattern ORIGINAL_FILENAMES_PATTERN = Pattern.compile( + public static final Pattern ORIGINAL_FILENAMES_PATTERN = Pattern.compile( "AndroidManifest\\.xml|META-INF/[^/]+\\.(RSA|SF|MF)|stamp-cert-sha256"); - public final static Pattern STANDARD_FILENAMES_PATTERN = Pattern.compile( + public static final Pattern STANDARD_FILENAMES_PATTERN = Pattern.compile( "[^/]+\\.dex|resources\\.arsc|(" + String.join("|", RESOURCES_DIRNAMES) + "|" + String.join("|", RAW_DIRNAMES) + ")/.*|" + ORIGINAL_FILENAMES_PATTERN.pattern()); @@ -194,10 +194,7 @@ public class ApkInfo implements YamlSerializable { } public void save(File file) throws AndrolibException { - try ( - OutputStream out = Files.newOutputStream(file.toPath()); - YamlWriter writer = new YamlWriter(out) - ) { + try (YamlWriter writer = new YamlWriter(Files.newOutputStream(file.toPath()))) { write(writer); } catch (FileNotFoundException ex) { throw new AndrolibException("File not found"); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlLine.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlLine.java index 50091f6a..d95828a7 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlLine.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlLine.java @@ -19,7 +19,6 @@ package brut.androlib.apk; import java.util.Objects; public class YamlLine { - public int indent = 0; private String key = ""; private String value = ""; @@ -62,7 +61,7 @@ public class YamlLine { if (isItem) { // array item line has only the value value = line.substring(1).trim(); - } else { + } else { // split line to key - value String[] parts = line.split(":"); if (parts.length > 0) { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlReader.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlReader.java index 85569af2..8fd109e8 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlReader.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlReader.java @@ -22,9 +22,8 @@ import java.io.InputStream; import java.util.*; public class YamlReader { - private ArrayList mLines; - private int mCurrent = 0; + private int mCurrent; public YamlReader(InputStream in) { mLines = new ArrayList<>(); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlSerializable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlSerializable.java index f477bba5..2dc272a3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlSerializable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlSerializable.java @@ -20,5 +20,6 @@ import brut.androlib.exceptions.AndrolibException; public interface YamlSerializable { void readItem(YamlReader reader) throws AndrolibException; + void write(YamlWriter writer); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlStringEscapeUtils.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlStringEscapeUtils.java index 867692bc..fb947e82 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlStringEscapeUtils.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlStringEscapeUtils.java @@ -23,7 +23,11 @@ import java.io.IOException; import java.io.StringWriter; import java.io.Writer; -public class YamlStringEscapeUtils { +public final class YamlStringEscapeUtils { + + private YamlStringEscapeUtils() { + // Private constructor for utility class + } public static String escapeString(String str) { return escapeJavaStyleString(str); @@ -48,12 +52,12 @@ public class YamlStringEscapeUtils { } /** - * @param out write to receive the escaped string + * @param writer Writer to receive the escaped string * @param str String to escape values in, may be null * @throws IOException if an IOException occurs */ - private static void escapeJavaStyleString(Writer out, String str) throws IOException { - if (out == null) { + private static void escapeJavaStyleString(Writer writer, String str) throws IOException { + if (writer == null) { throw new IllegalArgumentException("The Writer must not be null"); } if (str == null) { @@ -66,51 +70,51 @@ public class YamlStringEscapeUtils { // "[^\t\n\r\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFD]" // handle unicode if (ch > 0xFFFD) { - out.write("\\u" + CharSequenceTranslator.hex(ch)); + writer.write("\\u" + CharSequenceTranslator.hex(ch)); } else if (ch > 0xD7FF && ch < 0xE000) { - out.write("\\u" + CharSequenceTranslator.hex(ch)); + writer.write("\\u" + CharSequenceTranslator.hex(ch)); } else if (ch > 0x7E && ch != 0x85 && ch < 0xA0) { - out.write("\\u00" + CharSequenceTranslator.hex(ch)); + writer.write("\\u00" + CharSequenceTranslator.hex(ch)); } else if (ch < 32) { switch (ch) { case '\t' : - out.write('\\'); - out.write('t'); + writer.write('\\'); + writer.write('t'); break; case '\n' : - out.write('\\'); - out.write('n'); + writer.write('\\'); + writer.write('n'); break; case '\r' : - out.write('\\'); - out.write('r'); + writer.write('\\'); + writer.write('r'); break; default : if (ch > 0xf) { - out.write("\\u00" + CharSequenceTranslator.hex(ch)); + writer.write("\\u00" + CharSequenceTranslator.hex(ch)); } else { - out.write("\\u000" + CharSequenceTranslator.hex(ch)); + writer.write("\\u000" + CharSequenceTranslator.hex(ch)); } break; } } else { switch (ch) { case '\'' : - out.write('\''); + writer.write('\''); break; case '"' : - out.write('\\'); - out.write('"'); + writer.write('\\'); + writer.write('"'); break; case '\\' : - out.write('\\'); - out.write('\\'); + writer.write('\\'); + writer.write('\\'); break; case '/' : - out.write('/'); + writer.write('/'); break; default : - out.write(ch); + writer.write(ch); break; } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlWriter.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlWriter.java index 1c37051d..133d6d38 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlWriter.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlWriter.java @@ -21,10 +21,10 @@ import java.nio.charset.StandardCharsets; import java.util.*; public class YamlWriter implements Closeable { + private static final String QUOTE = "'"; - private int mIndent = 0; private final PrintWriter mWriter; - private final String QUOTE = "'"; + private int mIndent; public YamlWriter(OutputStream out) { mWriter = new PrintWriter(new BufferedWriter( diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/AXmlDecodingException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/AXmlDecodingException.java index 1e740b28..c094e8f6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/AXmlDecodingException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/AXmlDecodingException.java @@ -17,6 +17,7 @@ package brut.androlib.exceptions; public class AXmlDecodingException extends AndrolibException { + public AXmlDecodingException(String message, Throwable cause) { super(message, cause); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/AndrolibException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/AndrolibException.java index e72da574..6c73b20f 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/AndrolibException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/AndrolibException.java @@ -19,18 +19,20 @@ package brut.androlib.exceptions; import brut.common.BrutException; public class AndrolibException extends BrutException { + public AndrolibException() { + super(); } public AndrolibException(String message) { super(message); } - public AndrolibException(String message, Throwable cause) { - super(message, cause); - } - public AndrolibException(Throwable cause) { super(cause); } + + public AndrolibException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/CantFind9PatchChunkException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/CantFind9PatchChunkException.java index acee4d20..d67f63d9 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/CantFind9PatchChunkException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/CantFind9PatchChunkException.java @@ -17,6 +17,7 @@ package brut.androlib.exceptions; public class CantFind9PatchChunkException extends AndrolibException { + public CantFind9PatchChunkException(String message, Throwable cause) { super(message, cause); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/CantFindFrameworkResException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/CantFindFrameworkResException.java index 56434610..cafa4341 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/CantFindFrameworkResException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/CantFindFrameworkResException.java @@ -17,8 +17,10 @@ package brut.androlib.exceptions; public class CantFindFrameworkResException extends AndrolibException { - public CantFindFrameworkResException(int id) { - mPkgId = id; + private final int mPkgId; + + public CantFindFrameworkResException(int pkgId) { + mPkgId = pkgId; } public int getPkgId() { @@ -27,8 +29,6 @@ public class CantFindFrameworkResException extends AndrolibException { @Override public String getMessage() { - return String.format("Can't find framework resources for package of id: %d", this.getPkgId()); + return String.format("Could not find framework resources for package of id: %d", mPkgId); } - - private final int mPkgId; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/InFileNotFoundException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/InFileNotFoundException.java index 8f06a966..07d3e63d 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/InFileNotFoundException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/InFileNotFoundException.java @@ -17,6 +17,8 @@ package brut.androlib.exceptions; public class InFileNotFoundException extends AndrolibException { + public InFileNotFoundException() { + super(); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/OutDirExistsException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/OutDirExistsException.java index 994cd48e..b6c3ec8d 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/OutDirExistsException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/OutDirExistsException.java @@ -17,6 +17,8 @@ package brut.androlib.exceptions; public class OutDirExistsException extends AndrolibException { + public OutDirExistsException() { + super(); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/RawXmlEncounteredException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/RawXmlEncounteredException.java index c8a4aabe..b44b1166 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/RawXmlEncounteredException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/RawXmlEncounteredException.java @@ -17,6 +17,7 @@ package brut.androlib.exceptions; public class RawXmlEncounteredException extends AndrolibException { + public RawXmlEncounteredException(String message, Throwable cause) { super(message, cause); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/UndefinedResObjectException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/UndefinedResObjectException.java index ff411bde..8df878d2 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/UndefinedResObjectException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions/UndefinedResObjectException.java @@ -17,6 +17,7 @@ package brut.androlib.exceptions; public class UndefinedResObjectException extends AndrolibException { + public UndefinedResObjectException(String message) { super(message); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java index 2e1e65f9..fadaee45 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java @@ -30,13 +30,16 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -public class SmaliMod { +public final class SmaliMod { + + private SmaliMod() { + // Private constructor for utility class + } + public static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, int apiLevel, boolean verboseErrors, boolean printTokens) throws IOException, RecognitionException { - try ( - InputStream in = Files.newInputStream(smaliFile.toPath()); - InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8) - ) { + try (InputStreamReader reader = new InputStreamReader( + Files.newInputStream(smaliFile.toPath()), StandardCharsets.UTF_8)) { smaliFlexLexer lexer = new smaliFlexLexer(reader, apiLevel); lexer.setSourceFile(smaliFile); CommonTokenStream tokens = new CommonTokenStream(lexer); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/Framework.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/Framework.java index 4377a299..bc15fd95 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/Framework.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/Framework.java @@ -23,7 +23,6 @@ import brut.androlib.res.decoder.ARSCDecoder; import brut.androlib.res.data.arsc.ARSCData; import brut.androlib.res.data.arsc.FlagsOffset; import brut.util.BrutIO; -import brut.util.Jar; import java.io.*; import java.nio.file.Files; @@ -35,18 +34,17 @@ import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; public class Framework { - private final Config config; + private static final Logger LOGGER = Logger.getLogger(Framework.class.getName()); - private File mFrameworkDirectory = null; - - private final static Logger LOGGER = Logger.getLogger(Framework.class.getName()); + private final Config mConfig; + private File mFrameworkDirectory; public Framework(Config config) { - this.config = config; + mConfig = config; } public void installFramework(File frameFile) throws AndrolibException { - installFramework(frameFile, config.frameworkTag); + installFramework(frameFile, mConfig.frameworkTag); } public void installFramework(File frameFile, String tag) throws AndrolibException { @@ -54,17 +52,16 @@ public class Framework { ZipEntry entry = zip.getEntry("resources.arsc"); if (entry == null) { - throw new AndrolibException("Can't find resources.arsc file"); + throw new AndrolibException("Could not find resources.arsc file"); } byte[] data = BrutIO.readAndClose(zip.getInputStream(entry)); - ARSCData arsc = ARSCDecoder.decode(new ByteArrayInputStream(data), true, true); + ARSCDecoder decoder = new ARSCDecoder(new ByteArrayInputStream(data), null, true, true); + ARSCData arsc = decoder.decode(); publicizeResources(data, arsc.getFlagsOffsets()); - File outFile = new File(getFrameworkDirectory(), arsc - .getOnePackage().getId() - + (tag == null ? "" : '-' + tag) - + ".apk"); + File outFile = new File(getFrameworkDirectory(), + arsc.getOnePackage().getId() + (tag == null ? "" : '-' + tag) + ".apk"); try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(outFile.toPath()))) { out.setMethod(ZipOutputStream.STORED); @@ -116,8 +113,10 @@ public class Framework { public void publicizeResources(File arscFile) throws AndrolibException { byte[] data = new byte[(int) arscFile.length()]; - try(InputStream in = Files.newInputStream(arscFile.toPath()); - OutputStream out = Files.newOutputStream(arscFile.toPath())) { + try ( + InputStream in = Files.newInputStream(arscFile.toPath()); + OutputStream out = Files.newOutputStream(arscFile.toPath()) + ) { //noinspection ResultOfMethodCallIgnored in.read(data); publicizeResources(data); @@ -127,16 +126,18 @@ public class Framework { } } - private void publicizeResources(byte[] arsc) throws AndrolibException { - publicizeResources(arsc, ARSCDecoder.decode(new ByteArrayInputStream(arsc), true, true).getFlagsOffsets()); + private void publicizeResources(byte[] data) throws AndrolibException { + ARSCDecoder decoder = new ARSCDecoder(new ByteArrayInputStream(data), null, true, true); + ARSCData arsc = decoder.decode(); + publicizeResources(data, arsc.getFlagsOffsets()); } - public void publicizeResources(byte[] arsc, FlagsOffset[] flagsOffsets) { + public void publicizeResources(byte[] data, FlagsOffset[] flagsOffsets) { for (FlagsOffset flags : flagsOffsets) { int offset = flags.offset + 3; int end = offset + 4 * flags.count; while (offset < end) { - arsc[offset] |= (byte) 0x40; + data[offset] |= (byte) 0x40; offset += 4; } } @@ -150,7 +151,7 @@ public class Framework { String path; // use default framework path or specified on the command line - path = config.frameworkDirectory; + path = mConfig.frameworkDirectory; File dir = new File(path); @@ -162,19 +163,19 @@ public class Framework { throw new AndrolibException("Please remove file at " + dir.getParentFile()); } - if (! dir.exists()) { - if (! dir.mkdirs()) { - if (config.frameworkDirectory != null) { - LOGGER.severe("Can't create Framework directory: " + dir); + if (!dir.exists()) { + if (!dir.mkdirs()) { + if (mConfig.frameworkDirectory != null) { + LOGGER.severe("Could not create Framework directory: " + dir); } throw new AndrolibException(String.format( - "Can't create directory: (%s). Pass a writable path with --frame-path {DIR}. ", dir + "Could not create directory: (%s). Pass a writable path with --frame-path {DIR}. ", dir )); } } - if (config.frameworkDirectory == null) { - if (! dir.canWrite()) { + if (mConfig.frameworkDirectory == null) { + if (!dir.canWrite()) { LOGGER.severe(String.format("WARNING: Could not write to (%1$s), using %2$s instead...", dir.getAbsolutePath(), System.getProperty("java.io.tmpdir"))); LOGGER.severe("Please be aware this is a volatile directory and frameworks could go missing, " + @@ -222,11 +223,11 @@ public class Framework { apk = new File(dir, "1.apk"); - if (! apk.exists()) { - LOGGER.warning("Can't empty framework directory, no file found at: " + apk.getAbsolutePath()); + if (!apk.exists()) { + LOGGER.warning("Could not empty framework directory, no file found at: " + apk.getAbsolutePath()); } else { try { - if (apk.exists() && Objects.requireNonNull(dir.listFiles()).length > 1 && ! config.forceDeleteFramework) { + if (apk.exists() && Objects.requireNonNull(dir.listFiles()).length > 1 && !mConfig.forceDeleteFramework) { LOGGER.warning("More than default framework detected. Please run command with `--force` parameter to wipe framework directory."); } else { for (File file : Objects.requireNonNull(dir.listFiles())) { @@ -244,6 +245,6 @@ public class Framework { } private InputStream getAndroidFrameworkResourcesAsStream() { - return Jar.class.getResourceAsStream("/brut/androlib/android-framework.jar"); + return Framework.class.getResourceAsStream("/prebuilt/android-framework.jar"); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java index 72c02e01..f94b6ddc 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java @@ -22,11 +22,12 @@ import brut.androlib.exceptions.AndrolibException; import brut.androlib.res.data.*; import brut.androlib.res.decoder.*; import brut.androlib.res.xml.ResValuesXmlSerializable; -import brut.androlib.res.xml.ResXmlPatcher; +import brut.androlib.res.xml.ResXmlUtils; import brut.directory.Directory; import brut.directory.DirectoryException; import brut.directory.ExtFile; import brut.xmlpull.MXSerializer; +import com.google.common.collect.Sets; import org.xmlpull.v1.XmlSerializer; import java.io.*; @@ -34,21 +35,23 @@ import java.util.*; import java.util.logging.Logger; public class ResourcesDecoder { - private final static Logger LOGGER = Logger.getLogger(ResourcesDecoder.class.getName()); + private static final Logger LOGGER = Logger.getLogger(ResourcesDecoder.class.getName()); + + private static final Set IGNORED_PACKAGES = Sets.newHashSet( + "android", "com.htc", "com.lge", "com.lge.internal", "yi", "flyme", "air.com.adobe.appentry", + "FFFFFFFFFFFFFFFFFFFFFF" + ); private final Config mConfig; private final ApkInfo mApkInfo; private final ResTable mResTable; - private final Map mResFileMapping = new HashMap<>(); - - private final static String[] IGNORED_PACKAGES = new String[] { - "android", "com.htc", "com.lge", "com.lge.internal", "yi", "flyme", "air.com.adobe.appentry", - "FFFFFFFFFFFFFFFFFFFFFF" }; + private final Map mResFileMapping; public ResourcesDecoder(Config config, ApkInfo apkInfo) { mConfig = config; mApkInfo = apkInfo; mResTable = new ResTable(mConfig, mApkInfo); + mResFileMapping = new HashMap<>(); } public ResTable getResTable() throws AndrolibException { @@ -59,7 +62,7 @@ public class ResourcesDecoder { return mResTable; } - public Map getResFileMapping() { + public Map getResFileMapping() { return mResFileMapping; } @@ -67,7 +70,7 @@ public class ResourcesDecoder { mResTable.loadMainPkg(mApkInfo.getApkFile()); } - public void decodeManifest(File outDir) throws AndrolibException { + public void decodeManifest(File apkDir) throws AndrolibException { if (!mApkInfo.hasManifest()) { return; } @@ -76,10 +79,10 @@ public class ResourcesDecoder { XmlSerializer xmlSerializer = newXmlSerializer(); ResStreamDecoder fileDecoder = new AndroidManifestPullStreamDecoder(axmlParser, xmlSerializer); - Directory in, out; + Directory inDir, outDir; try { - in = mApkInfo.getApkFile().getDirectory(); - out = new ExtFile(outDir).getDirectory(); + inDir = mApkInfo.getApkFile().getDirectory(); + outDir = new ExtFile(apkDir).getDirectory(); if (mApkInfo.hasResources()) { LOGGER.info("Decoding AndroidManifest.xml with resources..."); @@ -88,16 +91,16 @@ public class ResourcesDecoder { } try ( - InputStream is = in.getFileInput("AndroidManifest.xml"); - OutputStream os = out.getFileOutput("AndroidManifest.xml") + InputStream in = inDir.getFileInput("AndroidManifest.xml"); + OutputStream out = outDir.getFileOutput("AndroidManifest.xml") ) { - fileDecoder.decode(is, os); + fileDecoder.decode(in, out); } } catch (DirectoryException | IOException ex) { throw new AndrolibException(ex); } - File manifest = new File(outDir, "AndroidManifest.xml"); + File manifest = new File(apkDir, "AndroidManifest.xml"); if (mApkInfo.hasResources() && !mConfig.analysisMode) { // Remove versionName / versionCode (aapt API 16) @@ -108,14 +111,14 @@ public class ResourcesDecoder { // it will be passed as a parameter to aapt like "--min-sdk-version" via apktool.yml adjustPackageManifest(manifest); - ResXmlPatcher.removeManifestVersions(manifest); + ResXmlUtils.removeManifestVersions(manifest); // update apk info mApkInfo.packageInfo.forcedPackageId = String.valueOf(mResTable.getPackageId()); } // record feature flags - List featureFlags = ResXmlPatcher.pullManifestFeatureFlags(manifest); + List featureFlags = ResXmlUtils.pullManifestFeatureFlags(manifest); if (featureFlags != null) { for (String flag : featureFlags) { mApkInfo.addFeatureFlag(flag, true); @@ -123,8 +126,8 @@ public class ResourcesDecoder { } } - public void updateApkInfo(File outDir) throws AndrolibException { - mResTable.initApkInfo(mApkInfo, outDir); + public void updateApkInfo(File apkDir) throws AndrolibException { + mResTable.initApkInfo(mApkInfo, apkDir); } private void adjustPackageManifest(File manifest) throws AndrolibException { @@ -141,15 +144,15 @@ public class ResourcesDecoder { // 3) Check if pkgOriginal === mPackageRenamed // 4) Check if pkgOriginal is ignored via IGNORED_PACKAGES if (pkgOriginal == null || pkgRenamed == null || pkgOriginal.equals(pkgRenamed) - || (Arrays.asList(IGNORED_PACKAGES).contains(pkgOriginal))) { + || IGNORED_PACKAGES.contains(pkgOriginal)) { LOGGER.info("Regular manifest package..."); } else { LOGGER.info("Renamed manifest package found! Replacing " + pkgRenamed + " with " + pkgOriginal); - ResXmlPatcher.renameManifestPackage(manifest, pkgOriginal); + ResXmlUtils.renameManifestPackage(manifest, pkgOriginal); } } - public void decodeResources(File outDir) throws AndrolibException { + public void decodeResources(File apkDir) throws AndrolibException { if (!mApkInfo.hasResources()) { return; } @@ -165,11 +168,11 @@ public class ResourcesDecoder { decoders.setDecoder("xml", new ResXmlPullStreamDecoder(axmlParser, xmlSerializer)); ResFileDecoder fileDecoder = new ResFileDecoder(decoders); - Directory in, out, outRes; + Directory inDir, outDir; try { - in = mApkInfo.getApkFile().getDirectory(); - out = new ExtFile(outDir).getDirectory().createDir("res"); + inDir = mApkInfo.getApkFile().getDirectory(); + outDir = new ExtFile(apkDir).getDirectory().createDir("res"); } catch (DirectoryException ex) { throw new AndrolibException(ex); } @@ -177,15 +180,15 @@ public class ResourcesDecoder { for (ResPackage pkg : mResTable.listMainPackages()) { LOGGER.info("Decoding file-resources..."); for (ResResource res : pkg.listFiles()) { - fileDecoder.decode(res, in, out, mResFileMapping); + fileDecoder.decode(res, inDir, outDir, mResFileMapping); } LOGGER.info("Decoding values */* XMLs..."); for (ResValuesFile valuesFile : pkg.listValuesFiles()) { - generateValuesFile(valuesFile, out, xmlSerializer); + generateValuesFile(valuesFile, outDir, xmlSerializer); } - generatePublicXml(pkg, out, xmlSerializer); + generatePublicXml(pkg, outDir, xmlSerializer); } AndrolibException decodeError = axmlParser.getFirstError(); @@ -207,11 +210,10 @@ public class ResourcesDecoder { } } - private void generateValuesFile(ResValuesFile valuesFile, Directory out, - XmlSerializer serial) throws AndrolibException { - try { - OutputStream outStream = out.getFileOutput(valuesFile.getPath()); - serial.setOutput(outStream, null); + private void generateValuesFile(ResValuesFile valuesFile, Directory resDir, XmlSerializer serial) + throws AndrolibException { + try (OutputStream out = resDir.getFileOutput(valuesFile.getPath())) { + serial.setOutput(out, null); serial.startDocument(null, null); serial.startTag(null, "resources"); @@ -225,17 +227,15 @@ public class ResourcesDecoder { serial.endTag(null, "resources"); serial.endDocument(); serial.flush(); - outStream.close(); } catch (DirectoryException | IOException ex) { throw new AndrolibException("Could not generate: " + valuesFile.getPath(), ex); } } - private void generatePublicXml(ResPackage pkg, Directory out, - XmlSerializer serial) throws AndrolibException { - try { - OutputStream outStream = out.getFileOutput("values/public.xml"); - serial.setOutput(outStream, null); + private void generatePublicXml(ResPackage pkg, Directory resDir, XmlSerializer serial) + throws AndrolibException { + try (OutputStream out = resDir.getFileOutput("values/public.xml")) { + serial.setOutput(out, null); serial.startDocument(null, null); serial.startTag(null, "resources"); @@ -250,7 +250,6 @@ public class ResourcesDecoder { serial.endTag(null, "resources"); serial.endDocument(); serial.flush(); - outStream.close(); } catch (DirectoryException | IOException ex) { throw new AndrolibException("Could not generate public.xml file", ex); } 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 ea1e350b..b75aa0e9 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 @@ -19,6 +19,165 @@ package brut.androlib.res.data; import java.util.logging.Logger; public class ResConfigFlags { + private static final Logger LOGGER = Logger.getLogger(ResConfigFlags.class.getName()); + + public static final byte SDK_BASE = 1; + public static final byte SDK_BASE_1_1 = 2; + public static final byte SDK_CUPCAKE = 3; + public static final byte SDK_DONUT = 4; + public static final byte SDK_ECLAIR = 5; + public static final byte SDK_ECLAIR_0_1 = 6; + public static final byte SDK_ECLAIR_MR1 = 7; + public static final byte SDK_FROYO = 8; + public static final byte SDK_GINGERBREAD = 9; + public static final byte SDK_GINGERBREAD_MR1 = 10; + public static final byte SDK_HONEYCOMB = 11; + public static final byte SDK_HONEYCOMB_MR1 = 12; + public static final byte SDK_HONEYCOMB_MR2 = 13; + public static final byte SDK_ICE_CREAM_SANDWICH = 14; + public static final byte SDK_ICE_CREAM_SANDWICH_MR1 = 15; + public static final byte SDK_JELLY_BEAN = 16; + public static final byte SDK_JELLY_BEAN_MR1 = 17; + public static final byte SDK_JELLY_BEAN_MR2 = 18; + public static final byte SDK_KITKAT = 19; + public static final byte SDK_LOLLIPOP = 21; + public static final byte SDK_LOLLIPOP_MR1 = 22; + public static final byte SDK_MNC = 23; + public static final byte SDK_NOUGAT = 24; + public static final byte SDK_NOUGAT_MR1 = 25; + public static final byte SDK_OREO = 26; + public static final byte SDK_OREO_MR1 = 27; + public static final byte SDK_P = 28; + public static final byte SDK_Q = 29; + public static final byte SDK_R = 30; + public static final byte SDK_S = 31; + public static final byte SDK_S_V2 = 32; + public static final byte SDK_TIRAMISU = 33; + public static final byte SDK_UPSIDEDOWN_CAKE = 34; + public static final byte SDK_VANILLA_ICE_CREAM = 35; + + // AOSP changed Build IDs during QPR2 of API 34 (Upsidedown Cake), restarting at A. + // However, API 35 (Vanilla) took letter A (AP2A), so we start at B. + public static final byte SDK_BAKLAVA = 36; + + // AOSP has this as 10,000 for dev purposes. + // platform_frameworks_base/commit/c7a1109a1fe0771d4c9b572dcf178e2779fc4f2d + public static final int SDK_DEVELOPMENT = 10000; + + public static final byte ORIENTATION_ANY = 0; + public static final byte ORIENTATION_PORT = 1; + public static final byte ORIENTATION_LAND = 2; + public static final byte ORIENTATION_SQUARE = 3; + + public static final byte TOUCHSCREEN_ANY = 0; + public static final byte TOUCHSCREEN_NOTOUCH = 1; + public static final byte TOUCHSCREEN_STYLUS = 2; + public static final byte TOUCHSCREEN_FINGER = 3; + + public static final int DENSITY_DEFAULT = 0; + public static final int DENSITY_LOW = 120; + public static final int DENSITY_MEDIUM = 160; + public static final int DENSITY_400 = 190; + public static final int DENSITY_TV = 213; + public static final int DENSITY_HIGH = 240; + public static final int DENSITY_XHIGH = 320; + public static final int DENSITY_XXHIGH = 480; + public static final int DENSITY_XXXHIGH = 640; + public static final int DENSITY_ANY = 0xFFFE; + public static final int DENSITY_NONE = 0xFFFF; + + public static final int MNC_ZERO = -1; + + public static final short MASK_LAYOUTDIR = 0xc0; + public static final short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00; + public static final short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40; + public static final short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80; + public static final short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06; + + public static final short MASK_SCREENROUND = 0x03; + public static final short SCREENLAYOUT_ROUND_ANY = 0; + public static final short SCREENLAYOUT_ROUND_NO = 0x1; + public static final short SCREENLAYOUT_ROUND_YES = 0x2; + + public static final byte GRAMMATICAL_GENDER_ANY = 0; + public static final byte GRAMMATICAL_GENDER_NEUTER = 1; + public static final byte GRAMMATICAL_GENDER_FEMININE = 2; + public static final byte GRAMMATICAL_GENDER_MASCULINE = 3; + + public static final byte KEYBOARD_ANY = 0; + public static final byte KEYBOARD_NOKEYS = 1; + public static final byte KEYBOARD_QWERTY = 2; + public static final byte KEYBOARD_12KEY = 3; + + public static final byte NAVIGATION_ANY = 0; + public static final byte NAVIGATION_NONAV = 1; + public static final byte NAVIGATION_DPAD = 2; + public static final byte NAVIGATION_TRACKBALL = 3; + public static final byte NAVIGATION_WHEEL = 4; + + public static final byte MASK_KEYSHIDDEN = 0x3; + public static final byte KEYSHIDDEN_ANY = 0x0; + public static final byte KEYSHIDDEN_NO = 0x1; + public static final byte KEYSHIDDEN_YES = 0x2; + public static final byte KEYSHIDDEN_SOFT = 0x3; + + public static final byte MASK_NAVHIDDEN = 0xc; + public static final byte NAVHIDDEN_ANY = 0x0; + public static final byte NAVHIDDEN_NO = 0x4; + public static final byte NAVHIDDEN_YES = 0x8; + + public static final byte MASK_SCREENSIZE = 0x0f; + public static final byte SCREENSIZE_ANY = 0x00; + public static final byte SCREENSIZE_SMALL = 0x01; + public static final byte SCREENSIZE_NORMAL = 0x02; + public static final byte SCREENSIZE_LARGE = 0x03; + public static final byte SCREENSIZE_XLARGE = 0x04; + + public static final byte MASK_SCREENLONG = 0x30; + public static final byte SCREENLONG_ANY = 0x00; + public static final byte SCREENLONG_NO = 0x10; + public static final byte SCREENLONG_YES = 0x20; + + public static final byte MASK_UI_MODE_TYPE = 0x0f; + public static final byte UI_MODE_TYPE_ANY = 0x00; + public static final byte UI_MODE_TYPE_NORMAL = 0x01; + public static final byte UI_MODE_TYPE_DESK = 0x02; + public static final byte UI_MODE_TYPE_CAR = 0x03; + public static final byte UI_MODE_TYPE_TELEVISION = 0x04; + public static final byte UI_MODE_TYPE_APPLIANCE = 0x05; + public static final byte UI_MODE_TYPE_WATCH = 0x06; + public static final byte UI_MODE_TYPE_VR_HEADSET = 0x07; + + // start - miui + public static final byte UI_MODE_TYPE_GODZILLAUI = 0x0b; + public static final byte UI_MODE_TYPE_SMALLUI = 0x0c; + public static final byte UI_MODE_TYPE_MEDIUMUI = 0x0d; + public static final byte UI_MODE_TYPE_LARGEUI = 0x0e; + public static final byte UI_MODE_TYPE_HUGEUI = 0x0f; + // end - miui + + public static final byte MASK_UI_MODE_NIGHT = 0x30; + public static final byte UI_MODE_NIGHT_ANY = 0x00; + public static final byte UI_MODE_NIGHT_NO = 0x10; + public static final byte UI_MODE_NIGHT_YES = 0x20; + + public static final byte COLOR_HDR_MASK = 0xC; + public static final byte COLOR_HDR_NO = 0x4; + public static final byte COLOR_HDR_SHIFT = 0x2; + public static final byte COLOR_HDR_UNDEFINED = 0x0; + public static final byte COLOR_HDR_YES = 0x8; + + public static final byte COLOR_UNDEFINED = 0x0; + + public static final byte COLOR_WIDE_UNDEFINED = 0x0; + public static final byte COLOR_WIDE_NO = 0x1; + public static final byte COLOR_WIDE_YES = 0x2; + public static final byte COLOR_WIDE_MASK = 0x3; + + // TODO: Dirty static hack. This counter should be a part of ResPackage, + // but it would be hard right now and this feature is very rarely used. + private static int sErrCounter = 0; + public final short mcc; public final short mnc; @@ -137,7 +296,7 @@ public class ResConfigFlags { if (localeVariant[0] == '\00') { localeVariant = null; } - } else { + } else { localeVariant = null; } @@ -522,172 +681,13 @@ public class ResConfigFlags { return false; } final ResConfigFlags other = (ResConfigFlags) obj; - return this.mQualifiers.equals(other.mQualifiers); + return mQualifiers.equals(other.mQualifiers); } @Override public int hashCode() { int hash = 17; - hash = 31 * hash + this.mQualifiers.hashCode(); + hash = 31 * hash + mQualifiers.hashCode(); return hash; } - - // TODO: Dirty static hack. This counter should be a part of ResPackage, - // but it would be hard right now and this feature is very rarely used. - private static int sErrCounter = 0; - - public final static byte SDK_BASE = 1; - public final static byte SDK_BASE_1_1 = 2; - public final static byte SDK_CUPCAKE = 3; - public final static byte SDK_DONUT = 4; - public final static byte SDK_ECLAIR = 5; - public final static byte SDK_ECLAIR_0_1 = 6; - public final static byte SDK_ECLAIR_MR1 = 7; - public final static byte SDK_FROYO = 8; - public final static byte SDK_GINGERBREAD = 9; - public final static byte SDK_GINGERBREAD_MR1 = 10; - public final static byte SDK_HONEYCOMB = 11; - public final static byte SDK_HONEYCOMB_MR1 = 12; - public final static byte SDK_HONEYCOMB_MR2 = 13; - public final static byte SDK_ICE_CREAM_SANDWICH = 14; - public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15; - public final static byte SDK_JELLY_BEAN = 16; - public final static byte SDK_JELLY_BEAN_MR1 = 17; - public final static byte SDK_JELLY_BEAN_MR2 = 18; - public final static byte SDK_KITKAT = 19; - 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_OREO = 26; - public final static byte SDK_OREO_MR1 = 27; - public final static byte SDK_P = 28; - public final static byte SDK_Q = 29; - public final static byte SDK_R = 30; - public final static byte SDK_S = 31; - public final static byte SDK_S_V2 = 32; - public final static byte SDK_TIRAMISU = 33; - public final static byte SDK_UPSIDEDOWN_CAKE = 34; - public final static byte SDK_VANILLA_ICE_CREAM = 35; - - // AOSP changed Build IDs during QPR2 of API 34 (Upsidedown Cake), restarting at A. - // However, API 35 (Vanilla) took letter A (AP2A), so we start at B. - public final static byte SDK_BAKLAVA = 36; - - // AOSP has this as 10,000 for dev purposes. - // platform_frameworks_base/commit/c7a1109a1fe0771d4c9b572dcf178e2779fc4f2d - public final static int SDK_DEVELOPMENT = 10000; - - public final static byte ORIENTATION_ANY = 0; - public final static byte ORIENTATION_PORT = 1; - public final static byte ORIENTATION_LAND = 2; - public final static byte ORIENTATION_SQUARE = 3; - - public final static byte TOUCHSCREEN_ANY = 0; - public final static byte TOUCHSCREEN_NOTOUCH = 1; - public final static byte TOUCHSCREEN_STYLUS = 2; - public final static byte TOUCHSCREEN_FINGER = 3; - - public final static int DENSITY_DEFAULT = 0; - public final static int DENSITY_LOW = 120; - public final static int DENSITY_MEDIUM = 160; - public final static int DENSITY_400 = 190; - public final static int DENSITY_TV = 213; - public final static int DENSITY_HIGH = 240; - public final static int DENSITY_XHIGH = 320; - public final static int DENSITY_XXHIGH = 480; - public final static int DENSITY_XXXHIGH = 640; - public final static int DENSITY_ANY = 0xFFFE; - public final static int DENSITY_NONE = 0xFFFF; - - public final static int MNC_ZERO = -1; - - public final static short MASK_LAYOUTDIR = 0xc0; - public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00; - public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40; - public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80; - public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06; - - public final static short MASK_SCREENROUND = 0x03; - public final static short SCREENLAYOUT_ROUND_ANY = 0; - public final static short SCREENLAYOUT_ROUND_NO = 0x1; - public final static short SCREENLAYOUT_ROUND_YES = 0x2; - - public final static byte GRAMMATICAL_GENDER_ANY = 0; - public final static byte GRAMMATICAL_GENDER_NEUTER = 1; - public final static byte GRAMMATICAL_GENDER_FEMININE = 2; - public final static byte GRAMMATICAL_GENDER_MASCULINE = 3; - - public final static byte KEYBOARD_ANY = 0; - public final static byte KEYBOARD_NOKEYS = 1; - public final static byte KEYBOARD_QWERTY = 2; - public final static byte KEYBOARD_12KEY = 3; - - public final static byte NAVIGATION_ANY = 0; - public final static byte NAVIGATION_NONAV = 1; - public final static byte NAVIGATION_DPAD = 2; - public final static byte NAVIGATION_TRACKBALL = 3; - public final static byte NAVIGATION_WHEEL = 4; - - public final static byte MASK_KEYSHIDDEN = 0x3; - public final static byte KEYSHIDDEN_ANY = 0x0; - public final static byte KEYSHIDDEN_NO = 0x1; - public final static byte KEYSHIDDEN_YES = 0x2; - public final static byte KEYSHIDDEN_SOFT = 0x3; - - public final static byte MASK_NAVHIDDEN = 0xc; - public final static byte NAVHIDDEN_ANY = 0x0; - public final static byte NAVHIDDEN_NO = 0x4; - public final static byte NAVHIDDEN_YES = 0x8; - - public final static byte MASK_SCREENSIZE = 0x0f; - public final static byte SCREENSIZE_ANY = 0x00; - public final static byte SCREENSIZE_SMALL = 0x01; - public final static byte SCREENSIZE_NORMAL = 0x02; - public final static byte SCREENSIZE_LARGE = 0x03; - public final static byte SCREENSIZE_XLARGE = 0x04; - - public final static byte MASK_SCREENLONG = 0x30; - public final static byte SCREENLONG_ANY = 0x00; - public final static byte SCREENLONG_NO = 0x10; - public final static byte SCREENLONG_YES = 0x20; - - public final static byte MASK_UI_MODE_TYPE = 0x0f; - public final static byte UI_MODE_TYPE_ANY = 0x00; - public final static byte UI_MODE_TYPE_NORMAL = 0x01; - public final static byte UI_MODE_TYPE_DESK = 0x02; - public final static byte UI_MODE_TYPE_CAR = 0x03; - public final static byte UI_MODE_TYPE_TELEVISION = 0x04; - public final static byte UI_MODE_TYPE_APPLIANCE = 0x05; - public final static byte UI_MODE_TYPE_WATCH = 0x06; - public final static byte UI_MODE_TYPE_VR_HEADSET = 0x07; - - // start - miui - public final static byte UI_MODE_TYPE_GODZILLAUI = 0x0b; - public final static byte UI_MODE_TYPE_SMALLUI = 0x0c; - public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d; - public final static byte UI_MODE_TYPE_LARGEUI = 0x0e; - public final static byte UI_MODE_TYPE_HUGEUI = 0x0f; - // end - miui - - public final static byte MASK_UI_MODE_NIGHT = 0x30; - public final static byte UI_MODE_NIGHT_ANY = 0x00; - public final static byte UI_MODE_NIGHT_NO = 0x10; - public final static byte UI_MODE_NIGHT_YES = 0x20; - - public final static byte COLOR_HDR_MASK = 0xC; - public final static byte COLOR_HDR_NO = 0x4; - public final static byte COLOR_HDR_SHIFT = 0x2; - public final static byte COLOR_HDR_UNDEFINED = 0x0; - public final static byte COLOR_HDR_YES = 0x8; - - public final static byte COLOR_UNDEFINED = 0x0; - - public final static byte COLOR_WIDE_UNDEFINED = 0x0; - public final static byte COLOR_WIDE_NO = 0x1; - public final static byte COLOR_WIDE_YES = 0x2; - public final static byte COLOR_WIDE_MASK = 0x3; - - private static final Logger LOGGER = Logger.getLogger(ResConfigFlags.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java index 6b118fec..eaa9ba55 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java @@ -45,7 +45,7 @@ public class ResID { @Override public int hashCode() { int hash = 17; - hash = 31 * hash + this.id; + hash = 31 * hash + id; return hash; } @@ -58,6 +58,6 @@ public class ResID { return false; } final ResID other = (ResID) obj; - return this.id == other.id; + return id == other.id; } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java index 90907c04..4c21a007 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java @@ -26,20 +26,26 @@ import java.util.*; import java.util.logging.Logger; public class ResPackage { + private static final Logger LOGGER = Logger.getLogger(ResPackage.class.getName()); + private final ResTable mResTable; private final int mId; private final String mName; - private final Map mResSpecs = new LinkedHashMap<>(); - private final Map mConfigs = new LinkedHashMap<>(); - private final Map mTypes = new LinkedHashMap<>(); - private final Set mSynthesizedRes = new HashSet<>(); + private final Map mResSpecs; + private final Map mConfigs; + private final Map mTypes; + private final Set mSynthesizedRes; private ResValueFactory mValueFactory; public ResPackage(ResTable resTable, int id, String name) { - this.mResTable = resTable; - this.mId = id; - this.mName = name; + mResTable = resTable; + mId = id; + mName = name; + mResSpecs = new LinkedHashMap<>(); + mConfigs = new LinkedHashMap<>(); + mTypes = new LinkedHashMap<>(); + mSynthesizedRes = new HashSet<>(); } public List listResSpecs() { @@ -159,17 +165,17 @@ public class ResPackage { return false; } final ResPackage other = (ResPackage) obj; - if (!Objects.equals(this.mResTable, other.mResTable)) { + if (!Objects.equals(mResTable, other.mResTable)) { return false; } - return this.mId == other.mId; + return mId == other.mId; } @Override public int hashCode() { int hash = 17; - hash = 31 * hash + (this.mResTable != null ? this.mResTable.hashCode() : 0); - hash = 31 * hash + this.mId; + hash = 31 * hash + (mResTable != null ? mResTable.hashCode() : 0); + hash = 31 * hash + mId; return hash; } @@ -179,6 +185,4 @@ public class ResPackage { } return mValueFactory; } - - private final static Logger LOGGER = Logger.getLogger(ResPackage.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java index 724f3ab9..9657877a 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java @@ -18,37 +18,36 @@ package brut.androlib.res.data; import brut.androlib.exceptions.AndrolibException; import brut.androlib.exceptions.UndefinedResObjectException; +import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; public class ResResSpec { + private static final Set EMPTY_RESOURCE_NAMES = Sets.newHashSet( + "0_resource_name_obfuscated", "(name removed)" + ); + private final ResID mId; private final String mName; private final ResPackage mPackage; private final ResTypeSpec mType; - private final Map mResources = new LinkedHashMap<>(); - private static final Set EMPTY_RESOURCE_NAMES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( - "0_resource_name_obfuscated", - "(name removed)" - ))); + private final Map mResources; public ResResSpec(ResID id, String name, ResPackage pkg, ResTypeSpec type) { - this.mId = id; + mId = id; if (name == null || name.isEmpty() || EMPTY_RESOURCE_NAMES.contains(name)) { name = "APKTOOL_DUMMYVAL_" + id.toString(); } else if (type.getResSpecUnsafe(name) != null) { name = String.format("APKTOOL_DUPLICATE_%s_%s", type, id.toString()); } - this.mName = name; - this.mPackage = pkg; - this.mType = type; + mName = name; + mPackage = pkg; + mType = type; + mResources = new LinkedHashMap<>(); } public Set listResources() { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java index f35aecff..ac4c45f2 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java @@ -25,9 +25,9 @@ public class ResResource { private final ResValue mValue; public ResResource(ResType config, ResResSpec spec, ResValue value) { - this.mConfig = config; - this.mResSpec = spec; - this.mValue = value; + mConfig = config; + mResSpec = spec; + mValue = value; } public String getFilePath() { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java index b568a2ce..c2c51dda 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java @@ -25,33 +25,30 @@ import brut.androlib.apk.UsesFramework; import brut.androlib.res.Framework; import brut.androlib.res.data.value.ResValue; import brut.androlib.res.decoder.ARSCDecoder; -import brut.androlib.res.xml.ResXmlPatcher; +import brut.androlib.res.xml.ResXmlUtils; import brut.directory.Directory; import brut.directory.DirectoryException; import brut.directory.ExtFile; import com.google.common.base.Strings; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.util.*; import java.util.logging.Logger; public class ResTable { - private final static Logger LOGGER = Logger.getLogger(ApkDecoder.class.getName()); + private static final Logger LOGGER = Logger.getLogger(ApkDecoder.class.getName()); private final Config mConfig; private final ApkInfo mApkInfo; - private final Map mPackagesById = new HashMap<>(); - private final Map mPackagesByName = new HashMap<>(); - private final Set mMainPackages = new LinkedHashSet<>(); - private final Set mFramePackages = new LinkedHashSet<>(); + private final Map mPackagesById; + private final Map mPackagesByName; + private final Set mMainPackages; + private final Set mFramePackages; private String mPackageRenamed; private String mPackageOriginal; private int mPackageId; - - private boolean mMainPkgLoaded = false; + private boolean mMainPkgLoaded; public ResTable() { this(Config.getDefaultConfig(), new ApkInfo()); @@ -64,6 +61,10 @@ public class ResTable { public ResTable(Config config, ApkInfo apkInfo) { mConfig = config; mApkInfo = apkInfo; + mPackagesById = new HashMap<>(); + mPackagesByName = new HashMap<>(); + mMainPackages = new LinkedHashSet<>(); + mFramePackages = new LinkedHashSet<>(); } public boolean getAnalysisMode() { @@ -175,11 +176,13 @@ public class ResTable { return pkg; } - private ResPackage[] loadResPackagesFromApk(ExtFile apkFile, boolean keepBrokenResources) throws AndrolibException { + private ResPackage[] loadResPackagesFromApk(ExtFile apkFile, boolean keepBrokenResources) + throws AndrolibException { try { Directory dir = apkFile.getDirectory(); - try (BufferedInputStream bis = new BufferedInputStream(dir.getFileInput("resources.arsc"))) { - return ARSCDecoder.decode(bis, false, keepBrokenResources, this).getPackages(); + try (BufferedInputStream in = new BufferedInputStream(dir.getFileInput("resources.arsc"))) { + ARSCDecoder decoder = new ARSCDecoder(in, this, false, keepBrokenResources); + return decoder.decode().getPackages(); } } catch (DirectoryException | IOException ex) { throw new AndrolibException("Could not load resources.arsc from file: " + apkFile, ex); @@ -220,8 +223,8 @@ public class ResTable { return pkg; } - public ResValue getValue(String package_, String type, String name) throws AndrolibException { - return getPackage(package_).getType(type).getResSpec(name).getDefaultResource().getValue(); + public ResValue getValue(String pkg, String type, String name) throws AndrolibException { + return getPackage(pkg).getType(type).getResSpec(name).getDefaultResource().getValue(); } public void addPackage(ResPackage pkg, boolean main) throws AndrolibException { @@ -304,14 +307,14 @@ public class ResTable { return false; } - public void initApkInfo(ApkInfo apkInfo, File outDir) throws AndrolibException { + public void initApkInfo(ApkInfo apkInfo, File apkDir) throws AndrolibException { apkInfo.isFrameworkApk = isFrameworkApk(); apkInfo.usesFramework = getUsesFramework(); if (!mApkInfo.sdkInfo.isEmpty()) { - updateSdkInfoFromResources(outDir); + updateSdkInfoFromResources(apkDir); } initPackageInfo(); - loadVersionName(outDir); + loadVersionName(apkDir); } private UsesFramework getUsesFramework() { @@ -327,24 +330,24 @@ public class ResTable { return info; } - private void updateSdkInfoFromResources(File outDir) { + private void updateSdkInfoFromResources(File apkDir) { String minSdkVersion = mApkInfo.getMinSdkVersion(); if (minSdkVersion != null) { - String refValue = ResXmlPatcher.pullValueFromIntegers(outDir, minSdkVersion); + String refValue = ResXmlUtils.pullValueFromIntegers(apkDir, minSdkVersion); if (refValue != null) { mApkInfo.setMinSdkVersion(refValue); } } String targetSdkVersion = mApkInfo.getTargetSdkVersion(); if (targetSdkVersion != null) { - String refValue = ResXmlPatcher.pullValueFromIntegers(outDir, targetSdkVersion); + String refValue = ResXmlUtils.pullValueFromIntegers(apkDir, targetSdkVersion); if (refValue != null) { mApkInfo.setTargetSdkVersion(refValue); } } String maxSdkVersion = mApkInfo.getMaxSdkVersion(); if (maxSdkVersion != null) { - String refValue = ResXmlPatcher.pullValueFromIntegers(outDir, maxSdkVersion); + String refValue = ResXmlUtils.pullValueFromIntegers(apkDir, maxSdkVersion); if (refValue != null) { mApkInfo.setMaxSdkVersion(refValue); } @@ -371,9 +374,9 @@ public class ResTable { mApkInfo.packageInfo.forcedPackageId = String.valueOf(id); } - private void loadVersionName(File outDir) { + private void loadVersionName(File apkDir) { String versionName = mApkInfo.versionInfo.versionName; - String refValue = ResXmlPatcher.pullValueFromStrings(outDir, versionName); + String refValue = ResXmlUtils.pullValueFromStrings(apkDir, versionName); if (refValue != null) { mApkInfo.versionInfo.versionName = refValue; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java index b93bf0fa..355174ac 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java @@ -22,10 +22,11 @@ import java.util.*; public class ResType { private final ResConfigFlags mFlags; - private final Map mResources = new LinkedHashMap<>(); + private final Map mResources; public ResType(ResConfigFlags flags) { - this.mFlags = flags; + mFlags = flags; + mResources = new LinkedHashMap<>(); } public ResResource getResource(ResResSpec spec) throws AndrolibException { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java index 57e99e00..b82640fd 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java @@ -21,7 +21,6 @@ import brut.androlib.exceptions.UndefinedResObjectException; import java.util.*; public final class ResTypeSpec { - public static final String RES_TYPE_NAME_ARRAY = "array"; public static final String RES_TYPE_NAME_ATTR = "attr"; public static final String RES_TYPE_NAME_ATTR_PRIVATE = "^attr-private"; @@ -30,13 +29,13 @@ public final class ResTypeSpec { public static final String RES_TYPE_NAME_STYLES = "style"; private final String mName; - private final Map mResSpecs = new LinkedHashMap<>(); - private final int mId; + private final Map mResSpecs; public ResTypeSpec(String name, int id) { - this.mName = name; - this.mId = id; + mName = name; + mId = id; + mResSpecs = new LinkedHashMap<>(); } public String getName() { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java index dc860fb1..b1f967a6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java @@ -24,12 +24,13 @@ public class ResValuesFile { private final ResPackage mPackage; private final ResTypeSpec mType; private final ResType mConfig; - private final Set mResources = new LinkedHashSet<>(); + private final Set mResources; public ResValuesFile(ResPackage pkg, ResTypeSpec type, ResType config) { - this.mPackage = pkg; - this.mType = type; - this.mConfig = config; + mPackage = pkg; + mType = type; + mConfig = config; + mResources = new LinkedHashSet<>(); } public String getPath() { @@ -63,17 +64,17 @@ public class ResValuesFile { return false; } final ResValuesFile other = (ResValuesFile) obj; - if (!Objects.equals(this.mType, other.mType)) { + if (!Objects.equals(mType, other.mType)) { return false; } - return Objects.equals(this.mConfig, other.mConfig); + return Objects.equals(mConfig, other.mConfig); } @Override public int hashCode() { int hash = 17; - hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0); - hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0); + hash = 31 * hash + (mType != null ? mType.hashCode() : 0); + hash = 31 * hash + (mConfig != null ? mConfig.hashCode() : 0); return hash; } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/ARSCData.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/ARSCData.java index aad1a24c..e6c7a68d 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/ARSCData.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/ARSCData.java @@ -22,11 +22,13 @@ import brut.androlib.res.data.ResPackage; import java.util.logging.Logger; public class ARSCData { + private static final Logger LOGGER = Logger.getLogger(ARSCData.class.getName()); + private final ResPackage[] mPackages; private final FlagsOffset[] mFlagsOffsets; - public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets) { - mPackages = packages; + public ARSCData(ResPackage[] pkgs, FlagsOffset[] flagsOffsets) { + mPackages = pkgs; mFlagsOffsets = flagsOffsets; } @@ -63,6 +65,4 @@ public class ARSCData { } return id; } - - private static final Logger LOGGER = Logger.getLogger(ARSCData.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/ARSCHeader.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/ARSCHeader.java index 5fe443dd..88383d93 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/ARSCHeader.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/ARSCHeader.java @@ -16,7 +16,6 @@ */ package brut.androlib.res.data.arsc; -import brut.util.ExtCountingDataInput; import brut.util.ExtDataInput; import java.io.EOFException; @@ -25,13 +24,40 @@ import java.math.BigInteger; import java.util.logging.Logger; public class ARSCHeader { + private static final Logger LOGGER = Logger.getLogger(ARSCHeader.class.getName()); + + public static final short RES_NONE_TYPE = -1; + public static final short RES_NULL_TYPE = 0x0000; + public static final short RES_STRING_POOL_TYPE = 0x0001; + public static final short RES_TABLE_TYPE = 0x0002; + public static final short RES_XML_TYPE = 0x0003; + + // RES_TABLE_TYPE Chunks + public static final short XML_TYPE_PACKAGE = 0x0200; + public static final short XML_TYPE_TYPE = 0x0201; + public static final short XML_TYPE_SPEC_TYPE = 0x0202; + public static final short XML_TYPE_LIBRARY = 0x0203; + public static final short XML_TYPE_OVERLAY = 0x0204; + public static final short XML_TYPE_OVERLAY_POLICY = 0x0205; + public static final short XML_TYPE_STAGED_ALIAS = 0x0206; + + // RES_XML_TYPE Chunks + public static final short RES_XML_FIRST_CHUNK_TYPE = 0x0100; + public static final short RES_XML_START_NAMESPACE_TYPE = 0x0100; + public static final short RES_XML_END_NAMESPACE_TYPE = 0x0101; + public static final short RES_XML_START_ELEMENT_TYPE = 0x0102; + public static final short RES_XML_END_ELEMENT_TYPE = 0x0103; + public static final short RES_XML_CDATA_TYPE = 0x0104; + public static final short RES_XML_LAST_CHUNK_TYPE = 0x017f; + public static final short RES_XML_RESOURCE_MAP_TYPE = 0x0180; + public final short type; public final int headerSize; public final int chunkSize; - public final int startPosition; - public final int endPosition; + public final long startPosition; + public final long endPosition; - public ARSCHeader(short type, int headerSize, int chunkSize, int headerStart) { + public ARSCHeader(short type, int headerSize, int chunkSize, long headerStart) { this.type = type; this.headerSize = headerSize; this.chunkSize = chunkSize; @@ -39,9 +65,9 @@ public class ARSCHeader { this.endPosition = headerStart + chunkSize; } - public static ARSCHeader read(ExtCountingDataInput in) throws IOException { + public static ARSCHeader read(ExtDataInput in) throws IOException { short type; - int start = in.position(); + long start = in.position(); try { type = in.readShort(); } catch (EOFException ex) { @@ -50,13 +76,13 @@ public class ARSCHeader { return new ARSCHeader(type, in.readShort(), in.readInt(), start); } - public void checkForUnreadHeader(ExtCountingDataInput in) throws IOException { + public void checkForUnreadHeader(ExtDataInput in) throws IOException { // Some applications lie about the reported size of their chunk header. Trusting the chunkSize is misleading // So compare to what we actually read in the header vs reported and skip the rest. // However, this runs after each chunk and not every chunk reading has a specific distinction between the // header and the body. - int actualHeaderSize = in.position() - this.startPosition; - int exceedingSize = this.headerSize - actualHeaderSize; + int actualHeaderSize = (int) (in.position() - startPosition); + int exceedingSize = headerSize - actualHeaderSize; if (exceedingSize > 0) { byte[] buf = new byte[exceedingSize]; in.readFully(buf); @@ -64,11 +90,11 @@ public class ARSCHeader { if (exceedingBI.equals(BigInteger.ZERO)) { LOGGER.fine(String.format("Chunk header size (%d), read (%d), but exceeding bytes are all zero.", - this.headerSize, actualHeaderSize + headerSize, actualHeaderSize )); } else { LOGGER.warning(String.format("Chunk header size (%d), read (%d). Exceeding bytes: 0x%X.", - this.headerSize, actualHeaderSize, exceedingBI + headerSize, actualHeaderSize, exceedingBI )); } } @@ -77,31 +103,4 @@ public class ARSCHeader { public void skipChunk(ExtDataInput in) throws IOException { in.skipBytes(chunkSize - headerSize); } - - public final static short RES_NONE_TYPE = -1; - public final static short RES_NULL_TYPE = 0x0000; - public final static short RES_STRING_POOL_TYPE = 0x0001; - public final static short RES_TABLE_TYPE = 0x0002; - public final static short RES_XML_TYPE = 0x0003; - - // RES_TABLE_TYPE Chunks - public final static short XML_TYPE_PACKAGE = 0x0200; - public final static short XML_TYPE_TYPE = 0x0201; - public final static short XML_TYPE_SPEC_TYPE = 0x0202; - public final static short XML_TYPE_LIBRARY = 0x0203; - public final static short XML_TYPE_OVERLAY = 0x0204; - public final static short XML_TYPE_OVERLAY_POLICY = 0x0205; - public final static short XML_TYPE_STAGED_ALIAS = 0x0206; - - // RES_XML_TYPE Chunks - public final static short RES_XML_FIRST_CHUNK_TYPE = 0x0100; - public final static short RES_XML_START_NAMESPACE_TYPE = 0x0100; - public final static short RES_XML_END_NAMESPACE_TYPE = 0x0101; - public final static short RES_XML_START_ELEMENT_TYPE = 0x0102; - public final static short RES_XML_END_ELEMENT_TYPE = 0x0103; - public final static short RES_XML_CDATA_TYPE = 0x0104; - public final static short RES_XML_LAST_CHUNK_TYPE = 0x017f; - public final static short RES_XML_RESOURCE_MAP_TYPE = 0x0180; - - private static final Logger LOGGER = Logger.getLogger(ARSCHeader.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/EntryData.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/EntryData.java index 66fe78b6..6660bd72 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/EntryData.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/arsc/EntryData.java @@ -19,7 +19,7 @@ package brut.androlib.res.data.arsc; import brut.androlib.res.data.value.ResValue; public class EntryData { - public short mFlags; - public int mSpecNamesId; - public ResValue mValue; + public short flags; + public int specNamesId; + public ResValue value; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/axml/NamespaceStack.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/axml/NamespaceStack.java index c07731c7..893f13ff 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/axml/NamespaceStack.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/axml/NamespaceStack.java @@ -31,38 +31,38 @@ package brut.androlib.res.data.axml; * !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !! */ public final class NamespaceStack { - private int[] m_data; - private int m_dataLength; - private int m_depth; + private int[] mData; + private int mDataLength; + private int mDepth; public NamespaceStack() { - m_data = new int[32]; + mData = new int[32]; } public void reset() { - m_dataLength = 0; - m_depth = 0; + mDataLength = 0; + mDepth = 0; } public int getCurrentCount() { - if (m_dataLength == 0) { + if (mDataLength == 0) { return 0; } - int offset = m_dataLength - 1; - return m_data[offset]; + int offset = mDataLength - 1; + return mData[offset]; } public int getAccumulatedCount(int depth) { - if (m_dataLength == 0 || depth < 0) { + if (mDataLength == 0 || depth < 0) { return 0; } - if (depth > m_depth) { - depth = m_depth; + if (depth > mDepth) { + depth = mDepth; } int accumulatedCount = 0; int offset = 0; for (; depth != 0; --depth) { - int count = m_data[offset]; + int count = mData[offset]; accumulatedCount += count; offset += (2 + count * 2); } @@ -70,34 +70,34 @@ public final class NamespaceStack { } public void push(int prefix, int uri) { - if (m_depth == 0) { + if (mDepth == 0) { increaseDepth(); } ensureDataCapacity(2); - int offset = m_dataLength - 1; - int count = m_data[offset]; - m_data[offset - 1 - count * 2] = count + 1; - m_data[offset] = prefix; - m_data[offset + 1] = uri; - m_data[offset + 2] = count + 1; - m_dataLength += 2; + int offset = mDataLength - 1; + int count = mData[offset]; + mData[offset - 1 - count * 2] = count + 1; + mData[offset] = prefix; + mData[offset + 1] = uri; + mData[offset + 2] = count + 1; + mDataLength += 2; } public boolean pop() { - if (m_dataLength == 0) { + if (mDataLength == 0) { return false; } - int offset = m_dataLength - 1; - int count = m_data[offset]; + int offset = mDataLength - 1; + int count = mData[offset]; if (count == 0) { return false; } count -= 1; offset -= 2; - m_data[offset] = count; + mData[offset] = count; offset -= (1 + count * 2); - m_data[offset] = count; - m_dataLength -= 2; + mData[offset] = count; + mDataLength -= 2; return true; } @@ -114,58 +114,58 @@ public final class NamespaceStack { } public int getDepth() { - return m_depth; + return mDepth; } public void increaseDepth() { ensureDataCapacity(2); - int offset = m_dataLength; - m_data[offset] = 0; - m_data[offset + 1] = 0; - m_dataLength += 2; - m_depth += 1; + int offset = mDataLength; + mData[offset] = 0; + mData[offset + 1] = 0; + mDataLength += 2; + mDepth += 1; } public void decreaseDepth() { - if (m_dataLength == 0) { + if (mDataLength == 0) { return; } - int offset = m_dataLength - 1; - int count = m_data[offset]; + int offset = mDataLength - 1; + int count = mData[offset]; if ((offset - 1 - count * 2) == 0) { return; } - m_dataLength -= 2 + count * 2; - m_depth -= 1; + mDataLength -= 2 + count * 2; + mDepth -= 1; } private void ensureDataCapacity(int capacity) { - int available = (m_data.length - m_dataLength); + int available = (mData.length - mDataLength); if (available > capacity) { return; } - int newLength = (m_data.length + available) * 2; + int newLength = (mData.length + available) * 2; int[] newData = new int[newLength]; - System.arraycopy(m_data, 0, newData, 0, m_dataLength); - m_data = newData; + System.arraycopy(mData, 0, newData, 0, mDataLength); + mData = newData; } private int find(int prefixOrUri, boolean prefix) { - if (m_dataLength == 0) { + if (mDataLength == 0) { return -1; } - int offset = m_dataLength - 1; - for (int i = m_depth; i != 0; --i) { - int count = m_data[offset]; + int offset = mDataLength - 1; + for (int i = mDepth; i != 0; --i) { + int count = mData[offset]; offset -= 2; for (; count != 0; --count) { if (prefix) { - if (m_data[offset] == prefixOrUri) { - return m_data[offset + 1]; + if (mData[offset] == prefixOrUri) { + return mData[offset + 1]; } } else { - if (m_data[offset + 1] == prefixOrUri) { - return m_data[offset]; + if (mData[offset + 1] == prefixOrUri) { + return mData[offset]; } } offset -= 2; @@ -175,12 +175,12 @@ public final class NamespaceStack { } private int get(int index, boolean prefix) { - if (m_dataLength == 0 || index < 0) { + if (mDataLength == 0 || index < 0) { return -1; } int offset = 0; - for (int i = m_depth; i != 0; --i) { - int count = m_data[offset]; + for (int i = mDepth; i != 0; --i) { + int count = mData[offset]; if (index >= count) { index -= count; offset += (2 + count * 2); @@ -190,7 +190,7 @@ public final class NamespaceStack { if (!prefix) { offset += 1; } - return m_data[offset]; + return mData[offset]; } return -1; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ninepatch/NinePatchData.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ninepatch/NinePatchData.java index 28d5f4f4..f2473155 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ninepatch/NinePatchData.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ninepatch/NinePatchData.java @@ -17,6 +17,7 @@ package brut.androlib.res.data.ninepatch; import brut.util.ExtDataInput; + import java.io.IOException; public class NinePatchData { @@ -32,19 +33,19 @@ public class NinePatchData { this.yDivs = yDivs; } - public static NinePatchData decode(ExtDataInput di) throws IOException { - di.skipBytes(1); // wasDeserialized - byte numXDivs = di.readByte(); - byte numYDivs = di.readByte(); - 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); // colorsOffset - int[] xDivs = di.readIntArray(numXDivs); - int[] yDivs = di.readIntArray(numYDivs); + public static NinePatchData decode(ExtDataInput in) throws IOException { + in.skipBytes(1); // wasDeserialized + byte numXDivs = in.readByte(); + byte numYDivs = in.readByte(); + in.skipBytes(1); // numColors + in.skipBytes(8); // xDivs/yDivs offset + int padLeft = in.readInt(); + int padRight = in.readInt(); + int padTop = in.readInt(); + int padBottom = in.readInt(); + in.skipBytes(4); // colorsOffset + int[] xDivs = in.readIntArray(numXDivs); + int[] yDivs = in.readIntArray(numYDivs); return new NinePatchData(padLeft, padRight, padTop, padBottom, xDivs, yDivs); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ninepatch/OpticalInset.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ninepatch/OpticalInset.java index 49ddee8f..1836ddfa 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ninepatch/OpticalInset.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ninepatch/OpticalInset.java @@ -17,6 +17,7 @@ package brut.androlib.res.data.ninepatch; import brut.util.ExtDataInput; + import java.io.IOException; public class OpticalInset { @@ -29,11 +30,11 @@ public class OpticalInset { 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()); + public static OpticalInset decode(ExtDataInput in) throws IOException { + int layoutBoundsLeft = Integer.reverseBytes(in.readInt()); + int layoutBoundsTop = Integer.reverseBytes(in.readInt()); + int layoutBoundsRight = Integer.reverseBytes(in.readInt()); + int layoutBoundsBottom = Integer.reverseBytes(in.readInt()); return new OpticalInset(layoutBoundsLeft, layoutBoundsTop, layoutBoundsRight, layoutBoundsBottom); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java index d4d34beb..06a14444 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java @@ -20,15 +20,19 @@ import brut.androlib.exceptions.AndrolibException; import brut.androlib.res.data.ResResource; import brut.androlib.res.xml.ResValuesXmlSerializable; import brut.util.Duo; +import com.google.common.collect.Sets; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; -import java.util.Arrays; +import java.util.Set; public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializable { + private static final Set ALLOWED_ARRAY_TYPES = Sets.newHashSet("string", "integer"); + + private final ResScalarValue[] mItems; + ResArrayValue(ResReferenceValue parent, Duo[] items) { super(parent); - mItems = new ResScalarValue[items.length]; for (int i = 0; i < items.length; i++) { mItems[i] = items[i].m2; @@ -83,12 +87,9 @@ public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializab return null; } } - if (!Arrays.asList(AllowedArrayTypes).contains(type)) { + if (!ALLOWED_ARRAY_TYPES.contains(type)) { return "string"; } return type; } - - private final ResScalarValue[] mItems; - private final String[] AllowedArrayTypes = {"string", "integer"}; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java index 982a78c5..b2551b4f 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java @@ -26,6 +26,28 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; public class ResAttr extends ResBagValue implements ResValuesXmlSerializable { + private static final int BAG_KEY_ATTR_MIN = 0x01000001; + private static final int BAG_KEY_ATTR_MAX = 0x01000002; + private static final int BAG_KEY_ATTR_L10N = 0x01000003; + + private static final int TYPE_REFERENCE = 0x01; + private static final int TYPE_STRING = 0x02; + private static final int TYPE_INT = 0x04; + private static final int TYPE_BOOL = 0x08; + private static final int TYPE_COLOR = 0x10; + private static final int TYPE_FLOAT = 0x20; + private static final int TYPE_DIMEN = 0x40; + private static final int TYPE_FRACTION = 0x80; + private static final int TYPE_ANY_STRING = 0xee; + + private static final int TYPE_ENUM = 0x00010000; + private static final int TYPE_FLAGS = 0x00020000; + + private final int mType; + private final Integer mMin; + private final Integer mMax; + private final Boolean mL10n; + ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, Boolean l10n) { super(parentVal); mType = type; @@ -139,26 +161,4 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable { } return s.substring(1); } - - private final int mType; - private final Integer mMin; - private final Integer mMax; - private final Boolean mL10n; - - private static final int BAG_KEY_ATTR_MIN = 0x01000001; - private static final int BAG_KEY_ATTR_MAX = 0x01000002; - private static final int BAG_KEY_ATTR_L10N = 0x01000003; - - private final static int TYPE_REFERENCE = 0x01; - private final static int TYPE_STRING = 0x02; - private final static int TYPE_INT = 0x04; - private final static int TYPE_BOOL = 0x08; - private final static int TYPE_COLOR = 0x10; - private final static int TYPE_FLOAT = 0x20; - private final static int TYPE_DIMEN = 0x40; - private final static int TYPE_FRACTION = 0x80; - private final static int TYPE_ANY_STRING = 0xee; - - private static final int TYPE_ENUM = 0x00010000; - private static final int TYPE_FLAGS = 0x00020000; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java index ea77c39e..8b71aedc 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java @@ -28,7 +28,7 @@ public class ResBagValue extends ResValue implements ResValuesXmlSerializable { protected final ResReferenceValue mParent; public ResBagValue(ResReferenceValue parent) { - this.mParent = parent; + mParent = parent; } @Override diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java index 831d422e..55f2d98b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java @@ -21,7 +21,7 @@ public class ResBoolValue extends ResScalarValue { public ResBoolValue(boolean value, int rawIntValue, String rawValue) { super("bool", rawIntValue, rawValue); - this.mValue = value; + mValue = value; } public boolean getValue() { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java index bbee5ff6..7440309f 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java @@ -17,6 +17,7 @@ package brut.androlib.res.data.value; public class ResColorValue extends ResIntValue { + public ResColorValue(int value, String rawValue) { super(value, rawValue, "color"); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java index bfdb24f2..667cead4 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java @@ -20,6 +20,7 @@ import android.util.TypedValue; import brut.androlib.exceptions.AndrolibException; public class ResDimenValue extends ResIntValue { + public ResDimenValue(int value, String rawValue) { super(value, rawValue, "dimen"); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEmptyValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEmptyValue.java index 3de842ec..8f2f23fa 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEmptyValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEmptyValue.java @@ -20,18 +20,18 @@ import brut.androlib.exceptions.AndrolibException; public class ResEmptyValue extends ResScalarValue { protected final int mValue; - protected int type; + protected int mType; public ResEmptyValue(int value, String rawValue, int type) { this(value, rawValue, "integer"); - this.type = type; + mType = type; } public ResEmptyValue(int value, String rawValue, String type) { super(type, value, rawValue); if (value != 1) throw new UnsupportedOperationException(); - this.mValue = value; + mValue = value; } public int getValue() { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java index d8ffced4..f2bb5588 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java @@ -28,10 +28,16 @@ import java.util.Map; import java.util.logging.Logger; public class ResEnumAttr extends ResAttr { + private static final Logger LOGGER = Logger.getLogger(ResEnumAttr.class.getName()); + + private final Duo[] mItems; + private final Map mItemsCache; + ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max, Boolean l10n, Duo[] items) { super(parent, type, min, max, l10n); mItems = items; + mItemsCache = new HashMap<>(); } @Override @@ -85,9 +91,4 @@ public class ResEnumAttr extends ResAttr { } return value2; } - - private final Duo[] mItems; - private final Map mItemsCache = new HashMap<>(); - - private static final Logger LOGGER = Logger.getLogger(ResEnumAttr.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java index f4ea0ce5..f73f8f77 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java @@ -23,7 +23,7 @@ public class ResFileValue extends ResIntBasedValue { public ResFileValue(String path, int rawIntValue) { super(rawIntValue); - this.mPath = path; + mPath = path; } public String getStrippedPath() throws AndrolibException { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java index c37519f6..03b0708b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java @@ -28,6 +28,12 @@ import java.util.Arrays; import java.util.logging.Logger; public class ResFlagsAttr extends ResAttr { + private static final Logger LOGGER = Logger.getLogger(ResFlagsAttr.class.getName()); + + private final FlagItem[] mItems; + private FlagItem[] mZeroFlags; + private FlagItem[] mFlags; + ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, Boolean l10n, Duo[] items) { super(parent, type, min, max, l10n); @@ -133,11 +139,4 @@ public class ResFlagsAttr extends ResAttr { Arrays.sort(mFlags, (o1, o2) -> Integer.compare(Integer.bitCount(o2.flag), Integer.bitCount(o1.flag))); } - - private final FlagItem[] mItems; - - private FlagItem[] mZeroFlags; - private FlagItem[] mFlags; - - private static final Logger LOGGER = Logger.getLogger(ResFlagsAttr.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java index b61f2ccf..11c218d0 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java @@ -21,7 +21,7 @@ public class ResFloatValue extends ResScalarValue { public ResFloatValue(float value, int rawIntValue, String rawValue) { super("float", rawIntValue, rawValue); - this.mValue = value; + mValue = value; } public float getValue() { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java index a98c2bb0..8a0e9eda 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java @@ -20,6 +20,7 @@ import android.util.TypedValue; import brut.androlib.exceptions.AndrolibException; public class ResFractionValue extends ResIntValue { + public ResFractionValue(int value, String rawValue) { super(value, rawValue, "fraction"); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java index dc4271ab..c6bd9632 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java @@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; public class ResIdValue extends ResValue implements ResValuesXmlSerializable { + @Override public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) throws IOException { serializer.startTag(null, "item"); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java index 3a00b1b1..a0c87935 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java @@ -21,16 +21,16 @@ import brut.androlib.exceptions.AndrolibException; public class ResIntValue extends ResScalarValue { protected final int mValue; - private int type; + private int mType; public ResIntValue(int value, String rawValue, int type) { this(value, rawValue, "integer"); - this.type = type; + mType = type; } public ResIntValue(int value, String rawValue, String type) { super(type, value, rawValue); - this.mValue = value; + mValue = value; } public int getValue() { @@ -39,6 +39,6 @@ public class ResIntValue extends ResScalarValue { @Override protected String encodeAsResXml() throws AndrolibException { - return TypedValue.coerceToString(type, mValue); + return TypedValue.coerceToString(mType, mValue); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java index 34e90a42..727b3a61 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java @@ -26,9 +26,14 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializable { + private static final String[] QUANTITY_MAP = { "other", "zero", "one", "two", "few", "many" }; + + private static final int BAG_KEY_PLURALS_START = 0x01000004; + + private final ResScalarValue[] mItems; + ResPluralsValue(ResReferenceValue parent, Duo[] items) { super(parent); - mItems = new ResScalarValue[6]; for (Duo item : items) { mItems[item.m1 - BAG_KEY_PLURALS_START] = item.m2; @@ -53,9 +58,4 @@ public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializ } serializer.endTag(null, "plurals"); } - - private final ResScalarValue[] mItems; - - public static final int BAG_KEY_PLURALS_START = 0x01000004; - private static final String[] QUANTITY_MAP = new String[] { "other", "zero", "one", "two", "few", "many" }; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java index 4cf2c842..09b01613 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java @@ -25,14 +25,13 @@ public class ResReferenceValue extends ResIntValue { private final ResPackage mPackage; private final boolean mTheme; - public ResReferenceValue(ResPackage package_, int value, String rawValue) { - this(package_, value, rawValue, false); + public ResReferenceValue(ResPackage pkg, int value, String rawValue) { + this(pkg, value, rawValue, false); } - public ResReferenceValue(ResPackage package_, int value, String rawValue, - boolean theme) { + public ResReferenceValue(ResPackage pkg, int value, String rawValue, boolean theme) { super(value, rawValue, "reference"); - mPackage = package_; + mPackage = pkg; mTheme = theme; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java index a2f72632..55a02927 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java @@ -115,8 +115,9 @@ public abstract class ResScalarValue extends ResIntBasedValue implements return mType; } - protected void serializeExtraXmlAttrs(XmlSerializer serializer, - ResResource res) throws IOException { + protected void serializeExtraXmlAttrs(XmlSerializer serializer, ResResource res) + throws IOException { + // stub } protected abstract String encodeAsResXml() throws AndrolibException; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java index 0cbb49b4..beabb6a4 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java @@ -25,6 +25,8 @@ import java.io.IOException; import java.util.regex.Pattern; public class ResStringValue extends ResScalarValue { + private static final Pattern ALL_DIGITS = Pattern.compile("\\d{9,}"); + public ResStringValue(String value, int rawValue) { this(value, rawValue, "string"); } @@ -64,8 +66,6 @@ public class ResStringValue extends ResScalarValue { if (val == null || val.isEmpty()) { return val; } - return allDigits.matcher(val).matches() ? "\\ " + val : val; + return ALL_DIGITS.matcher(val).matches() ? "\\ " + val : val; } - - private static final Pattern allDigits = Pattern.compile("\\d{9,}"); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java index 9cf6d74d..a548e813 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java @@ -29,9 +29,12 @@ import java.util.Set; import java.util.logging.Logger; public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializable { + private static final Logger LOGGER = Logger.getLogger(ResStyleValue.class.getName()); + + private final Duo[] mItems; + ResStyleValue(ResReferenceValue parent, Duo[] items, ResValueFactory factory) { super(parent); - mItems = new Duo[items.length]; for (int i = 0; i < items.length; i++) { mItems[i] = new Duo<>( @@ -97,8 +100,4 @@ public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializab serializer.endTag(null, "style"); processedNames.clear(); } - - private final Duo[] mItems; - - private static final Logger LOGGER = Logger.getLogger(ResStyleValue.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java index 1b535e6c..47d91d4b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java @@ -19,6 +19,7 @@ package brut.androlib.res.data.value; import brut.androlib.Config; public class ResValue { + public boolean shouldRemoveUnknownRes() { return Config.getInstance().isDecodeResolveModeRemoving(); } 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 a6af2a48..7364f5f2 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 @@ -25,8 +25,8 @@ import brut.util.Duo; public class ResValueFactory { private final ResPackage mPackage; - public ResValueFactory(ResPackage package_) { - this.mPackage = package_; + public ResValueFactory(ResPackage pkg) { + mPackage = pkg; } public ResScalarValue factory(int type, int value, String rawValue) throws AndrolibException { 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 7024f23e..d6bf1882 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 @@ -22,8 +22,7 @@ import brut.androlib.res.data.*; import brut.androlib.res.data.arsc.*; import brut.androlib.res.data.value.*; import brut.util.Duo; -import brut.util.ExtCountingDataInput; -import com.google.common.io.LittleEndianDataInputStream; +import brut.util.ExtDataInputStream; import java.io.*; import java.math.BigInteger; @@ -31,37 +30,57 @@ import java.util.*; import java.util.logging.Logger; public class ARSCDecoder { - public static ARSCData decode(InputStream arscStream, boolean findFlagsOffsets, boolean keepBroken) - throws AndrolibException { - return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable()); + private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class.getName()); + + private static final short ENTRY_FLAG_COMPLEX = 0x0001; + private static final short ENTRY_FLAG_PUBLIC = 0x0002; + private static final short ENTRY_FLAG_WEAK = 0x0004; + private static final short ENTRY_FLAG_COMPACT = 0x0008; + + private static final short TABLE_TYPE_FLAG_SPARSE = 0x01; + private static final short TABLE_TYPE_FLAG_OFFSET16 = 0x02; + + private static final int KNOWN_CONFIG_BYTES = 64; + + private static final int NO_ENTRY = 0xFFFFFFFF; + private static final int NO_ENTRY_OFFSET16 = 0xFFFF; + + private final ExtDataInputStream mIn; + private final ResTable mResTable; + private final List mFlagsOffsets; + private final boolean mKeepBroken; + private final HashMap mMissingResSpecMap; + private final HashMap mResTypeSpecs; + + private ARSCHeader mHeader; + private StringBlock mTableStrings; + private StringBlock mTypeNames; + private StringBlock mSpecNames; + private ResPackage mPkg; + private ResTypeSpec mTypeSpec; + private ResType mType; + private int mResId; + private int mTypeIdOffset; + + public ARSCDecoder(InputStream in, ResTable resTable, boolean storeFlagsOffsets, boolean keepBroken) { + mIn = ExtDataInputStream.littleEndian(in); + mResTable = resTable != null ? resTable : new ResTable(); + mFlagsOffsets = storeFlagsOffsets ? new ArrayList<>() : null; + mKeepBroken = keepBroken; + mMissingResSpecMap = new LinkedHashMap<>(); + mResTypeSpecs = new HashMap<>(); } - public static ARSCData decode(InputStream arscStream, boolean findFlagsOffsets, boolean keepBroken, - ResTable resTable) - throws AndrolibException { + public ARSCData decode() throws AndrolibException { try { - ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable, findFlagsOffsets, keepBroken); - ResPackage[] pkgs = decoder.readResourceTable(); - return new ARSCData(pkgs, decoder.mFlagsOffsets == null - ? null - : decoder.mFlagsOffsets.toArray(new FlagsOffset[0])); + ResPackage[] pkgs = readResourceTable(); + FlagsOffset[] flagsOffsets = mFlagsOffsets != null ? mFlagsOffsets.toArray(new FlagsOffset[0]) : null; + return new ARSCData(pkgs, flagsOffsets); } catch (IOException ex) { throw new AndrolibException("Could not decode arsc file", ex); } } - private ARSCDecoder(InputStream arscStream, ResTable resTable, boolean storeFlagsOffsets, boolean keepBroken) { - if (storeFlagsOffsets) { - mFlagsOffsets = new ArrayList<>(); - } else { - mFlagsOffsets = null; - } - mIn = new ExtCountingDataInput(new LittleEndianDataInputStream(arscStream)); - mResTable = resTable; - mKeepBroken = keepBroken; - mMissingResSpecMap = new LinkedHashMap<>(); - } - private ResPackage[] readResourceTable() throws IOException, AndrolibException { Set pkgs = new LinkedHashSet<>(); ResTypeSpec typeSpec; @@ -170,7 +189,7 @@ public class ARSCDecoder { // TypeIdOffset was added platform_frameworks_base/@f90f2f8dc36e7243b85e0b6a7fd5a590893c827e // which is only in split/new applications. - int splitHeaderSize = (2 + 2 + 4 + 4 + (2 * 128) + (4 * 5)); // short, short, int, int, char[128], int * 4 + int splitHeaderSize = 2 + 2 + 4 + 4 + (2 * 128) + (4 * 5); // short, short, int, int, char[128], int * 4 if (mHeader.headerSize == splitHeaderSize) { mTypeIdOffset = mIn.readInt(); } @@ -246,7 +265,7 @@ public class ARSCDecoder { int entryCount = mIn.readInt(); if (mFlagsOffsets != null) { - mFlagsOffsets.add(new FlagsOffset(mIn.position(), entryCount)); + mFlagsOffsets.add(new FlagsOffset((int) mIn.position(), entryCount)); } mHeader.checkForUnreadHeader(mIn); @@ -310,11 +329,11 @@ public class ARSCDecoder { } } - mType = flags.isInvalid && !mKeepBroken ? null : mPkg.getOrCreateConfig(flags); + mType = !flags.isInvalid || mKeepBroken ? mPkg.getOrCreateConfig(flags) : null; int noEntry = isOffset16 ? NO_ENTRY_OFFSET16 : NO_ENTRY; // #3428 - In some applications the res entries are padded for alignment. - int entriesStartAligned = mHeader.startPosition + entriesStart; + long entriesStartAligned = mHeader.startPosition + entriesStart; if (mIn.position() < entriesStartAligned) { long bytesSkipped = mIn.skip(entriesStartAligned - mIn.position()); LOGGER.fine(String.format("Skipping: %d byte(s) to align with ResTable_entry start.", bytesSkipped)); @@ -394,15 +413,15 @@ public class ARSCDecoder { } EntryData entryData = new EntryData(); - entryData.mFlags = flags; - entryData.mSpecNamesId = specNamesId; - entryData.mValue = value; + entryData.flags = flags; + entryData.specNamesId = specNamesId; + entryData.value = value; return entryData; } private void readEntry(EntryData entryData) throws AndrolibException { - int specNamesId = entryData.mSpecNamesId; - ResValue value = entryData.mValue; + int specNamesId = entryData.specNamesId; + ResValue value = entryData.value; if (mTypeSpec.isString() && value instanceof ResFileValue) { value = new ResStringValue(value.toString(), ((ResFileValue) value).getRawIntValue()); @@ -481,8 +500,8 @@ public class ARSCDecoder { int data = mIn.readInt(); return type == TypedValue.TYPE_STRING - ? mPkg.getValueFactory().factory(mTableStrings.getHTML(data), data) - : mPkg.getValueFactory().factory(type, data, null); + ? mPkg.getValueFactory().factory(mTableStrings.getHTML(data), data) + : mPkg.getValueFactory().factory(type, data, null); } private ResConfigFlags readConfigFlags() throws IOException, AndrolibException { @@ -684,36 +703,4 @@ public class ARSCDecoder { expectedType, mHeader.type)); } } - - private final ExtCountingDataInput mIn; - private final ResTable mResTable; - private final List mFlagsOffsets; - private final boolean mKeepBroken; - - private ARSCHeader mHeader; - private StringBlock mTableStrings; - private StringBlock mTypeNames; - private StringBlock mSpecNames; - private ResPackage mPkg; - private ResTypeSpec mTypeSpec; - private ResType mType; - private int mResId; - private int mTypeIdOffset = 0; - private final HashMap mMissingResSpecMap; - private final HashMap mResTypeSpecs = new HashMap<>(); - - private final static short ENTRY_FLAG_COMPLEX = 0x0001; - private final static short ENTRY_FLAG_PUBLIC = 0x0002; - private final static short ENTRY_FLAG_WEAK = 0x0004; - private final static short ENTRY_FLAG_COMPACT = 0x0008; - - private final static short TABLE_TYPE_FLAG_SPARSE = 0x01; - private final static short TABLE_TYPE_FLAG_OFFSET16 = 0x02; - - private static final int KNOWN_CONFIG_BYTES = 64; - - private static final int NO_ENTRY = 0xFFFFFFFF; - private static final int NO_ENTRY_OFFSET16 = 0xFFFF; - - private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java index b8deb587..e5d16483 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java @@ -29,8 +29,7 @@ import brut.androlib.res.data.axml.NamespaceStack; import brut.androlib.res.data.value.ResAttr; import brut.androlib.res.data.value.ResScalarValue; import brut.androlib.res.xml.ResXmlEncoders; -import brut.util.ExtCountingDataInput; -import com.google.common.io.LittleEndianDataInputStream; +import brut.util.ExtDataInputStream; import org.xmlpull.v1.XmlPullParserException; import java.io.*; @@ -47,9 +46,45 @@ import java.util.logging.Logger; * this state methods return invalid values or throw exceptions. */ public class AXmlResourceParser implements XmlResourceParser { + private static final Logger LOGGER = Logger.getLogger(AXmlResourceParser.class.getName()); + + private static final String E_NOT_SUPPORTED = "Method is not supported."; + private static final String ANDROID_RES_NS_AUTO = "http://schemas.android.com/apk/res-auto"; + public static final String ANDROID_RES_NS = "http://schemas.android.com/apk/res/android"; + + // ResXMLTree_attribute + private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0; // ns + private static final int ATTRIBUTE_IX_NAME = 1; // name + private static final int ATTRIBUTE_IX_VALUE_STRING = 2; // rawValue + private static final int ATTRIBUTE_IX_VALUE_TYPE = 3; // (size/res0/dataType) + private static final int ATTRIBUTE_IX_VALUE_DATA = 4; // data + private static final int ATTRIBUTE_LENGTH = 5; + + private static final int PRIVATE_PKG_ID = 0x7F; + + private final ResTable mResTable; + private final NamespaceStack mNamespaces; + + private boolean mIsOperational; + private ExtDataInputStream mIn; + private StringBlock mStringBlock; + private int[] mResourceIds; + private boolean mDecreaseDepth; + private AndrolibException mFirstError; + + // All values are essentially indices, e.g. mNameIndex is an index of name in mStringBlock. + private int mEvent; + private int mLineNumber; + private int mNameIndex; + private int mNamespaceIndex; + private int[] mAttributes; + private int mIdIndex; + private int mClassIndex; + private int mStyleIndex; public AXmlResourceParser(ResTable resTable) { mResTable = resTable; + mNamespaces = new NamespaceStack(); resetEventInfo(); } @@ -64,16 +99,16 @@ public class AXmlResourceParser implements XmlResourceParser { public void open(InputStream stream) { close(); if (stream != null) { - mIn = new ExtCountingDataInput(new LittleEndianDataInputStream(stream)); + mIn = ExtDataInputStream.littleEndian(stream); } } @Override public void close() { - if (!isOperational) { + if (!mIsOperational) { return; } - isOperational = false; + mIsOperational = false; mIn = null; mStringBlock = null; mResourceIds = null; @@ -314,17 +349,16 @@ public class AXmlResourceParser implements XmlResourceParser { private String getNonDefaultNamespaceUri(int offset) { String prefix = mStringBlock.getString(mNamespaces.getPrefix(offset)); - if (prefix != null) { - return mStringBlock.getString(mNamespaces.getUri(offset)); + if (prefix == null) { + // If we are here. There is some clever obfuscation going on. Our reference points to the namespace are gone. + // Normally we could take the index * attributeCount to get an offset. + // That would point to the URI in the StringBlock table, but that is empty. + // We have the namespaces that can't be touched in the opening tag. + // Though no known way to correlate them at this time. + // So return the res-auto namespace. + return ANDROID_RES_NS_AUTO; } - - // If we are here. There is some clever obfuscation going on. Our reference points to the namespace are gone. - // Normally we could take the index * attributeCount to get an offset. - // That would point to the URI in the StringBlock table, but that is empty. - // We have the namespaces that can't be touched in the opening tag. - // Though no known way to correlate them at this time. - // So return the res-auto namespace. - return ANDROID_RES_NS_AUTO; + return mStringBlock.getString(mNamespaces.getUri(offset)); } @Override @@ -404,7 +438,9 @@ public class AXmlResourceParser implements XmlResourceParser { int valueRaw = mAttributes[offset + ATTRIBUTE_IX_VALUE_STRING]; try { - String stringBlockValue = valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(mStringBlock.getString(valueRaw)); + String stringBlockValue = valueRaw != -1 + ? ResXmlEncoders.escapeXmlChars(mStringBlock.getString(valueRaw)) + : null; String resourceMapValue = null; // Ensure we only track down obfuscated values for reference/attribute type values. Otherwise, we might @@ -453,21 +489,20 @@ public class AXmlResourceParser implements XmlResourceParser { public float getAttributeFloatValue(int index, float defaultValue) { int offset = getAttributeOffset(index); int valueType = mAttributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_FLOAT) { - int valueData = mAttributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - return Float.intBitsToFloat(valueData); + if (valueType != TypedValue.TYPE_FLOAT) { + return defaultValue; } - return defaultValue; + return Float.intBitsToFloat(mAttributes[offset + ATTRIBUTE_IX_VALUE_DATA]); } @Override public int getAttributeIntValue(int index, int defaultValue) { int offset = getAttributeOffset(index); int valueType = mAttributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType >= TypedValue.TYPE_FIRST_INT && valueType <= TypedValue.TYPE_LAST_INT) { - return mAttributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + if (valueType < TypedValue.TYPE_FIRST_INT || valueType > TypedValue.TYPE_LAST_INT) { + return defaultValue; } - return defaultValue; + return mAttributes[offset + ATTRIBUTE_IX_VALUE_DATA]; } @Override @@ -479,10 +514,10 @@ public class AXmlResourceParser implements XmlResourceParser { public int getAttributeResourceValue(int index, int defaultValue) { int offset = getAttributeOffset(index); int valueType = mAttributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_REFERENCE) { - return mAttributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + if (valueType != TypedValue.TYPE_REFERENCE) { + return defaultValue; } - return defaultValue; + return mAttributes[offset + ATTRIBUTE_IX_VALUE_DATA]; } @Override @@ -687,7 +722,7 @@ public class AXmlResourceParser implements XmlResourceParser { mStringBlock = StringBlock.readWithChunk(mIn); mNamespaces.increaseDepth(); - isOperational = true; + mIsOperational = true; } if (mEvent == END_DOCUMENT) { @@ -698,8 +733,8 @@ public class AXmlResourceParser implements XmlResourceParser { resetEventInfo(); while (true) { - if (m_decreaseDepth) { - m_decreaseDepth = false; + if (mDecreaseDepth) { + mDecreaseDepth = false; mNamespaces.decreaseDepth(); } @@ -710,7 +745,7 @@ public class AXmlResourceParser implements XmlResourceParser { } // #2070 - Some applications have 2 start namespaces, but only 1 end namespace. - if (mIn.remaining() == 0) { + if (mIn.available() == 0) { LOGGER.warning(String.format("AXML hit unexpected end of file at byte: 0x%X", mIn.position())); mEvent = END_DOCUMENT; break; @@ -809,7 +844,7 @@ public class AXmlResourceParser implements XmlResourceParser { mNamespaceIndex = mIn.readInt(); mNameIndex = mIn.readInt(); mEvent = END_TAG; - m_decreaseDepth = true; + mDecreaseDepth = true; break; } @@ -828,40 +863,4 @@ public class AXmlResourceParser implements XmlResourceParser { mFirstError = error; } } - - private ExtCountingDataInput mIn; - private final ResTable mResTable; - private AndrolibException mFirstError; - - private boolean isOperational = false; - private StringBlock mStringBlock; - private int[] mResourceIds; - private final NamespaceStack mNamespaces = new NamespaceStack(); - private boolean m_decreaseDepth; - - // All values are essentially indices, e.g. mNameIndex is an index of name in mStringBlock. - private int mEvent; - private int mLineNumber; - private int mNameIndex; - private int mNamespaceIndex; - private int[] mAttributes; - private int mIdIndex; - private int mClassIndex; - private int mStyleIndex; - - private final static Logger LOGGER = Logger.getLogger(AXmlResourceParser.class.getName()); - private static final String E_NOT_SUPPORTED = "Method is not supported."; - - // ResXMLTree_attribute - private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0; // ns - private static final int ATTRIBUTE_IX_NAME = 1; // name - private static final int ATTRIBUTE_IX_VALUE_STRING = 2; // rawValue - private static final int ATTRIBUTE_IX_VALUE_TYPE = 3; // (size/res0/dataType) - private static final int ATTRIBUTE_IX_VALUE_DATA = 4; // data - private static final int ATTRIBUTE_LENGTH = 5; - - private static final int PRIVATE_PKG_ID = 0x7F; - - private static final String ANDROID_RES_NS_AUTO = "http://schemas.android.com/apk/res-auto"; - public static final String ANDROID_RES_NS = "http://schemas.android.com/apk/res/android"; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java index 8fd38da5..a79385d6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java @@ -25,11 +25,6 @@ import java.util.regex.Pattern; * AXmlResourceParser specifically for parsing encoded AndroidManifest.xml. */ public class AndroidManifestResourceParser extends AXmlResourceParser { - - public AndroidManifestResourceParser(ResTable resTable) { - super(resTable); - } - /** * Pattern for matching numeric string meta-data values. aapt automatically infers the * type for a manifest meta-data value based on the string in the unencoded XML. However, @@ -39,6 +34,10 @@ public class AndroidManifestResourceParser extends AXmlResourceParser { */ private static final Pattern PATTERN_NUMERIC_STRING = Pattern.compile("\\s?\\d+"); + public AndroidManifestResourceParser(ResTable resTable) { + super(resTable); + } + @Override public String getAttributeValue(int index) { String value = super.getAttributeValue(index); 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 1750ba78..20f7d349 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 @@ -21,6 +21,7 @@ import brut.androlib.exceptions.CantFind9PatchChunkException; import brut.androlib.res.data.ninepatch.NinePatchData; import brut.androlib.res.data.ninepatch.OpticalInset; import brut.util.ExtDataInput; +import brut.util.ExtDataInputStream; import org.apache.commons.io.IOUtils; import javax.imageio.ImageIO; @@ -30,6 +31,11 @@ import java.awt.image.WritableRaster; import java.io.*; 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; + @Override public void decode(InputStream in, OutputStream out) throws AndrolibException { try { @@ -122,32 +128,32 @@ public class Res9patchStreamDecoder implements ResStreamDecoder { private NinePatchData getNinePatch(byte[] data) throws AndrolibException, IOException { - ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); - find9patchChunk(di, NP_CHUNK_TYPE); - return NinePatchData.decode(di); + ExtDataInput in = ExtDataInputStream.bigEndian(new ByteArrayInputStream(data)); + find9patchChunk(in, NP_CHUNK_TYPE); + return NinePatchData.decode(in); } private OpticalInset getOpticalInset(byte[] data) throws AndrolibException, IOException { - ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); - find9patchChunk(di, OI_CHUNK_TYPE); - return OpticalInset.decode(di); + ExtDataInput in = ExtDataInputStream.bigEndian(new ByteArrayInputStream(data)); + find9patchChunk(in, OI_CHUNK_TYPE); + return OpticalInset.decode(in); } - private void find9patchChunk(DataInput di, int magic) throws AndrolibException, + private void find9patchChunk(DataInput in, int magic) throws AndrolibException, IOException { - di.skipBytes(8); + in.skipBytes(8); while (true) { int size; try { - size = di.readInt(); + size = in.readInt(); } catch (IOException ex) { - throw new CantFind9PatchChunkException("Cant find nine patch chunk", ex); + throw new CantFind9PatchChunkException("Could not find nine patch chunk", ex); } - if (di.readInt() == magic) { + if (in.readInt() == magic) { return; } - di.skipBytes(size + 4); + in.skipBytes(size + 4); } } @@ -162,9 +168,4 @@ public class Res9patchStreamDecoder implements ResStreamDecoder { im.setRGB(x, y, NP_COLOR); } } - - 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; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java index 08ca7202..1c3112ac 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java @@ -24,7 +24,7 @@ import brut.androlib.res.data.value.ResBoolValue; import brut.androlib.res.data.value.ResFileValue; import brut.directory.Directory; import brut.directory.DirectoryException; -import brut.directory.DirUtil; +import brut.directory.DirUtils; import brut.util.BrutIO; import java.io.*; @@ -33,6 +33,17 @@ import java.util.logging.Level; import java.util.logging.Logger; public class ResFileDecoder { + private static final Logger LOGGER = Logger.getLogger(ResFileDecoder.class.getName()); + + private static final String[] RAW_IMAGE_EXTENSIONS = { + "m4a", // apple + "qmg", // samsung + }; + private static final String[] RAW_9PATCH_IMAGE_EXTENSIONS = { + "qmg", // samsung + "spi", // samsung + }; + private final ResStreamDecoderContainer mDecoders; public ResFileDecoder(ResStreamDecoderContainer decoders) { @@ -109,7 +120,7 @@ public class ResFileDecoder { return; } catch (CantFind9PatchChunkException ex) { LOGGER.log(Level.WARNING, String.format( - "Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.", inFileName + "Could not find 9patch chunk in file: \"%s\". Renaming it to *.png.", inFileName ), ex); outDir.removeFile(outFileName); outFileName = outResName + ext; @@ -156,24 +167,12 @@ public class ResFileDecoder { } } - public void copyRaw(Directory inDir, Directory outDir, String inFilename, - String outFilename) throws AndrolibException { + public void copyRaw(Directory inDir, Directory outDir, String inFileName, + String outFileName) throws AndrolibException { try { - DirUtil.copyToDir(inDir, outDir, inFilename, outFilename); + DirUtils.copyToDir(inDir, outDir, inFileName, outFileName); } catch (DirectoryException ex) { throw new AndrolibException(ex); } } - - private final static Logger LOGGER = Logger.getLogger(ResFileDecoder.class.getName()); - - private final static String[] RAW_IMAGE_EXTENSIONS = new String[] { - "m4a", // apple - "qmg", // samsung - }; - - private final static String[] RAW_9PATCH_IMAGE_EXTENSIONS = new String[] { - "qmg", // samsung - "spi", // samsung - }; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java index 6f3917dc..c4c7119a 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java @@ -22,6 +22,7 @@ import org.apache.commons.io.IOUtils; import java.io.*; public class ResRawStreamDecoder implements ResStreamDecoder { + @Override public void decode(InputStream in, OutputStream out) throws AndrolibException { try { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java index ca7a7227..f9fd0edd 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java @@ -21,7 +21,11 @@ import java.io.*; import java.util.*; public class ResStreamDecoderContainer { - private final Map mDecoders = new HashMap<>(); + private final Map mDecoders; + + public ResStreamDecoderContainer() { + mDecoders = new HashMap<>(); + } public void decode(InputStream in, OutputStream out, String decoderName) throws AndrolibException { 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 2927e06c..831e741d 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 @@ -18,7 +18,7 @@ package brut.androlib.res.decoder; import brut.androlib.res.data.arsc.ARSCHeader; import brut.androlib.res.xml.ResXmlEncoders; -import brut.util.ExtCountingDataInput; +import brut.util.ExtDataInput; import com.google.common.annotations.VisibleForTesting; import java.io.IOException; @@ -29,8 +29,23 @@ import java.util.List; import java.util.logging.Logger; public class StringBlock { - public static StringBlock readWithChunk(ExtCountingDataInput reader) throws IOException { - int startPosition = reader.position(); + private static final Logger LOGGER = Logger.getLogger(StringBlock.class.getName()); + + private static final CharsetDecoder UTF16LE_DECODER = StandardCharsets.UTF_16LE.newDecoder(); + private static final CharsetDecoder UTF8_DECODER = StandardCharsets.UTF_8.newDecoder(); + private static final CharsetDecoder CESU8_DECODER = Charset.forName("CESU8").newDecoder(); + + private static final int UTF8_FLAG = 0x00000100; + private static final int STRING_BLOCK_HEADER_SIZE = 28; + + private int[] mStringOffsets; + private byte[] mStrings; + private int[] mStyleOffsets; + private int[] mStyles; + private boolean mIsUtf8; + + public static StringBlock readWithChunk(ExtDataInput reader) throws IOException { + long startPosition = reader.position(); reader.skipCheckShort(ARSCHeader.RES_STRING_POOL_TYPE); int headerSize = reader.readShort(); int chunkSize = reader.readInt(); @@ -38,9 +53,8 @@ public class StringBlock { return readWithoutChunk(reader, startPosition, headerSize, chunkSize); } - public static StringBlock readWithoutChunk(ExtCountingDataInput reader, int startPosition, int headerSize, - int chunkSize) throws IOException - { + public static StringBlock readWithoutChunk(ExtDataInput reader, long startPosition, + int headerSize, int chunkSize) throws IOException { // ResStringPool_header int stringCount = reader.readInt(); int styleCount = reader.readInt(); @@ -90,6 +104,15 @@ public class StringBlock { return block; } + private StringBlock() { + } + + @VisibleForTesting + StringBlock(byte[] strings, boolean isUTF8) { + mStrings = strings; + mIsUtf8 = isUTF8; + } + /** * Returns raw string (without any styling information) at specified index. * @param index int @@ -111,6 +134,7 @@ public class StringBlock { offset += val[0]; } length = val[1]; + return decodeString(offset, length); } @@ -154,14 +178,14 @@ public class StringBlock { if (string == null) { return -1; } - for (int i = 0; i != mStringOffsets.length; ++i) { + for (int i = 0; i != mStringOffsets.length; i++) { int offset = mStringOffsets[i]; int length = getShort(mStrings, offset); if (length != string.length()) { continue; } int j = 0; - for (; j != length; ++j) { + for (; j != length; j++) { offset += 2; if (string.charAt(j) != getShort(mStrings, offset)) { break; @@ -174,15 +198,6 @@ public class StringBlock { return -1; } - private StringBlock() { - } - - @VisibleForTesting - StringBlock(byte[] strings, boolean isUTF8) { - mStrings = strings; - mIsUtf8 = isUTF8; - } - /** * Returns style information - array of int triplets, where in each triplet: * * first int is index of tag name ('b','i', etc.) * second int is tag @@ -196,7 +211,7 @@ public class StringBlock { int count = 0; int[] style; - for (int i = offset; i < mStyles.length; ++i) { + for (int i = offset; i < mStyles.length; i++) { if (mStyles[i] == -1) { break; } @@ -262,39 +277,25 @@ public class StringBlock { val = array[offset]; offset += 1; if ((val & 0x80) != 0) { - int low = (array[offset] & 0xFF); + int low = array[offset] & 0xFF; length = ((val & 0x7F) << 8) + low; offset += 1; } else { length = val; } - return new int[] { offset, length}; + return new int[] { offset, length }; } private static int[] getUtf16(byte[] array, int offset) { - int val = ((array[offset + 1] & 0xFF) << 8 | array[offset] & 0xFF); + int val = (array[offset + 1] & 0xFF) << 8 | array[offset] & 0xFF; if ((val & 0x8000) != 0) { int high = (array[offset + 3] & 0xFF) << 8; int low = (array[offset + 2] & 0xFF); int len_value = ((val & 0x7FFF) << 16) + (high + low); - return new int[] {4, len_value * 2}; + return new int[] { 4, len_value * 2 }; } - return new int[] {2, val * 2}; + return new int[] { 2, val * 2 }; } - - private int[] mStringOffsets; - private byte[] mStrings; - private int[] mStyleOffsets; - private int[] mStyles; - private boolean mIsUtf8; - - private final CharsetDecoder UTF16LE_DECODER = StandardCharsets.UTF_16LE.newDecoder(); - private final CharsetDecoder UTF8_DECODER = StandardCharsets.UTF_8.newDecoder(); - private final CharsetDecoder CESU8_DECODER = Charset.forName("CESU8").newDecoder(); - private static final Logger LOGGER = Logger.getLogger(StringBlock.class.getName()); - - private static final int UTF8_FLAG = 0x00000100; - private static final int STRING_BLOCK_HEADER_SIZE = 28; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StyledString.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StyledString.java index 9339f505..454a8cff 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StyledString.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StyledString.java @@ -27,6 +27,8 @@ import java.util.Map; import java.util.logging.Logger; public class StyledString { + private static final Logger LOGGER = Logger.getLogger(StyledString.class.getName()); + private final String mText; private final List mSpans; @@ -52,63 +54,63 @@ public class StyledString { private static final MapSplitter ATTRIBUTES_SPLITTER = Splitter.on(';').omitEmptyStrings().withKeyValueSeparator(Splitter.on('=').limit(2)); - private final String tag; - private final int firstChar; - private final int lastChar; + private final String mTag; + private final int mFirstChar; + private final int mLastChar; public Span(String tag, int firstChar, int lastChar) { - this.tag = tag; - this.firstChar = firstChar; - this.lastChar = lastChar; + mTag = tag; + mFirstChar = firstChar; + mLastChar = lastChar; } public String getTag() { - return tag; + return mTag; } public int getFirstChar() { - return firstChar; + return mFirstChar; } public int getLastChar() { - return lastChar; + return mLastChar; } public String getName() { - int separatorIdx = tag.indexOf(';'); - return separatorIdx == -1 ? tag : tag.substring(0, separatorIdx); + int separatorIdx = mTag.indexOf(';'); + return separatorIdx == -1 ? mTag : mTag.substring(0, separatorIdx); } public Map getAttributes() { - int separatorIdx = tag.indexOf(';'); - return separatorIdx == -1 ? null : ATTRIBUTES_SPLITTER.split( - tag.substring(separatorIdx + 1, tag.endsWith(";") ? tag.length() - 1 : tag.length()) - ); + int separatorIdx = mTag.indexOf(';'); + return separatorIdx != -1 ? ATTRIBUTES_SPLITTER.split( + mTag.substring(separatorIdx + 1, mTag.endsWith(";") ? mTag.length() - 1 : mTag.length()) + ) : null; } @Override public int compareTo(Span o) { - int res = Integer.compare(firstChar, o.firstChar); + int res = Integer.compare(mFirstChar, o.mFirstChar); if (res != 0) { return res; } - res = Integer.compare(lastChar, o.lastChar); + res = Integer.compare(mLastChar, o.mLastChar); if (res != 0) { return -res; } - return -tag.compareTo(o.tag); + return -mTag.compareTo(o.mTag); } } private static class Decoder { - private String text; - private StringBuilder xmlValue; - private int lastOffset; + private String mText; + private StringBuilder mXmlValue; + private int mLastOffset; - String decode(StyledString styledString) { - text = styledString.getText(); - xmlValue = new StringBuilder(text.length() * 2); - lastOffset = 0; + public String decode(StyledString styledString) { + mText = styledString.getText(); + mXmlValue = new StringBuilder(mText.length() * 2); + mLastOffset = 0; // recurse top-level tags PeekingIterator it = Iterators.peekingIterator(styledString.getSpans().iterator()); @@ -116,11 +118,11 @@ public class StyledString { decodeIterate(it); } - // write the remaining encoded raw text - if (lastOffset < text.length()) { - xmlValue.append(ResXmlEncoders.escapeXmlChars(text.substring(lastOffset))); + // write the remaining encoded raw mText + if (mLastOffset < mText.length()) { + mXmlValue.append(ResXmlEncoders.escapeXmlChars(mText.substring(mLastOffset))); } - return xmlValue.toString(); + return mXmlValue.toString(); } private void decodeIterate(PeekingIterator it) { @@ -130,45 +132,43 @@ public class StyledString { int spanStart = span.getFirstChar(); int spanEnd = span.getLastChar() + 1; - // write encoded raw text preceding the opening tag - if (spanStart > lastOffset) { - xmlValue.append(ResXmlEncoders.escapeXmlChars(text.substring(lastOffset, spanStart))); + // write encoded raw mText preceding the opening tag + if (spanStart > mLastOffset) { + mXmlValue.append(ResXmlEncoders.escapeXmlChars(mText.substring(mLastOffset, spanStart))); } - lastOffset = spanStart; + mLastOffset = spanStart; // write opening tag - xmlValue.append('<').append(name); + mXmlValue.append('<').append(name); if (attributes != null) { for (Map.Entry attrEntry : attributes.entrySet()) { - xmlValue.append(' ').append(attrEntry.getKey()).append("=\"") + mXmlValue.append(' ').append(attrEntry.getKey()).append("=\"") .append(ResXmlEncoders.escapeXmlChars(attrEntry.getValue())).append('"'); } } // if an opening tag is followed by a matching closing tag, write as an empty-element tag if (spanStart == spanEnd) { - xmlValue.append("/>"); + mXmlValue.append("/>"); return; } - xmlValue.append('>'); + mXmlValue.append('>'); // recurse nested tags while (it.hasNext() && it.peek().getFirstChar() < spanEnd) { decodeIterate(it); } - // write encoded raw text preceding the closing tag - if (spanEnd > lastOffset && text.length() >= spanEnd) { - xmlValue.append(ResXmlEncoders.escapeXmlChars(text.substring(lastOffset, spanEnd))); - } else if (text.length() >= lastOffset && text.length() < spanEnd) { - LOGGER.warning("Span (" + name + ") exceeds text length " + text.length()); - xmlValue.append(ResXmlEncoders.escapeXmlChars(text.substring(lastOffset))); + // write encoded raw mText preceding the closing tag + if (spanEnd > mLastOffset && mText.length() >= spanEnd) { + mXmlValue.append(ResXmlEncoders.escapeXmlChars(mText.substring(mLastOffset, spanEnd))); + } else if (mText.length() >= mLastOffset && mText.length() < spanEnd) { + LOGGER.warning("Span (" + name + ") exceeds mText length " + mText.length()); + mXmlValue.append(ResXmlEncoders.escapeXmlChars(mText.substring(mLastOffset))); } - lastOffset = spanEnd; + mLastOffset = spanEnd; // write closing tag - xmlValue.append("'); + mXmlValue.append("'); } } - - private static final Logger LOGGER = Logger.getLogger(StyledString.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java index 0ee8c522..9c08626c 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java @@ -23,6 +23,6 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; public interface ResValuesXmlSerializable { - void serializeToResValuesXml(XmlSerializer serializer, - ResResource res) throws IOException, AndrolibException; + void serializeToResValuesXml(XmlSerializer serializer, ResResource res) + throws IOException, AndrolibException; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java index 74e679d3..1e64ec62 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java @@ -25,6 +25,10 @@ import java.util.List; public final class ResXmlEncoders { + private ResXmlEncoders() { + // Private constructor for utility class + } + public static String escapeXmlChars(String str) { return StringUtils.replace(StringUtils.replace(str, "&", "&"), "<", "<"); } 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/ResXmlUtils.java similarity index 91% rename from brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java rename to brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlUtils.java index 1914dde2..d9ee9a38 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/ResXmlUtils.java @@ -41,7 +41,15 @@ import java.nio.file.Files; import java.util.*; import java.util.logging.Logger; -public final class ResXmlPatcher { +public final class ResXmlUtils { + private static final Logger LOGGER = Logger.getLogger(ResXmlUtils.class.getName()); + + private static final String FEATURE_LOAD_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; + private static final String FEATURE_DISABLE_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl"; + + private ResXmlUtils() { + // Private constructor for utility class + } /** * Removes "debug" tag from file @@ -50,9 +58,6 @@ public final class ResXmlPatcher { * @throws AndrolibException Error reading Manifest file */ public static void removeApplicationDebugTag(File file) throws AndrolibException { - if (!file.exists()) { - return; - } try { Document doc = loadDocument(file); Node application = doc.getElementsByTagName("application").item(0); @@ -67,7 +72,6 @@ public final class ResXmlPatcher { } saveDocument(file, doc); - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { } } @@ -78,9 +82,6 @@ public final class ResXmlPatcher { * @param file AndroidManifest file */ public static void setApplicationDebugTagTrue(File file) { - if (!file.exists()) { - return; - } try { Document doc = loadDocument(file); Node application = doc.getElementsByTagName("application").item(0); @@ -98,7 +99,6 @@ public final class ResXmlPatcher { debugAttr.setNodeValue("true"); saveDocument(file, doc); - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { } } @@ -109,9 +109,6 @@ public final class ResXmlPatcher { * @param file AndroidManifest file */ public static void setNetworkSecurityConfig(File file) { - if (!file.exists()) { - return; - } try { Document doc = loadDocument(file); Node application = doc.getElementsByTagName("application").item(0); @@ -130,7 +127,6 @@ public final class ResXmlPatcher { netSecConfAttr.setNodeValue("@xml/network_security_config"); saveDocument(file, doc); - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { } } @@ -155,13 +151,13 @@ public final class ResXmlPatcher { } else { document = documentBuilder.newDocument(); } - + Element root = (Element) document.getElementsByTagName("network-security-config").item(0); if (root == null) { root = document.createElement("network-security-config"); document.appendChild(root); } - + Element baseConfig = (Element) document.getElementsByTagName("base-config").item(0); if (baseConfig == null) { baseConfig = document.createElement("base-config"); @@ -192,13 +188,13 @@ public final class ResXmlPatcher { certSystem.setAttribute("src", "system"); trustAnchors.appendChild(certSystem); } - + if (!hasUserCert) { Element certUser = document.createElement("certificates"); certUser.setAttribute("src", "user"); trustAnchors.appendChild(certUser); } - + saveDocument(file, document); } @@ -214,9 +210,6 @@ public final class ResXmlPatcher { * @param file File for AndroidManifest.xml */ public static void fixingPublicAttrsInProviderAttributes(File file) { - if (!file.exists()) { - return; - } try { Document doc = loadDocument(file); XPath xPath = XPathFactory.newInstance().newXPath(); @@ -257,7 +250,6 @@ public final class ResXmlPatcher { if (saved) { saveDocument(file, doc); } - } catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException | TransformerException ignored) { } @@ -285,16 +277,16 @@ public final class ResXmlPatcher { /** * Finds key in strings.xml file and returns text value * - * @param directory Root directory of apk + * @param apkDir Root directory of apk * @param key String reference (ie @string/foo) * @return String|null */ - public static String pullValueFromStrings(File directory, String key) { + public static String pullValueFromStrings(File apkDir, String key) { if (key == null || ! key.contains("@")) { return null; } - File file = new File(directory, "/res/values/strings.xml"); + File file = new File(apkDir, "/res/values/strings.xml"); key = key.replace("@string/", ""); if (!file.exists()) { @@ -307,7 +299,6 @@ public final class ResXmlPatcher { Object result = expression.evaluate(doc, XPathConstants.STRING); return result != null ? (String) result : null; - } catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) { return null; } @@ -316,16 +307,16 @@ public final class ResXmlPatcher { /** * Finds key in integers.xml file and returns text value * - * @param directory Root directory of apk + * @param apkDir Root directory of apk * @param key Integer reference (ie @integer/foo) * @return String|null */ - public static String pullValueFromIntegers(File directory, String key) { + public static String pullValueFromIntegers(File apkDir, String key) { if (key == null || ! key.contains("@")) { return null; } - File file = new File(directory, "/res/values/integers.xml"); + File file = new File(apkDir, "/res/values/integers.xml"); key = key.replace("@integer/", ""); if (!file.exists()) { @@ -350,9 +341,6 @@ public final class ResXmlPatcher { * @param file File representing AndroidManifest.xml */ public static void removeManifestVersions(File file) { - if (!file.exists()) { - return; - } try { Document doc = loadDocument(file); Node manifest = doc.getFirstChild(); @@ -390,7 +378,6 @@ public final class ResXmlPatcher { Node nodeAttr = attr.getNamedItem("package"); nodeAttr.setNodeValue(packageOriginal); saveDocument(file, doc); - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { } } @@ -401,9 +388,6 @@ public final class ResXmlPatcher { * @param file File for AndroidManifest.xml */ public static List pullManifestFeatureFlags(File file) { - if (!file.exists()) { - return null; - } try { Document doc = loadDocument(file); XPath xPath = XPathFactory.newInstance().newXPath(); @@ -425,7 +409,6 @@ public final class ResXmlPatcher { } return featureFlags; - } catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) { return null; } @@ -441,23 +424,22 @@ public final class ResXmlPatcher { */ public static Document loadDocument(File file) throws IOException, SAXException, ParserConfigurationException { - - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - docFactory.setFeature(FEATURE_DISABLE_DOCTYPE_DECL, true); - docFactory.setFeature(FEATURE_LOAD_DTD, false); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setFeature(FEATURE_DISABLE_DOCTYPE_DECL, true); + factory.setFeature(FEATURE_LOAD_DTD, false); try { - docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, " "); - docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, " "); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, " "); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, " "); } catch (IllegalArgumentException ex) { LOGGER.warning("JAXP 1.5 Support is required to validate XML"); } - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + DocumentBuilder builder = factory.newDocumentBuilder(); // Not using the parse(File) method on purpose, so that we can control when // to close it. Somehow parse(File) does not seem to close the file in all cases. - try (FileInputStream inputStream = new FileInputStream(file)) { - return docBuilder.parse(inputStream); + try (InputStream in = Files.newInputStream(file.toPath())) { + return builder.parse(in); } } @@ -472,7 +454,6 @@ public final class ResXmlPatcher { */ private static void saveDocument(File file, Document doc) throws IOException, SAXException, ParserConfigurationException, TransformerException { - TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); @@ -480,16 +461,11 @@ public final class ResXmlPatcher { byte[] xmlDecl = "".getBytes(StandardCharsets.US_ASCII); byte[] newLine = System.getProperty("line.separator").getBytes(StandardCharsets.US_ASCII); - try (OutputStream output = Files.newOutputStream(file.toPath())) { - output.write(xmlDecl); - output.write(newLine); - transformer.transform(new DOMSource(doc), new StreamResult(output)); - output.write(newLine); + try (OutputStream out = Files.newOutputStream(file.toPath())) { + out.write(xmlDecl); + out.write(newLine); + transformer.transform(new DOMSource(doc), new StreamResult(out)); + out.write(newLine); } } - - private static final String FEATURE_LOAD_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; - private static final String FEATURE_DISABLE_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl"; - - private static final Logger LOGGER = Logger.getLogger(ResXmlPatcher.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java index d7d7bb83..99c608fc 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java @@ -29,30 +29,27 @@ import java.nio.file.Files; import java.util.logging.Logger; public class SmaliBuilder { + private static final Logger LOGGER = Logger.getLogger(SmaliBuilder.class.getName()); - public static void build(File smaliDir, File dexFile, int apiLevel) throws AndrolibException { - new SmaliBuilder(smaliDir, dexFile, apiLevel).build(); - } + private final ExtFile mSmaliDir; + private final int mApiLevel; - private SmaliBuilder(File smaliDir, File dexFile, int apiLevel) { + public SmaliBuilder(File smaliDir, int apiLevel) { mSmaliDir = new ExtFile(smaliDir); - mDexFile = dexFile; mApiLevel = apiLevel; } - private void build() throws AndrolibException { + public void build(File dexFile) throws AndrolibException { try { - DexBuilder dexBuilder; - if (mApiLevel > 0) { - dexBuilder = new DexBuilder(Opcodes.forApi(mApiLevel)); - } else { - dexBuilder = new DexBuilder(Opcodes.getDefault()); - } + DexBuilder dexBuilder = mApiLevel > 0 + ? new DexBuilder(Opcodes.forApi(mApiLevel)) + : new DexBuilder(Opcodes.getDefault()); for (String fileName : mSmaliDir.getDirectory().getFiles(true)) { buildFile(fileName, dexBuilder); } - dexBuilder.writeTo(new FileDataStore(new File(mDexFile.getAbsolutePath()))); + + dexBuilder.writeTo(new FileDataStore(dexFile)); } catch (DirectoryException | IOException | RuntimeException ex) { throw new AndrolibException("Could not smali folder: " + mSmaliDir.getName(), ex); } @@ -82,10 +79,4 @@ public class SmaliBuilder { throw ex; } } - - private final ExtFile mSmaliDir; - private final File mDexFile; - private final int mApiLevel; - - private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java index b3a52a5d..b2da1d8b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java @@ -30,21 +30,19 @@ import com.android.tools.smali.dexlib2.iface.MultiDexContainer; import java.io.*; public class SmaliDecoder { + private final File mApkFile; + private final String mDexName; + private final boolean mBakDeb; + private final int mApiLevel; - public static DexFile decode(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) - throws AndrolibException { - return new SmaliDecoder(apkFile, outDir, dexName, bakDeb, apiLevel).decode(); - } - - private SmaliDecoder(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) { + public SmaliDecoder(File apkFile, String dexName, boolean bakDeb, int apiLevel) { mApkFile = apkFile; - mOutDir = outDir; mDexName = dexName; mBakDeb = bakDeb; mApiLevel = apiLevel; } - private DexFile decode() throws AndrolibException { + public DexFile decode(File outDir) throws AndrolibException { try { final BaksmaliOptions options = new BaksmaliOptions(); @@ -93,20 +91,14 @@ public class SmaliDecoder { if (dexFile instanceof DexBackedOdexFile) { options.inlineResolver = - InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile)dexFile).getOdexVersion()); + InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile) dexFile).getOdexVersion()); } - Baksmali.disassembleDexFile(dexFile, mOutDir, jobs, options); + Baksmali.disassembleDexFile(dexFile, outDir, jobs, options); return dexFile; } catch (IOException ex) { throw new AndrolibException("Could not baksmali file: " + mDexName, ex); } } - - private final File mApkFile; - private final File mOutDir; - private final String mDexName; - private final boolean mBakDeb; - private final int mApiLevel; } diff --git a/brut.apktool/apktool-lib/src/main/resources/properties/apktool.properties b/brut.apktool/apktool-lib/src/main/resources/apktool.properties similarity index 100% rename from brut.apktool/apktool-lib/src/main/resources/properties/apktool.properties rename to brut.apktool/apktool-lib/src/main/resources/apktool.properties diff --git a/brut.apktool/apktool-lib/src/main/resources/brut/androlib/android-framework.jar b/brut.apktool/apktool-lib/src/main/resources/prebuilt/android-framework.jar similarity index 100% rename from brut.apktool/apktool-lib/src/main/resources/brut/androlib/android-framework.jar rename to brut.apktool/apktool-lib/src/main/resources/prebuilt/android-framework.jar diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java index 027cc13c..049354f1 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java @@ -31,6 +31,7 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.*; +import java.nio.file.Files; import java.util.Map; import java.util.Set; import java.util.logging.Logger; @@ -39,6 +40,16 @@ import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.junit.Assert.*; public class BaseTest { + protected static final Logger LOGGER = Logger.getLogger(BaseTest.class.getName()); + + private static final String ACCESS_EXTERNAL_DTD = "http://javax.xml.XMLConstants/property/accessExternalDTD"; + private static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema"; + private static final String FEATURE_LOAD_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; + private static final String FEATURE_DISABLE_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl"; + + protected static ExtFile sTmpDir; + protected static ExtFile sTestOrigDir; + protected static ExtFile sTestNewDir; protected void compareBinaryFolder(String path, boolean res) throws BrutException, IOException { boolean exists = true; @@ -125,24 +136,24 @@ public class BaseTest { assertTrue(path + ": " + diff.getAllDifferences().toString(), diff.similar()); } - protected static Document loadDocument(File file) throws IOException, SAXException, ParserConfigurationException { - - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - docFactory.setFeature(FEATURE_DISABLE_DOCTYPE_DECL, true); - docFactory.setFeature(FEATURE_LOAD_DTD, false); + protected static Document loadDocument(File file) + throws IOException, SAXException, ParserConfigurationException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setFeature(FEATURE_DISABLE_DOCTYPE_DECL, true); + factory.setFeature(FEATURE_LOAD_DTD, false); try { - docFactory.setAttribute(ACCESS_EXTERNAL_DTD, " "); - docFactory.setAttribute(ACCESS_EXTERNAL_SCHEMA, " "); + factory.setAttribute(ACCESS_EXTERNAL_DTD, " "); + factory.setAttribute(ACCESS_EXTERNAL_SCHEMA, " "); } catch (IllegalArgumentException ex) { LOGGER.warning("JAXP 1.5 Support is required to validate XML"); } - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + DocumentBuilder builder = factory.newDocumentBuilder(); // Not using the parse(File) method on purpose, so that we can control when // to close it. Somehow parse(File) does not seem to close the file in all cases. - try (FileInputStream inputStream = new FileInputStream(file)) { - return docBuilder.parse(inputStream); + try (InputStream in = Files.newInputStream(file.toPath())) { + return builder.parse(in); } } @@ -164,21 +175,11 @@ public class BaseTest { NodeList children = element.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE && resourceNameContains((Element) child, name)) { + if (child.getNodeType() == Node.ELEMENT_NODE + && resourceNameContains((Element) child, name)) { return true; } } return false; } - - protected static ExtFile sTmpDir; - protected static ExtFile sTestOrigDir; - protected static ExtFile sTestNewDir; - - protected final static Logger LOGGER = Logger.getLogger(BaseTest.class.getName()); - - private static final String ACCESS_EXTERNAL_DTD = "http://javax.xml.XMLConstants/property/accessExternalDTD"; - private static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema"; - private static final String FEATURE_LOAD_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; - private static final String FEATURE_DISABLE_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl"; } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java index e59768de..cf94df09 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java @@ -18,9 +18,9 @@ package brut.androlib; import brut.androlib.exceptions.AndrolibException; import brut.androlib.res.Framework; -import brut.androlib.res.xml.ResXmlPatcher; +import brut.androlib.res.xml.ResXmlUtils; import brut.common.BrutException; -import brut.directory.DirUtil; +import brut.directory.DirUtils; import brut.directory.Directory; import brut.directory.FileDirectory; import brut.util.OS; @@ -42,7 +42,11 @@ import java.nio.file.Files; import java.util.HashMap; import java.util.Map; -public abstract class TestUtils { +public final class TestUtils { + + private TestUtils() { + // Private constructor for utility class + } public static Map parseStringsXml(File file) throws BrutException { try { @@ -68,28 +72,28 @@ public abstract class TestUtils { public static Document getDocumentFromFile(File file) throws BrutException { try { - return ResXmlPatcher.loadDocument(file); + return ResXmlUtils.loadDocument(file); } catch (IOException | SAXException | ParserConfigurationException ex) { throw new BrutException(ex); } } - public static void copyResourceDir(Class class_, String dirPath, File out) throws BrutException { + public static void copyResourceDir(Class clz, String dirPath, File out) throws BrutException { if (!out.exists()) { out.mkdirs(); } - copyResourceDir(class_, dirPath, new FileDirectory(out)); + copyResourceDir(clz, dirPath, new FileDirectory(out)); } - public static void copyResourceDir(Class class_, String dirPath, Directory out) throws BrutException { - if (class_ == null) { - class_ = Class.class; + public static void copyResourceDir(Class clz, String dirPath, Directory out) throws BrutException { + if (clz == null) { + clz = Class.class; } - URL dirURL = class_.getClassLoader().getResource(dirPath); + URL dirURL = clz.getClassLoader().getResource(dirPath); if (dirURL != null && dirURL.getProtocol().equals("file")) { try { - DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out); + DirUtils.copyToDir(new FileDirectory(dirURL.getFile()), out); } catch (UnsupportedEncodingException ex) { throw new BrutException(ex); } @@ -97,15 +101,15 @@ public abstract class TestUtils { } if (dirURL == null) { - String className = class_.getName().replace(".", "/") + ".class"; - dirURL = class_.getClassLoader().getResource(className); + String className = clz.getName().replace(".", "/") + ".class"; + dirURL = clz.getClassLoader().getResource(className); } if (dirURL.getProtocol().equals("jar")) { String jarPath; try { jarPath = URLDecoder.decode(dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")), "UTF-8"); - DirUtil.copyToDir(new FileDirectory(jarPath), out); + DirUtils.copyToDir(new FileDirectory(jarPath), out); } catch (UnsupportedEncodingException ex) { throw new BrutException(ex); } @@ -122,11 +126,12 @@ public abstract class TestUtils { public static byte[] readHeaderOfFile(File file, int size) throws IOException { byte[] buffer = new byte[size]; - InputStream inputStream = Files.newInputStream(file.toPath()); - if (inputStream.read(buffer) != buffer.length) { - throw new IOException("File size too small for buffer length: " + size); + + try (InputStream in = Files.newInputStream(file.toPath())) { + if (in.read(buffer) != buffer.length) { + throw new IOException("File size too small for buffer length: " + size); + } } - inputStream.close(); return buffer; } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java index 275d338e..25f6d4cb 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java @@ -30,6 +30,7 @@ import java.io.File; import static org.junit.Assert.assertTrue; public class AndroidOreoNotSparseTest extends BaseTest { + @BeforeClass public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java index a8d4cad5..07fe8c0a 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java @@ -30,6 +30,7 @@ import java.io.File; import static org.junit.Assert.assertTrue; public class AndroidOreoSparseTest extends BaseTest { + @BeforeClass public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java index 4a46b79f..a8f5ddb3 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java @@ -33,6 +33,12 @@ import java.util.logging.Logger; import static org.junit.Assert.assertTrue; public class EmptyResourcesArscTest { + private static final Logger LOGGER = Logger.getLogger(EmptyResourcesArscTest.class.getName()); + + private static ExtFile sTmpDir; + private static ExtFile sTestOrigDir; + private static ExtFile sTestNewDir; + @BeforeClass public static void beforeClass() throws Exception { TestUtils.cleanFrameworkFile(); @@ -64,10 +70,4 @@ public class EmptyResourcesArscTest { assertTrue(sTestNewDir.isDirectory()); assertTrue(sTestOrigDir.isDirectory()); } - - private static ExtFile sTmpDir; - private static ExtFile sTestOrigDir; - private static ExtFile sTestNewDir; - - private final static Logger LOGGER = Logger.getLogger(EmptyResourcesArscTest.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NetworkConfigTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NetworkConfigTest.java index 0c4a25ed..f483d279 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NetworkConfigTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NetworkConfigTest.java @@ -37,7 +37,6 @@ import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; - import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; @@ -88,20 +87,19 @@ public class NetworkConfigTest extends BaseTest { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); - Document document = builder.parse(new ByteArrayInputStream(obtained.getBytes())); + Document doc = builder.parse(new ByteArrayInputStream(obtained.getBytes())); // XPath expression to check for user and system certificates - XPathFactory xPathFactory = XPathFactory.newInstance(); - XPath xpath = xPathFactory.newXPath(); - + XPath xPath = XPathFactory.newInstance().newXPath(); + // Check if 'system' certificate exists - XPathExpression systemCertExpr = xpath.compile("//certificates[@src='system']"); - NodeList systemCertNodes = (NodeList) systemCertExpr.evaluate(document, XPathConstants.NODESET); + XPathExpression systemCertExpr = xPath.compile("//certificates[@src='system']"); + NodeList systemCertNodes = (NodeList) systemCertExpr.evaluate(doc, XPathConstants.NODESET); assertTrue(systemCertNodes.getLength() > 0); // Check if 'user' certificate exists - XPathExpression userCertExpr = xpath.compile("//certificates[@src='user']"); - NodeList userCertNodes = (NodeList) userCertExpr.evaluate(document, XPathConstants.NODESET); + XPathExpression userCertExpr = xPath.compile("//certificates[@src='user']"); + NodeList userCertNodes = (NodeList) userCertExpr.evaluate(doc, XPathConstants.NODESET); assertTrue(userCertNodes.getLength() > 0); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NonStandardPkgIdTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NonStandardPkgIdTest.java index 4158f51b..dfef5c36 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NonStandardPkgIdTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NonStandardPkgIdTest.java @@ -31,6 +31,7 @@ import org.junit.Test; import static org.junit.Assert.*; public class NonStandardPkgIdTest extends BaseTest { + private static ResTable mResTable; @BeforeClass public static void beforeClass() throws Exception { @@ -90,6 +91,4 @@ public class NonStandardPkgIdTest extends BaseTest { assertEquals(0x80, mResTable.getResSpec(0x80020001).getPackage().getId()); assertEquals(0x80, mResTable.getResSpec(0x80030000).getPackage().getId()); } - - private static ResTable mResTable; } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoReaderTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoReaderTest.java index 3dda5b1c..f63b007a 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoReaderTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoReaderTest.java @@ -40,7 +40,7 @@ public class ApkInfoReaderTest { assertNotNull(apkInfo.usesFramework); assertNotNull(apkInfo.usesFramework.ids); assertEquals(1, apkInfo.usesFramework.ids.size()); - assertEquals(1, (long)apkInfo.usesFramework.ids.get(0)); + assertEquals(1, (long) apkInfo.usesFramework.ids.get(0)); assertNull(apkInfo.usesFramework.tag); assertNotNull(apkInfo.versionInfo); assertNull(apkInfo.versionInfo.versionCode); @@ -50,7 +50,7 @@ public class ApkInfoReaderTest { @Test public void testStandard() throws AndrolibException { ApkInfo apkInfo = ApkInfo.load( - this.getClass().getResourceAsStream("/apk/standard.yml")); + getClass().getResourceAsStream("/apk/standard.yml")); checkStandard(apkInfo); assertEquals("2.8.1", apkInfo.version); } @@ -58,7 +58,7 @@ public class ApkInfoReaderTest { @Test public void testUnknownFields() throws AndrolibException { ApkInfo apkInfo = ApkInfo.load( - this.getClass().getResourceAsStream("/apk/unknown_fields.yml")); + getClass().getResourceAsStream("/apk/unknown_fields.yml")); checkStandard(apkInfo); assertEquals("2.8.1", apkInfo.version); } @@ -66,7 +66,7 @@ public class ApkInfoReaderTest { @Test public void testSkipIncorrectIndent() throws AndrolibException { ApkInfo apkInfo = ApkInfo.load( - this.getClass().getResourceAsStream("/apk/skip_incorrect_indent.yml")); + getClass().getResourceAsStream("/apk/skip_incorrect_indent.yml")); checkStandard(apkInfo); assertNotEquals("2.0.0", apkInfo.version); } @@ -74,7 +74,7 @@ public class ApkInfoReaderTest { @Test public void testFirstIncorrectIndent() throws AndrolibException { ApkInfo apkInfo = ApkInfo.load( - this.getClass().getResourceAsStream("/apk/first_incorrect_indent.yml")); + getClass().getResourceAsStream("/apk/first_incorrect_indent.yml")); checkStandard(apkInfo); assertNotEquals("2.0.0", apkInfo.version); } @@ -82,13 +82,13 @@ public class ApkInfoReaderTest { @Test public void testUnknownFiles() throws AndrolibException { ApkInfo apkInfo = ApkInfo.load( - this.getClass().getResourceAsStream("/apk/unknown_files.yml")); + getClass().getResourceAsStream("/apk/unknown_files.yml")); assertEquals("2.0.0", apkInfo.version); assertEquals("testapp.apk", apkInfo.apkFileName); assertFalse(apkInfo.isFrameworkApk); assertNotNull(apkInfo.usesFramework); assertEquals(1, apkInfo.usesFramework.ids.size()); - assertEquals(1, (long)apkInfo.usesFramework.ids.get(0)); + assertEquals(1, (long) apkInfo.usesFramework.ids.get(0)); assertNotNull(apkInfo.packageInfo); assertEquals("127", apkInfo.packageInfo.forcedPackageId); assertNotNull(apkInfo.versionInfo); @@ -106,13 +106,13 @@ public class ApkInfoReaderTest { @Test public void testUlist_with_indent() throws AndrolibException { ApkInfo apkInfo = ApkInfo.load( - this.getClass().getResourceAsStream("/apk/list_with_indent.yml")); + getClass().getResourceAsStream("/apk/list_with_indent.yml")); assertEquals("2.8.0", apkInfo.version); assertEquals("basic.apk", apkInfo.apkFileName); assertFalse(apkInfo.isFrameworkApk); assertNotNull(apkInfo.usesFramework); assertEquals(1, apkInfo.usesFramework.ids.size()); - assertEquals(1, (long)apkInfo.usesFramework.ids.get(0)); + assertEquals(1, (long) apkInfo.usesFramework.ids.get(0)); assertEquals("tag", apkInfo.usesFramework.tag); assertNotNull(apkInfo.packageInfo); assertEquals("127", apkInfo.packageInfo.forcedPackageId); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoSerializationTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoSerializationTest.java index fc15a908..7e9275ad 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoSerializationTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoSerializationTest.java @@ -23,25 +23,24 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.*; +import java.nio.file.Files; import static org.junit.Assert.*; public class ApkInfoSerializationTest { - @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void checkApkInfoSerialization() throws IOException, AndrolibException { ApkInfo control = ApkInfo.load( - this.getClass().getResourceAsStream("/apk/unknown_files.yml")); + getClass().getResourceAsStream("/apk/unknown_files.yml")); check(control); File savedApkInfo = folder.newFile("saved.yml"); control.save(savedApkInfo); - try (FileInputStream fis = new FileInputStream(savedApkInfo)) { - ApkInfo saved = ApkInfo.load(fis); - check(saved); + try (InputStream in = Files.newInputStream(savedApkInfo.toPath())) { + check(ApkInfo.load(in)); } } @@ -51,7 +50,7 @@ public class ApkInfoSerializationTest { assertFalse(apkInfo.isFrameworkApk); assertNotNull(apkInfo.usesFramework); assertEquals(1, apkInfo.usesFramework.ids.size()); - assertEquals(1, (long)apkInfo.usesFramework.ids.get(0)); + assertEquals(1, (long) apkInfo.usesFramework.ids.get(0)); assertNotNull(apkInfo.packageInfo); assertEquals("127", apkInfo.packageInfo.forcedPackageId); assertNotNull(apkInfo.versionInfo); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ConsistentPropertyTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ConsistentPropertyTest.java index 8bd82fab..2ff2f3c3 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ConsistentPropertyTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ConsistentPropertyTest.java @@ -26,7 +26,7 @@ public class ConsistentPropertyTest { @Test public void testAssertingAllKnownApkInfoProperties() throws AndrolibException { ApkInfo apkInfo = ApkInfo.load( - this.getClass().getResourceAsStream("/apk/basic.yml")); + getClass().getResourceAsStream("/apk/basic.yml")); assertEquals("2.8.0", apkInfo.version); assertEquals("basic.apk", apkInfo.apkFileName); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/DoNotCompressHieroglyphTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/DoNotCompressHieroglyphTest.java index 9c9fbd3c..f3ae48bc 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/DoNotCompressHieroglyphTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/DoNotCompressHieroglyphTest.java @@ -26,7 +26,7 @@ public class DoNotCompressHieroglyphTest { @Test public void testHieroglyph() throws AndrolibException { ApkInfo apkInfo = ApkInfo.load( - this.getClass().getResourceAsStream("/apk/donotcompress_with_hieroglyph.yml")); + getClass().getResourceAsStream("/apk/donotcompress_with_hieroglyph.yml")); assertEquals("2.0.0", apkInfo.version); assertEquals("testapp.apk", apkInfo.apkFileName); assertEquals(2, apkInfo.doNotCompress.size()); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/MaliciousYamlTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/MaliciousYamlTest.java index 66a53c6a..df6fc9a2 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/MaliciousYamlTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/MaliciousYamlTest.java @@ -26,7 +26,7 @@ public class MaliciousYamlTest { @Test public void testMaliciousYaml() throws AndrolibException { ApkInfo apkInfo = ApkInfo.load( - this.getClass().getResourceAsStream("/apk/cve20220476.yml")); + getClass().getResourceAsStream("/apk/cve20220476.yml")); assertEquals("2.6.1-ddc4bb-SNAPSHOT", apkInfo.version); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ForceManifestDecodeNoResourcesTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ForceManifestDecodeNoResourcesTest.java index da1acfed..19a8cb19 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ForceManifestDecodeNoResourcesTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ForceManifestDecodeNoResourcesTest.java @@ -34,8 +34,7 @@ import java.util.Arrays; import static org.junit.Assert.*; public class ForceManifestDecodeNoResourcesTest extends BaseTest { - - private final byte[] xmlHeader = new byte[] { + private static final byte[] XML_HEADER = { 0x3C, // < 0x3F, // ? 0x78, // x @@ -68,7 +67,7 @@ public class ForceManifestDecodeNoResourcesTest extends BaseTest { // let's probe filetype of manifest, we should detect XML File manifestFile = new File(output + File.separator + "AndroidManifest.xml"); byte[] magic = TestUtils.readHeaderOfFile(manifestFile, 6); - assertArrayEquals(this.xmlHeader, magic); + assertArrayEquals(XML_HEADER, magic); // confirm resources.arsc still exists, as its raw File resourcesArsc = new File(output + File.separator + "resources.arsc"); @@ -87,7 +86,7 @@ public class ForceManifestDecodeNoResourcesTest extends BaseTest { // let's probe filetype of manifest, we should detect XML File manifestFile = new File(output + File.separator + "AndroidManifest.xml"); byte[] magic = TestUtils.readHeaderOfFile(manifestFile, 6); - assertArrayEquals(this.xmlHeader, magic); + assertArrayEquals(XML_HEADER, magic); // confirm resources.arsc does not exist File resourcesArsc = new File(output + File.separator + "resources.arsc"); @@ -106,7 +105,7 @@ public class ForceManifestDecodeNoResourcesTest extends BaseTest { // lets probe filetype of manifest, we should detect XML File manifestFile = new File(output + File.separator + "AndroidManifest.xml"); byte[] magic = TestUtils.readHeaderOfFile(manifestFile, 6); - assertArrayEquals(this.xmlHeader, magic); + assertArrayEquals(XML_HEADER, magic); // confirm resources.arsc does not exist File resourcesArsc = new File(output + File.separator + "resources.arsc"); @@ -125,7 +124,7 @@ public class ForceManifestDecodeNoResourcesTest extends BaseTest { // lets probe filetype of manifest, we should not detect XML File manifestFile = new File(output + File.separator + "AndroidManifest.xml"); byte[] magic = TestUtils.readHeaderOfFile(manifestFile, 6); - assertFalse(Arrays.equals(this.xmlHeader, magic)); + assertFalse(Arrays.equals(XML_HEADER, magic)); // confirm resources.arsc exists File resourcesArsc = new File(output + File.separator + "resources.arsc"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MissingDiv9PatchTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MissingDiv9PatchTest.java index 3f493c77..4cb1a5b0 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MissingDiv9PatchTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MissingDiv9PatchTest.java @@ -29,10 +29,12 @@ import org.junit.Test; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; +import java.nio.file.Files; import static org.junit.Assert.*; public class MissingDiv9PatchTest extends BaseTest { + private static final int NP_COLOR = 0xff000000; @BeforeClass public static void beforeClass() throws Exception { @@ -48,26 +50,22 @@ public class MissingDiv9PatchTest extends BaseTest { @Test public void assertMissingDivAdded() throws Exception { - InputStream inputStream = getFileInputStream(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + File file = new File(sTmpDir, "pip_dismiss_scrim.9.png"); + byte[] data; - Res9patchStreamDecoder decoder = new Res9patchStreamDecoder(); - decoder.decode(inputStream, outputStream); + try (InputStream in = Files.newInputStream(file.toPath())) { + Res9patchStreamDecoder decoder = new Res9patchStreamDecoder(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + decoder.decode(in, out); + data = out.toByteArray(); + } - BufferedImage image = ImageIO.read(new ByteArrayInputStream(outputStream.toByteArray())); + BufferedImage image = ImageIO.read(new ByteArrayInputStream(data)); int height = image.getHeight() - 1; // First and last pixel will be invisible, so lets check the first column and ensure its all black for (int y = 1; y < height; y++) { assertEquals("y coordinate failed at: " + y, NP_COLOR, image.getRGB(0, y)); } - } - - private FileInputStream getFileInputStream() throws IOException { - File file = new File(sTmpDir, "pip_dismiss_scrim.9.png"); - return new FileInputStream(file.toPath().toString()); - } - - private static final int NP_COLOR = 0xff000000; } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/res/decoder/StringBlockWithSurrogatePairInUtf8Test.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/res/decoder/StringBlockWithSurrogatePairInUtf8Test.java index 004399e2..634db3c9 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/res/decoder/StringBlockWithSurrogatePairInUtf8Test.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/res/decoder/StringBlockWithSurrogatePairInUtf8Test.java @@ -25,48 +25,55 @@ import static org.junit.Assert.assertEquals; public class StringBlockWithSurrogatePairInUtf8Test { @Test public void decodeSingleOctet() { - final String actual = new StringBlock("abcDEF123".getBytes(StandardCharsets.UTF_8), true).decodeString(0, 9); + final byte[] bytes = "abcDEF123".getBytes(StandardCharsets.UTF_8); + final String actual = new StringBlock(bytes, true).decodeString(0, 9); assertEquals("Incorrect decoding", "abcDEF123", actual); } @Test public void decodeTwoOctets() { - final String actual0 = new StringBlock(new byte[] {(byte) 0xC2, (byte) 0x80}, true).decodeString(0, 2); + final byte[] bytes0 = { (byte) 0xC2, (byte) 0x80 }; + final String actual0 = new StringBlock(bytes0, true).decodeString(0, 2); assertEquals("Incorrect decoding", "\u0080", actual0); - final String actual1 = new StringBlock(new byte[] {(byte) 0xDF, (byte) 0xBF}, true).decodeString(0, 2); + final byte[] bytes1 = { (byte) 0xDF, (byte) 0xBF }; + final String actual1 = new StringBlock(bytes1, true).decodeString(0, 2); assertEquals("Incorrect decoding", "\u07FF", actual1); } @Test public void decodeThreeOctets() { - final String actual0 = new StringBlock(new byte[] {(byte) 0xE0, (byte) 0xA0, (byte) 0x80}, true).decodeString(0, 3); + final byte[] bytes0 = { (byte) 0xE0, (byte) 0xA0, (byte) 0x80 }; + final String actual0 = new StringBlock(bytes0, true).decodeString(0, 3); assertEquals("Incorrect decoding", "\u0800", actual0); - final String actual1 = new StringBlock(new byte[] {(byte) 0xEF, (byte) 0xBF, (byte) 0xBF}, true).decodeString(0, 3); + final byte[] bytes1 = { (byte) 0xEF, (byte) 0xBF, (byte) 0xBF }; + final String actual1 = new StringBlock(bytes1, true).decodeString(0, 3); assertEquals("Incorrect decoding", "\uFFFF", actual1); } @Test public void decodeSurrogatePair_when_givesAsThreeOctetsFromInvalidRangeOfUtf8() { // See: https://github.com/iBotPeaches/Apktool/issues/2299 - final String actual = new StringBlock(new byte[] {(byte) 0xED, (byte) 0xA0, (byte) 0xBD, (byte) 0xED, (byte) 0xB4, (byte) 0x86}, true).decodeString(0, 6); - assertEquals("Incorrect decoding", "\uD83D\uDD06", actual); + final byte[] bytes0 = { (byte) 0xED, (byte) 0xA0, (byte) 0xBD, (byte) 0xED, (byte) 0xB4, (byte) 0x86 }; + final String actual0 = new StringBlock(bytes0, true).decodeString(0, 6); + assertEquals("Incorrect decoding", "\uD83D\uDD06", actual0); // See: https://github.com/iBotPeaches/Apktool/issues/2546 - final byte[] bytesWithCharactersBeforeSurrogatePair = {'G', 'o', 'o', 'd', ' ', 'm', 'o', 'r', 'n', 'i', 'n', 'g', '!', ' ', + // Bytes with characters before surrogate pair + final byte[] bytes1 = { + 'G', 'o', 'o', 'd', ' ', 'm', 'o', 'r', 'n', 'i', 'n', 'g', '!', ' ', (byte) 0xED, (byte) 0xA0, (byte) 0xBD, (byte) 0xED, (byte) 0xB1, (byte) 0x8B, ' ', 'S', 'u', 'n', ' ', (byte) 0xED, (byte) 0xA0, (byte) 0xBC, (byte) 0xED, (byte) 0xBC, (byte) 0x9E }; - final String actual2 = new StringBlock(bytesWithCharactersBeforeSurrogatePair, true).decodeString(0, 31); - + final String actual1 = new StringBlock(bytes1, true).decodeString(0, 31); // D83D -> 0xED 0xA0 0xBD // DC4B -> 0xED 0xB1 0x8B // D83C -> 0xED 0xA0 0xBC // DF1E -> 0xED 0xBC 0x9E assertEquals("Incorrect decoding when there are valid characters before the surrogate pair", - "Good morning! \uD83D\uDC4B Sun \uD83C\uDF1E", actual2); + "Good morning! \uD83D\uDC4B Sun \uD83C\uDF1E", actual1); } @Test @@ -74,7 +81,8 @@ public class StringBlockWithSurrogatePairInUtf8Test { // \u10FFFF is encoded in UTF-8 as "0xDBFF 0xDFFF" (4-byte encoding), // but when used in Android resources which are encoded in UTF-8, 3-byte encoding is used, // so each of these is encoded as 3-bytes - final String actual = new StringBlock(new byte[] {(byte) 0xED, (byte) 0xAF, (byte) 0xBF, (byte) 0xED, (byte) 0xBF, (byte) 0xBF}, true).decodeString(0, 6); + final byte[] bytes = { (byte) 0xED, (byte) 0xAF, (byte) 0xBF, (byte) 0xED, (byte) 0xBF, (byte) 0xBF }; + final String actual = new StringBlock(bytes, true).decodeString(0, 6); assertEquals("Incorrect decoding", "\uDBFF\uDFFF", actual); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/util/AaptVersionTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/util/AaptVersionTest.java index 675a62c7..41b3b6a8 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/util/AaptVersionTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/util/AaptVersionTest.java @@ -26,9 +26,9 @@ public class AaptVersionTest { @Test public void testAapt2Iterations() throws BrutException { - assertEquals(2, AaptManager.getAppVersionFromString("Android Asset Packaging Tool (aapt) 2:17")); - assertEquals(2, AaptManager.getAppVersionFromString("Android Asset Packaging Tool (aapt) 2.17")); - assertEquals(1, AaptManager.getAppVersionFromString("Android Asset Packaging Tool, v0.9")); - assertEquals(1, AaptManager.getAppVersionFromString("Android Asset Packaging Tool, v0.2-2679779")); + assertEquals(2, AaptManager.getAaptVersionFromString("Android Asset Packaging Tool (aapt) 2:17")); + assertEquals(2, AaptManager.getAaptVersionFromString("Android Asset Packaging Tool (aapt) 2.17")); + assertEquals(1, AaptManager.getAaptVersionFromString("Android Asset Packaging Tool, v0.9")); + assertEquals(1, AaptManager.getAaptVersionFromString("Android Asset Packaging Tool, v0.2-2679779")); } } diff --git a/brut.j.common/src/main/java/brut/common/BrutException.java b/brut.j.common/src/main/java/brut/common/BrutException.java index b85c7165..b5ed4479 100644 --- a/brut.j.common/src/main/java/brut/common/BrutException.java +++ b/brut.j.common/src/main/java/brut/common/BrutException.java @@ -17,6 +17,15 @@ package brut.common; public class BrutException extends Exception { + + public BrutException() { + super(); + } + + public BrutException(String message) { + super(message); + } + public BrutException(Throwable cause) { super(cause); } @@ -24,11 +33,4 @@ public class BrutException extends Exception { public BrutException(String message, Throwable cause) { super(message, cause); } - - public BrutException(String message) { - super(message); - } - - public BrutException() { - } } diff --git a/brut.j.common/src/main/java/brut/common/InvalidUnknownFileException.java b/brut.j.common/src/main/java/brut/common/InvalidUnknownFileException.java index 195cd9a5..a5853ce3 100644 --- a/brut.j.common/src/main/java/brut/common/InvalidUnknownFileException.java +++ b/brut.j.common/src/main/java/brut/common/InvalidUnknownFileException.java @@ -17,6 +17,7 @@ package brut.common; public class InvalidUnknownFileException extends BrutException { + public InvalidUnknownFileException(String message) { super(message); } diff --git a/brut.j.common/src/main/java/brut/common/RootUnknownFileException.java b/brut.j.common/src/main/java/brut/common/RootUnknownFileException.java index 1457268e..d372795a 100644 --- a/brut.j.common/src/main/java/brut/common/RootUnknownFileException.java +++ b/brut.j.common/src/main/java/brut/common/RootUnknownFileException.java @@ -17,6 +17,7 @@ package brut.common; public class RootUnknownFileException extends BrutException { + public RootUnknownFileException(String message) { super(message); } diff --git a/brut.j.common/src/main/java/brut/common/TraversalUnknownFileException.java b/brut.j.common/src/main/java/brut/common/TraversalUnknownFileException.java index a58116e1..8956ab01 100644 --- a/brut.j.common/src/main/java/brut/common/TraversalUnknownFileException.java +++ b/brut.j.common/src/main/java/brut/common/TraversalUnknownFileException.java @@ -17,6 +17,7 @@ package brut.common; public class TraversalUnknownFileException extends BrutException { + public TraversalUnknownFileException(String message) { super(message); } diff --git a/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java index a39aa69b..acf2dbaa 100644 --- a/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java @@ -180,31 +180,31 @@ public abstract class AbstractDirectory implements Directory { } public void copyToDir(Directory out) throws DirectoryException { - DirUtil.copyToDir(out, out); + DirUtils.copyToDir(out, out); } public void copyToDir(Directory out, String[] fileNames) throws DirectoryException { - DirUtil.copyToDir(out, out, fileNames); + DirUtils.copyToDir(out, out, fileNames); } public void copyToDir(Directory out, String fileName) throws DirectoryException { - DirUtil.copyToDir(out, out, fileName); + DirUtils.copyToDir(out, out, fileName); } public void copyToDir(File out) throws DirectoryException { - DirUtil.copyToDir(this, out); + DirUtils.copyToDir(this, out); } public void copyToDir(File out, String[] fileNames) throws DirectoryException { - DirUtil.copyToDir(this, out, fileNames); + DirUtils.copyToDir(this, out, fileNames); } public void copyToDir(File out, String fileName) throws DirectoryException { - DirUtil.copyToDir(this, out, fileName); + DirUtils.copyToDir(this, out, fileName); } public int getCompressionLevel(String fileName) @@ -269,6 +269,7 @@ public abstract class AbstractDirectory implements Directory { private static class ParsedPath { public final String dir; public final String subpath; + public ParsedPath(String dir, String subpath) { this.dir = dir; this.subpath = subpath; diff --git a/brut.j.dir/src/main/java/brut/directory/DirUtil.java b/brut.j.dir/src/main/java/brut/directory/DirUtils.java similarity index 98% rename from brut.j.dir/src/main/java/brut/directory/DirUtil.java rename to brut.j.dir/src/main/java/brut/directory/DirUtils.java index d2a361c2..bdc36d8d 100644 --- a/brut.j.dir/src/main/java/brut/directory/DirUtil.java +++ b/brut.j.dir/src/main/java/brut/directory/DirUtils.java @@ -28,10 +28,10 @@ import java.nio.file.FileSystemException; import java.nio.file.Files; import java.util.logging.Logger; -public class DirUtil { +public final class DirUtils { private static final Logger LOGGER = Logger.getLogger(""); - private DirUtil() { + private DirUtils() { // Private constructor for utility class } diff --git a/brut.j.dir/src/main/java/brut/directory/Directory.java b/brut.j.dir/src/main/java/brut/directory/Directory.java index c5ece158..26a358d8 100644 --- a/brut.j.dir/src/main/java/brut/directory/Directory.java +++ b/brut.j.dir/src/main/java/brut/directory/Directory.java @@ -24,6 +24,8 @@ import java.util.Map; import java.util.Set; public interface Directory { + char separator = '/'; + Set getFiles(); Set getFiles(boolean recursive); @@ -65,6 +67,4 @@ public interface Directory { int getCompressionLevel(String fileName) throws DirectoryException; void close() throws IOException; - - char separator = '/'; } diff --git a/brut.j.dir/src/main/java/brut/directory/DirectoryException.java b/brut.j.dir/src/main/java/brut/directory/DirectoryException.java index 86db4992..3b735593 100644 --- a/brut.j.dir/src/main/java/brut/directory/DirectoryException.java +++ b/brut.j.dir/src/main/java/brut/directory/DirectoryException.java @@ -21,19 +21,19 @@ import brut.common.BrutException; public class DirectoryException extends BrutException { private static final long serialVersionUID = -8871963042836625387L; - public DirectoryException(String detailMessage, Throwable throwable) { - super(detailMessage, throwable); - } - - public DirectoryException(String detailMessage) { - super(detailMessage); - } - - public DirectoryException(Throwable throwable) { - super(throwable); - } - public DirectoryException() { super(); } + + public DirectoryException(String message) { + super(message); + } + + public DirectoryException(Throwable cause) { + super(cause); + } + + public DirectoryException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/brut.j.dir/src/main/java/brut/directory/ExtFile.java b/brut.j.dir/src/main/java/brut/directory/ExtFile.java index 55728487..9f3c253b 100644 --- a/brut.j.dir/src/main/java/brut/directory/ExtFile.java +++ b/brut.j.dir/src/main/java/brut/directory/ExtFile.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.net.URI; public class ExtFile extends File { + private Directory mDirectory; + public ExtFile(File file) { super(file.getPath()); } @@ -66,6 +68,4 @@ public class ExtFile extends File { return super.delete(); } - - private Directory mDirectory; } diff --git a/brut.j.dir/src/main/java/brut/directory/FileDirectory.java b/brut.j.dir/src/main/java/brut/directory/FileDirectory.java index 38dede74..f6815b29 100644 --- a/brut.j.dir/src/main/java/brut/directory/FileDirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/FileDirectory.java @@ -36,7 +36,6 @@ public class FileDirectory extends AbstractDirectory { } public FileDirectory(File dir) throws DirectoryException { - super(); if (!dir.isDirectory()) { throw new DirectoryException("file must be a directory: " + dir); } diff --git a/brut.j.dir/src/main/java/brut/directory/PathAlreadyExists.java b/brut.j.dir/src/main/java/brut/directory/PathAlreadyExists.java index 57e7d102..48ceafc8 100644 --- a/brut.j.dir/src/main/java/brut/directory/PathAlreadyExists.java +++ b/brut.j.dir/src/main/java/brut/directory/PathAlreadyExists.java @@ -17,20 +17,21 @@ package brut.directory; public class PathAlreadyExists extends DirectoryException { - public PathAlreadyExists() { - } - - public PathAlreadyExists(Throwable throwable) { - super(throwable); - } - - public PathAlreadyExists(String detailMessage) { - super(detailMessage); - } - - public PathAlreadyExists(String detailMessage, Throwable throwable) { - super(detailMessage, throwable); - } - private static final long serialVersionUID = 3776428251424428904L; + + public PathAlreadyExists() { + super(); + } + + public PathAlreadyExists(String message) { + super(message); + } + + public PathAlreadyExists(Throwable cause) { + super(cause); + } + + public PathAlreadyExists(String message, Throwable cause) { + super(message, cause); + } } diff --git a/brut.j.dir/src/main/java/brut/directory/PathNotExist.java b/brut.j.dir/src/main/java/brut/directory/PathNotExist.java index f31c8ab2..e4607e5d 100644 --- a/brut.j.dir/src/main/java/brut/directory/PathNotExist.java +++ b/brut.j.dir/src/main/java/brut/directory/PathNotExist.java @@ -17,21 +17,21 @@ package brut.directory; public class PathNotExist extends DirectoryException { + private static final long serialVersionUID = -6949242015506342032L; + public PathNotExist() { super(); } - public PathNotExist(String detailMessage, Throwable throwable) { - super(detailMessage, throwable); + public PathNotExist(String message) { + super(message); } - public PathNotExist(String detailMessage) { - super(detailMessage); + public PathNotExist(Throwable cause) { + super(cause); } - public PathNotExist(Throwable throwable) { - super(throwable); + public PathNotExist(String message, Throwable cause) { + super(message, cause); } - - private static final long serialVersionUID = -6949242015506342032L; } diff --git a/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java b/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java index 4eb8bc76..44426771 100644 --- a/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java @@ -48,7 +48,6 @@ public class ZipRODirectory extends AbstractDirectory { } public ZipRODirectory(File zipFile, String path) throws DirectoryException { - super(); try { mZipFile = new ZipFile(zipFile); } catch (IOException ex) { @@ -58,7 +57,6 @@ public class ZipRODirectory extends AbstractDirectory { } public ZipRODirectory(ZipFile zipFile, String path) { - super(); mZipFile = zipFile; mPath = path; } diff --git a/brut.j.dir/src/main/java/brut/directory/ZipUtils.java b/brut.j.dir/src/main/java/brut/directory/ZipUtils.java index d632e3fb..7772f60d 100644 --- a/brut.j.dir/src/main/java/brut/directory/ZipUtils.java +++ b/brut.j.dir/src/main/java/brut/directory/ZipUtils.java @@ -33,7 +33,7 @@ import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -public class ZipUtils { +public final class ZipUtils { private static final Logger LOGGER = Logger.getLogger(""); private ZipUtils() { @@ -95,8 +95,8 @@ public class ZipUtils { if (doNotCompress.test(entryName)) { zipEntry.setMethod(ZipEntry.STORED); zipEntry.setSize(file.length()); - try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath()))) { - CRC32 crc = BrutIO.calculateCrc(bis); + try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(file.toPath()))) { + CRC32 crc = BrutIO.calculateCrc(in); zipEntry.setCrc(crc.getValue()); } } else { diff --git a/brut.j.util/src/main/java/brut/util/AaptManager.java b/brut.j.util/src/main/java/brut/util/AaptManager.java index e62d33ea..8ba66b9f 100644 --- a/brut.j.util/src/main/java/brut/util/AaptManager.java +++ b/brut.j.util/src/main/java/brut/util/AaptManager.java @@ -17,27 +17,31 @@ package brut.util; import brut.common.BrutException; + import java.io.File; import java.util.ArrayList; import java.util.List; -public class AaptManager { - public static final int AAPT_VERSION_MIN = 1; - public static final int AAPT_VERSION_MAX = 2; +public final class AaptManager { - public static File getAapt2() throws BrutException { - return getAapt(2); + private AaptManager() { + // Private constructor for utility class } - public static File getAapt1() throws BrutException { - return getAapt(1); + public static String getAaptName(int version) { + switch (version) { + case 2: + return "aapt2"; + default: + return "aapt"; + } } - private static File getAapt(int version) throws BrutException { - String aaptName = getAaptBinaryName(version); + public static File getAaptBinary(int version) throws BrutException { + String aaptName = getAaptName(version); if (!OSDetection.is64Bit() && OSDetection.isMacOSX()) { - throw new BrutException(aaptName + " binaries are not available for 32-bit platform: " + OSDetection.returnOS()); + throw new BrutException(aaptName + " binary is not available for 32-bit platform: " + OSDetection.returnOS()); } StringBuilder aaptPath = new StringBuilder("/prebuilt/"); @@ -59,72 +63,44 @@ public class AaptManager { aaptPath.append(".exe"); } - File aaptBinary = Jar.getResourceAsFile(aaptPath.toString(), AaptManager.class); - if (!aaptBinary.setExecutable(true)) { - throw new BrutException("Can't set aapt binary as executable"); - } - + File aaptBinary = Jar.getResourceAsFile(AaptManager.class, aaptPath.toString()); + setAaptBinaryExecutable(aaptBinary); return aaptBinary; } - public static String getAaptBinaryName(int version) { - switch (version) { - case 2: - return "aapt2"; - default: - return "aapt"; - } - } - - public static int getAaptVersion(String aaptPath) throws BrutException { - return getAaptVersion(new File(aaptPath)); - } - public static int getAaptVersion(File aaptBinary) throws BrutException { - if (!aaptBinary.isFile() || !aaptBinary.canRead()) { - throw new BrutException("Can't read aapt binary: " + aaptBinary.getAbsolutePath()); - } - if (!aaptBinary.setExecutable(true)) { - throw new BrutException("Can't set aapt binary as executable: " + aaptBinary.getAbsolutePath()); - } + setAaptBinaryExecutable(aaptBinary); List cmd = new ArrayList<>(); - cmd.add(aaptBinary.getAbsolutePath()); + cmd.add(aaptBinary.getPath()); cmd.add("version"); - String version = OS.execAndReturn(cmd.toArray(new String[0])); - if (version == null) { - throw new BrutException("Could not execute aapt binary at location: " + aaptBinary.getAbsolutePath()); + String versionStr = OS.execAndReturn(cmd.toArray(new String[0])); + if (versionStr == null) { + throw new BrutException("Could not execute aapt binary at location: " + aaptBinary.getPath()); } - return getAppVersionFromString(version); + return getAaptVersionFromString(versionStr); } - public static int getAppVersionFromString(String version) throws BrutException { - if (version.startsWith("Android Asset Packaging Tool (aapt) 2:")) { + public static int getAaptVersionFromString(String versionStr) throws BrutException { + if (versionStr.startsWith("Android Asset Packaging Tool (aapt) 2:")) { return 2; - } else if (version.startsWith("Android Asset Packaging Tool (aapt) 2.")) { + } else if (versionStr.startsWith("Android Asset Packaging Tool (aapt) 2.")) { return 2; // Prior to Android SDK 26.0.2 - } else if (version.startsWith("Android Asset Packaging Tool, v0.")) { + } else if (versionStr.startsWith("Android Asset Packaging Tool, v0.")) { return 1; } - throw new BrutException("aapt version could not be identified: " + version); + throw new BrutException("aapt version could not be identified: " + versionStr); } - public static String getAaptExecutionCommand(String aaptPath, File aaptBinary) throws BrutException { - if (aaptPath.isEmpty()) { - return aaptBinary.getAbsolutePath(); - } - - aaptBinary = new File(aaptPath); + private static void setAaptBinaryExecutable(File aaptBinary) throws BrutException { if (!aaptBinary.isFile() || !aaptBinary.canRead()) { - throw new BrutException("Can't read aapt binary: " + aaptBinary.getAbsolutePath()); + throw new BrutException("Could not read aapt binary: " + aaptBinary.getPath()); } if (!aaptBinary.setExecutable(true)) { - throw new BrutException("Can't set aapt binary as executable: " + aaptBinary.getAbsolutePath()); + throw new BrutException("Could not set aapt binary as executable: " + aaptBinary.getPath()); } - - return aaptBinary.getPath(); } } diff --git a/brut.j.util/src/main/java/brut/util/BrutIO.java b/brut.j.util/src/main/java/brut/util/BrutIO.java index c6d33537..9d81e680 100644 --- a/brut.j.util/src/main/java/brut/util/BrutIO.java +++ b/brut.j.util/src/main/java/brut/util/BrutIO.java @@ -25,7 +25,12 @@ import org.apache.commons.io.IOUtils; import java.io.*; import java.util.zip.CRC32; -public class BrutIO { +public final class BrutIO { + + private BrutIO() { + // Private constructor for utility class + } + public static byte[] readAndClose(InputStream in) throws IOException { try { return IOUtils.toByteArray(in); diff --git a/brut.j.util/src/main/java/brut/util/DataInputDelegate.java b/brut.j.util/src/main/java/brut/util/DataInputDelegate.java deleted file mode 100644 index 0d5021d8..00000000 --- a/brut.j.util/src/main/java/brut/util/DataInputDelegate.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2010 Ryszard Wiśniewski - * Copyright (C) 2010 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 - * - * https://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.util; - -import java.io.DataInput; -import java.io.IOException; - -public abstract class DataInputDelegate implements DataInput { - protected final DataInput mDelegate; - - public DataInputDelegate(DataInput delegate) { - this.mDelegate = delegate; - } - - public int skipBytes(int n) throws IOException { - return mDelegate.skipBytes(n); - } - - public int readUnsignedShort() throws IOException { - return mDelegate.readUnsignedShort(); - } - - public int readUnsignedByte() throws IOException { - return mDelegate.readUnsignedByte(); - } - - public String readUTF() throws IOException { - return mDelegate.readUTF(); - } - - public short readShort() throws IOException { - return mDelegate.readShort(); - } - - public long readLong() throws IOException { - return mDelegate.readLong(); - } - - public String readLine() throws IOException { - return mDelegate.readLine(); - } - - public int readInt() throws IOException { - return mDelegate.readInt(); - } - - public void readFully(byte[] b, int off, int len) throws IOException { - mDelegate.readFully(b, off, len); - } - - public void readFully(byte[] b) throws IOException { - mDelegate.readFully(b); - } - - public float readFloat() throws IOException { - return mDelegate.readFloat(); - } - - public double readDouble() throws IOException { - return mDelegate.readDouble(); - } - - public char readChar() throws IOException { - return mDelegate.readChar(); - } - - public byte readByte() throws IOException { - return mDelegate.readByte(); - } - - public boolean readBoolean() throws IOException { - return mDelegate.readBoolean(); - } -} diff --git a/brut.j.util/src/main/java/brut/util/Duo.java b/brut.j.util/src/main/java/brut/util/Duo.java index 60aedacd..0b4654be 100644 --- a/brut.j.util/src/main/java/brut/util/Duo.java +++ b/brut.j.util/src/main/java/brut/util/Duo.java @@ -23,11 +23,10 @@ public class Duo { public final T2 m2; public Duo(T1 t1, T2 t2) { - this.m1 = t1; - this.m2 = t2; + m1 = t1; + m2 = t2; } - @SuppressWarnings("unchecked") @Override public boolean equals(Object obj) { if (obj == null) { @@ -37,17 +36,17 @@ public class Duo { return false; } final Duo other = (Duo) obj; - if (!Objects.equals(this.m1, other.m1)) { + if (!Objects.equals(m1, other.m1)) { return false; } - return Objects.equals(this.m2, other.m2); + return Objects.equals(m2, other.m2); } @Override public int hashCode() { int hash = 3; - hash = 71 * hash + (this.m1 != null ? this.m1.hashCode() : 0); - hash = 71 * hash + (this.m2 != null ? this.m2.hashCode() : 0); + hash = 71 * hash + (m1 != null ? m1.hashCode() : 0); + hash = 71 * hash + (m2 != null ? m2.hashCode() : 0); return hash; } } diff --git a/brut.j.util/src/main/java/brut/util/ExtCountingDataInput.java b/brut.j.util/src/main/java/brut/util/ExtCountingDataInput.java deleted file mode 100644 index cbfb7048..00000000 --- a/brut.j.util/src/main/java/brut/util/ExtCountingDataInput.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2010 Ryszard Wiśniewski - * Copyright (C) 2010 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 - * - * https://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.util; - -import org.apache.commons.io.input.CountingInputStream; -import com.google.common.io.LittleEndianDataInputStream; - -import java.io.DataInput; -import java.io.IOException; -import java.util.logging.Logger; - -public class ExtCountingDataInput extends ExtDataInput { - private static final Logger LOGGER = Logger.getLogger(ExtCountingDataInput.class.getName()); - - private final CountingInputStream mCountIn; - - public ExtCountingDataInput(LittleEndianDataInputStream in) { - this(new CountingInputStream(in)); - } - - public ExtCountingDataInput(CountingInputStream countIn) { - // We need to explicitly cast to DataInput as otherwise the constructor is ambiguous. - // We choose DataInput instead of InputStream as ExtDataInput wraps an InputStream in - // a DataInputStream which is big-endian and ignores the little-endian behavior. - super((DataInput) new LittleEndianDataInputStream(countIn)); - mCountIn = countIn; - } - - public int position() { - return mCountIn.getCount(); - } - - public int remaining() throws IOException { - return mCountIn.available(); - } - - public long skip(int bytes) throws IOException { - return mCountIn.skip(bytes); - } - - public int[] readSafeIntArray(int length, long maxPosition) throws IOException { - int[] array = new int[length]; - - for (int i = 0; i < length; i++) { - // #3236 - In some applications we have more strings than fit into the block. This function takes - // an expected max position and if we are past it, we return early during processing. - if (position() >= maxPosition) { - LOGGER.warning(String.format("Bad string block: string entry is at %d, past end at %d", - position(), maxPosition) - ); - return array; - } - - array[i] = readInt(); - } - return array; - } -} diff --git a/brut.j.util/src/main/java/brut/util/ExtDataInput.java b/brut.j.util/src/main/java/brut/util/ExtDataInput.java index d7c852d4..c857bc36 100644 --- a/brut.j.util/src/main/java/brut/util/ExtDataInput.java +++ b/brut.j.util/src/main/java/brut/util/ExtDataInput.java @@ -16,76 +16,23 @@ */ package brut.util; -import java.io.*; +import java.io.DataInput; +import java.io.IOException; -public class ExtDataInput extends DataInputDelegate { - public ExtDataInput(InputStream in) { - this((DataInput) new DataInputStream(in)); - } +public interface ExtDataInput extends DataInput { + public long position(); - public ExtDataInput(DataInput delegate) { - super(delegate); - } + public void skipShort() throws IOException; - public int[] readIntArray(int length) throws IOException { - int[] array = new int[length]; - for (int i = 0; i < length; i++) { - array[i] = readInt(); - } - return array; - } + public void skipInt() throws IOException; - public void skipInt() throws IOException { - skipBytes(4); - } + public void skipCheckShort(short expected) throws IOException; - public void skipShort() throws IOException { - skipBytes(2); - } + public void skipCheckByte(byte expected) throws IOException; - public void skipCheckShort(short expected) throws IOException { - short got = readShort(); - if (got != expected) { - throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got)); - } - } + public int[] readIntArray(int len) throws IOException; - public void skipCheckByte(byte expected) throws IOException { - byte got = readByte(); - if (got != expected) { - throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got)); - } - } + public int[] readSafeIntArray(int len, long maxPosition) throws IOException; - /** - * The general contract of DataInput doesn't guarantee all the bytes requested will be skipped - * and failure can occur for many reasons. We override this to try harder to skip all the bytes - * requested (this is similar to DataInputStream's wrapper). - */ - public final int skipBytes(int n) throws IOException { - int total = 0; - int cur; - - while ((total < n) && ((cur = super.skipBytes(n - total)) > 0)) { - total += cur; - } - - return total; - } - - public String readNullEndedString(int length, boolean fixed) throws IOException { - StringBuilder string = new StringBuilder(16); - while (length-- != 0) { - short ch = readShort(); - if (ch == 0) { - break; - } - string.append((char) ch); - } - if (fixed) { - skipBytes(length * 2); - } - - return string.toString(); - } + public String readNullEndedString(int len, boolean fixed) throws IOException; } diff --git a/brut.j.util/src/main/java/brut/util/ExtDataInputStream.java b/brut.j.util/src/main/java/brut/util/ExtDataInputStream.java new file mode 100644 index 00000000..872c78d8 --- /dev/null +++ b/brut.j.util/src/main/java/brut/util/ExtDataInputStream.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2010 Ryszard Wiśniewski + * Copyright (C) 2010 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 + * + * https://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.util; + +import com.google.common.io.CountingInputStream; +import com.google.common.io.LittleEndianDataInputStream; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.FilterInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.logging.Logger; + +public class ExtDataInputStream extends FilterInputStream implements ExtDataInput { + private static final Logger LOGGER = Logger.getLogger(ExtDataInputStream.class.getName()); + + private final DataInput mDelegate; + private final CountingInputStream mCountIn; + + public static ExtDataInputStream bigEndian(InputStream in) { + CountingInputStream countIn = new CountingInputStream(in); + DataInput delegate = new DataInputStream(countIn); + return new ExtDataInputStream(delegate, countIn); + } + + public static ExtDataInputStream littleEndian(InputStream in) { + CountingInputStream countIn = new CountingInputStream(in); + DataInput delegate = new LittleEndianDataInputStream(countIn); + return new ExtDataInputStream(delegate, countIn); + } + + private ExtDataInputStream(DataInput delegate, CountingInputStream countIn) { + super((InputStream) delegate); + mDelegate = delegate; + mCountIn = countIn; + } + + // ExtDataInput + + @Override + public long position() { + return mCountIn.getCount(); + } + + @Override + public void skipShort() throws IOException { + //noinspection ResultOfMethodCallIgnored + readShort(); + } + + @Override + public void skipInt() throws IOException { + //noinspection ResultOfMethodCallIgnored + readInt(); + } + + @Override + public void skipCheckShort(short expected) throws IOException { + short got = readShort(); + if (got != expected) { + throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got)); + } + } + + @Override + public void skipCheckByte(byte expected) throws IOException { + byte got = readByte(); + if (got != expected) { + throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got)); + } + } + + @Override + public int[] readIntArray(int len) throws IOException { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = readInt(); + } + return arr; + } + + @Override + public int[] readSafeIntArray(int len, long maxPosition) throws IOException { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + // #3236 - In some applications we have more strings than fit into the block. This function takes + // an expected max position and if we are past it, we return early during processing. + if (position() >= maxPosition) { + LOGGER.warning(String.format("Bad string block: string entry is at %d, past end at %d", + position(), maxPosition) + ); + return arr; + } + + arr[i] = readInt(); + } + return arr; + } + + @Override + public String readNullEndedString(int len, boolean fixed) throws IOException { + StringBuilder str = new StringBuilder(16); + while (len-- != 0) { + short ch = readShort(); + if (ch == 0) { + break; + } + str.append((char) ch); + } + if (fixed) { + skipBytes(len * 2); + } + return str.toString(); + } + + // DataInput + + @Override + public void readFully(byte[] b) throws IOException { + mDelegate.readFully(b); + } + + @Override + public void readFully(byte[] b, int off, int len) throws IOException { + mDelegate.readFully(b, off, len); + } + + /** + * The general contract of DataInput doesn't guarantee all the bytes requested will be skipped + * and failure can occur for many reasons. We override this to try harder to skip all the bytes + * requested (this is similar to DataInputStream's wrapper). + */ + @Override + public int skipBytes(int n) throws IOException { + int total = 0; + int cur; + while (total < n && (cur = mDelegate.skipBytes(n - total)) > 0) { + total += cur; + } + return total; + } + + @Override + public boolean readBoolean() throws IOException { + return mDelegate.readBoolean(); + } + + @Override + public byte readByte() throws IOException { + return mDelegate.readByte(); + } + + @Override + public int readUnsignedByte() throws IOException { + return mDelegate.readUnsignedByte(); + } + + @Override + public short readShort() throws IOException { + return mDelegate.readShort(); + } + + @Override + public int readUnsignedShort() throws IOException { + return mDelegate.readUnsignedShort(); + } + + @Override + public char readChar() throws IOException { + return mDelegate.readChar(); + } + + @Override + public int readInt() throws IOException { + return mDelegate.readInt(); + } + + @Override + public long readLong() throws IOException { + return mDelegate.readLong(); + } + + @Override + public float readFloat() throws IOException { + return mDelegate.readFloat(); + } + + @Override + public double readDouble() throws IOException { + return mDelegate.readDouble(); + } + + @Override + public String readLine() throws IOException { + return mDelegate.readLine(); + } + + @Override + public String readUTF() throws IOException { + return mDelegate.readUTF(); + } +} diff --git a/brut.j.util/src/main/java/brut/util/Jar.java b/brut.j.util/src/main/java/brut/util/Jar.java index 1473dae9..ad00a912 100644 --- a/brut.j.util/src/main/java/brut/util/Jar.java +++ b/brut.j.util/src/main/java/brut/util/Jar.java @@ -26,31 +26,35 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; -public abstract class Jar { - private static final Map mExtracted = new HashMap<>(); +public final class Jar { + private static final Map sExtracted = new HashMap<>(); - public static File getResourceAsFile(String name, Class clazz) throws BrutException { - File file = mExtracted.get(name); + private Jar() { + // Private constructor for utility class + } + + public static File getResourceAsFile(Class clz, String name) throws BrutException { + File file = sExtracted.get(name); if (file == null) { - file = extractToTmp(name, clazz); - mExtracted.put(name, file); + file = extractToTmp(clz, name); + sExtracted.put(name, file); } return file; } - public static File extractToTmp(String resourcePath, Class clazz) throws BrutException { - return extractToTmp(resourcePath, "brut_util_Jar_", clazz); + public static File extractToTmp(Class clz, String resourcePath) throws BrutException { + return extractToTmp(clz, resourcePath, "brut_util_Jar_"); } - public static File extractToTmp(String resourcePath, String tmpPrefix, Class clazz) throws BrutException { + public static File extractToTmp(Class clz, String resourcePath, String tmpPrefix) throws BrutException { InputStream in = null; try { - in = clazz.getResourceAsStream(resourcePath); + in = clz.getResourceAsStream(resourcePath); if (in == null) { throw new FileNotFoundException(resourcePath); } long suffix = ThreadLocalRandom.current().nextLong(); - suffix = suffix == Long.MIN_VALUE ? 0 : Math.abs(suffix); + suffix = suffix > Long.MIN_VALUE ? Math.abs(suffix) : 0; File fileOut = File.createTempFile(tmpPrefix, suffix + ".tmp"); fileOut.deleteOnExit(); diff --git a/brut.j.util/src/main/java/brut/util/OS.java b/brut.j.util/src/main/java/brut/util/OS.java index b0f1e57d..49d294a0 100644 --- a/brut.j.util/src/main/java/brut/util/OS.java +++ b/brut.j.util/src/main/java/brut/util/OS.java @@ -27,9 +27,13 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; -public class OS { +public final class OS { private static final Logger LOGGER = Logger.getLogger(""); + private OS() { + // Private constructor for utility class + } + public static void rmdir(File dir) throws BrutException { if (! dir.exists()) { return; @@ -142,19 +146,20 @@ public class OS { } } - static class StreamForwarder extends Thread { + private static class StreamForwarder extends Thread { + private final InputStream mIn; + private final String mType; - StreamForwarder(InputStream in, String type) { + public StreamForwarder(InputStream in, String type) { mIn = in; mType = type; } @Override public void run() { - try { - BufferedReader br = new BufferedReader(new InputStreamReader(mIn)); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(mIn))) { String line; - while ((line = br.readLine()) != null) { + while ((line = reader.readLine()) != null) { if (mType.equals("OUTPUT")) { LOGGER.info(line); } else { @@ -165,32 +170,29 @@ public class OS { ex.printStackTrace(); } } - - private final InputStream mIn; - private final String mType; } - static class StreamCollector implements Runnable { - private final StringBuilder buffer = new StringBuilder(); - private final InputStream inputStream; + private static class StreamCollector implements Runnable { + private final InputStream mIn; + private final StringBuilder mBuffer; - public StreamCollector(InputStream inputStream) { - super(); - this.inputStream = inputStream; + public StreamCollector(InputStream in) { + mIn = in; + mBuffer = new StringBuilder(); } @Override public void run() { - String line; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(mIn))) { + String line; while ((line = reader.readLine()) != null) { - buffer.append(line).append('\n'); + mBuffer.append(line).append('\n'); } } catch (IOException ignored) {} } public String get() { - return buffer.toString(); + return mBuffer.toString(); } } } diff --git a/brut.j.util/src/main/java/brut/util/OSDetection.java b/brut.j.util/src/main/java/brut/util/OSDetection.java index 63a5d5ea..1c27b616 100644 --- a/brut.j.util/src/main/java/brut/util/OSDetection.java +++ b/brut.j.util/src/main/java/brut/util/OSDetection.java @@ -16,10 +16,14 @@ */ package brut.util; -public class OSDetection { +public final class OSDetection { private static final String OS = System.getProperty("os.name").toLowerCase(); private static final String BIT = System.getProperty("sun.arch.data.model").toLowerCase(); + private OSDetection() { + // Private constructor for utility class + } + public static boolean isWindows() { return (OS.contains("win")); } diff --git a/brut.j.xml/src/main/java/brut/xmlpull/XmlPullUtils.java b/brut.j.xml/src/main/java/brut/xmlpull/XmlPullUtils.java index 921cf6d0..47069147 100644 --- a/brut.j.xml/src/main/java/brut/xmlpull/XmlPullUtils.java +++ b/brut.j.xml/src/main/java/brut/xmlpull/XmlPullUtils.java @@ -22,7 +22,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.*; -public class XmlPullUtils { +public final class XmlPullUtils { private static final String PROPERTY_XMLDECL_STANDALONE = "http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone"; @@ -30,6 +30,10 @@ public class XmlPullUtils { boolean onEvent(XmlPullParser in, XmlSerializer out) throws XmlPullParserException; } + private XmlPullUtils() { + // Private constructor for utility class + } + public static void copy(XmlPullParser in, XmlSerializer out) throws XmlPullParserException, IOException { copy(in, out, null);