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);