refactor: tweaks IO handling (#3723)

Use BrutIO where possible to improve and simplify stream handling.
Ensure streams are closed when no longer needed.

Some minor formatting tweaks and naming consistency.

No functionality changes.
This commit is contained in:
Igor Eisberg 2024-11-10 15:56:47 +02:00 committed by GitHub
parent 880a9587ee
commit c2eab3101c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 93 additions and 101 deletions

View File

@ -50,7 +50,7 @@ public class AaptInvoker {
} }
} }
public void invokeAapt(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include) public void invoke(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include)
throws AndrolibException { throws AndrolibException {
String aaptPath = mConfig.aaptPath; String aaptPath = mConfig.aaptPath;

View File

@ -35,7 +35,6 @@ import brut.util.AaptManager;
import brut.util.BrutIO; import brut.util.BrutIO;
import brut.util.OS; import brut.util.OS;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@ -377,7 +376,7 @@ public class ApkBuilder {
try { try {
AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo); AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo);
invoker.invokeAapt(tmpFile, manifest, resDir, ninePatch, null, getIncludeFiles()); invoker.invoke(tmpFile, manifest, resDir, ninePatch, null, getIncludeFiles());
Directory tmpDir = tmpFile.getDirectory(); Directory tmpDir = tmpFile.getDirectory();
tmpDir.copyToDir(outDir, "AndroidManifest.xml"); tmpDir.copyToDir(outDir, "AndroidManifest.xml");
@ -417,7 +416,7 @@ public class ApkBuilder {
try { try {
AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo); AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo);
invoker.invokeAapt(tmpFile, manifest, null, ninePatch, null, getIncludeFiles()); invoker.invoke(tmpFile, manifest, null, ninePatch, null, getIncludeFiles());
Directory tmpDir = tmpFile.getDirectory(); Directory tmpDir = tmpFile.getDirectory();
tmpDir.copyToDir(outDir, "AndroidManifest.xml"); tmpDir.copyToDir(outDir, "AndroidManifest.xml");

View File

@ -21,7 +21,6 @@ import brut.androlib.exceptions.AndrolibException;
import brut.androlib.res.data.ResConfigFlags; import brut.androlib.res.data.ResConfigFlags;
import brut.directory.DirectoryException; import brut.directory.DirectoryException;
import brut.directory.ExtFile; import brut.directory.ExtFile;
import brut.directory.FileDirectory;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
@ -212,10 +211,10 @@ public class ApkInfo implements YamlSerializable {
return apkInfo; return apkInfo;
} }
public static ApkInfo load(File appDir) throws AndrolibException { public static ApkInfo load(ExtFile apkDir) throws AndrolibException {
try (InputStream in = new FileDirectory(appDir).getFileInput("apktool.yml")) { try (InputStream in = apkDir.getDirectory().getFileInput("apktool.yml")) {
ApkInfo apkInfo = ApkInfo.load(in); ApkInfo apkInfo = ApkInfo.load(in);
apkInfo.setApkFile(new ExtFile(appDir)); apkInfo.setApkFile(apkDir);
return apkInfo; return apkInfo;
} catch (DirectoryException | IOException ex) { } catch (DirectoryException | IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);

View File

@ -22,8 +22,8 @@ import brut.androlib.exceptions.CantFindFrameworkResException;
import brut.androlib.res.decoder.ARSCDecoder; import brut.androlib.res.decoder.ARSCDecoder;
import brut.androlib.res.data.arsc.ARSCData; import brut.androlib.res.data.arsc.ARSCData;
import brut.androlib.res.data.arsc.FlagsOffset; import brut.androlib.res.data.arsc.FlagsOffset;
import brut.util.BrutIO;
import brut.util.Jar; import brut.util.Jar;
import org.apache.commons.io.IOUtils;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
@ -50,8 +50,6 @@ public class Framework {
} }
public void installFramework(File frameFile, String tag) throws AndrolibException { public void installFramework(File frameFile, String tag) throws AndrolibException {
InputStream in = null;
ZipOutputStream out = null;
try (ZipFile zip = new ZipFile(frameFile)) { try (ZipFile zip = new ZipFile(frameFile)) {
ZipEntry entry = zip.getEntry("resources.arsc"); ZipEntry entry = zip.getEntry("resources.arsc");
@ -59,9 +57,7 @@ public class Framework {
throw new AndrolibException("Can't find resources.arsc file"); throw new AndrolibException("Can't find resources.arsc file");
} }
in = zip.getInputStream(entry); byte[] data = BrutIO.readAndClose(zip.getInputStream(entry));
byte[] data = IOUtils.toByteArray(in);
ARSCData arsc = ARSCDecoder.decode(new ByteArrayInputStream(data), true, true); ARSCData arsc = ARSCDecoder.decode(new ByteArrayInputStream(data), true, true);
publicizeResources(data, arsc.getFlagsOffsets()); publicizeResources(data, arsc.getFlagsOffsets());
@ -70,7 +66,7 @@ public class Framework {
+ (tag == null ? "" : '-' + tag) + (tag == null ? "" : '-' + tag)
+ ".apk"); + ".apk");
out = new ZipOutputStream(Files.newOutputStream(outFile.toPath())); try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(outFile.toPath()))) {
out.setMethod(ZipOutputStream.STORED); out.setMethod(ZipOutputStream.STORED);
CRC32 crc = new CRC32(); CRC32 crc = new CRC32();
crc.update(data); crc.update(data);
@ -82,11 +78,10 @@ public class Framework {
out.write(data); out.write(data);
out.closeEntry(); out.closeEntry();
//Write fake AndroidManifest.xml file to support original aapt // write fake AndroidManifest.xml file to support original aapt
entry = zip.getEntry("AndroidManifest.xml"); entry = zip.getEntry("AndroidManifest.xml");
if (entry != null) { if (entry != null) {
in = zip.getInputStream(entry); byte[] manifest = BrutIO.readAndClose(zip.getInputStream(entry));
byte[] manifest = IOUtils.toByteArray(in);
CRC32 manifestCrc = new CRC32(); CRC32 manifestCrc = new CRC32();
manifestCrc.update(manifest); manifestCrc.update(manifest);
entry.setSize(manifest.length); entry.setSize(manifest.length);
@ -96,13 +91,11 @@ public class Framework {
out.write(manifest); out.write(manifest);
out.closeEntry(); out.closeEntry();
} }
}
LOGGER.info("Framework installed to: " + outFile); LOGGER.info("Framework installed to: " + outFile);
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
} }
} }
@ -212,15 +205,12 @@ public class Framework {
} }
if (id == 1) { if (id == 1) {
try ( try {
InputStream in = getAndroidFrameworkResourcesAsStream(); BrutIO.copyAndClose(getAndroidFrameworkResourcesAsStream(), Files.newOutputStream(apk.toPath()));
OutputStream out = Files.newOutputStream(apk.toPath())
) {
IOUtils.copy(in, out);
return apk;
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
return apk;
} }
throw new CantFindFrameworkResException(id); throw new CantFindFrameworkResException(id);

View File

@ -25,9 +25,8 @@ import brut.androlib.res.xml.ResValuesXmlSerializable;
import brut.androlib.res.xml.ResXmlPatcher; import brut.androlib.res.xml.ResXmlPatcher;
import brut.directory.Directory; import brut.directory.Directory;
import brut.directory.DirectoryException; import brut.directory.DirectoryException;
import brut.directory.FileDirectory; import brut.directory.ExtFile;
import brut.xmlpull.MXSerializer; import brut.xmlpull.MXSerializer;
import org.apache.commons.io.IOUtils;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
import java.io.*; import java.io.*;
@ -77,27 +76,25 @@ public class ResourcesDecoder {
XmlSerializer xmlSerializer = newXmlSerializer(); XmlSerializer xmlSerializer = newXmlSerializer();
ResStreamDecoder fileDecoder = new AndroidManifestPullStreamDecoder(axmlParser, xmlSerializer); ResStreamDecoder fileDecoder = new AndroidManifestPullStreamDecoder(axmlParser, xmlSerializer);
Directory inApk, out; Directory in, out;
InputStream inputStream = null;
OutputStream outputStream = null;
try { try {
inApk = mApkInfo.getApkFile().getDirectory(); in = mApkInfo.getApkFile().getDirectory();
out = new FileDirectory(outDir); out = new ExtFile(outDir).getDirectory();
if (mApkInfo.hasResources()) { if (mApkInfo.hasResources()) {
LOGGER.info("Decoding AndroidManifest.xml with resources..."); LOGGER.info("Decoding AndroidManifest.xml with resources...");
} else { } else {
LOGGER.info("Decoding AndroidManifest.xml with only framework resources..."); LOGGER.info("Decoding AndroidManifest.xml with only framework resources...");
} }
inputStream = inApk.getFileInput("AndroidManifest.xml");
outputStream = out.getFileOutput("AndroidManifest.xml");
fileDecoder.decode(inputStream, outputStream);
} catch (DirectoryException ex) { try (
InputStream is = in.getFileInput("AndroidManifest.xml");
OutputStream os = out.getFileOutput("AndroidManifest.xml")
) {
fileDecoder.decode(is, os);
}
} catch (DirectoryException | IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} finally {
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
} }
File manifest = new File(outDir, "AndroidManifest.xml"); File manifest = new File(outDir, "AndroidManifest.xml");
@ -171,25 +168,24 @@ public class ResourcesDecoder {
Directory in, out, outRes; Directory in, out, outRes;
try { try {
out = new FileDirectory(outDir);
in = mApkInfo.getApkFile().getDirectory(); in = mApkInfo.getApkFile().getDirectory();
outRes = out.createDir("res"); out = new ExtFile(outDir).getDirectory().createDir("res");
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
for (ResPackage pkg : mResTable.listMainPackages()) { for (ResPackage pkg : mResTable.listMainPackages()) {
LOGGER.info("Decoding file-resources..."); LOGGER.info("Decoding file-resources...");
for (ResResource res : pkg.listFiles()) { for (ResResource res : pkg.listFiles()) {
fileDecoder.decode(res, in, outRes, mResFileMapping); fileDecoder.decode(res, in, out, mResFileMapping);
} }
LOGGER.info("Decoding values */* XMLs..."); LOGGER.info("Decoding values */* XMLs...");
for (ResValuesFile valuesFile : pkg.listValuesFiles()) { for (ResValuesFile valuesFile : pkg.listValuesFiles()) {
generateValuesFile(valuesFile, outRes, xmlSerializer); generateValuesFile(valuesFile, out, xmlSerializer);
} }
generatePublicXml(pkg, outRes, xmlSerializer);
generatePublicXml(pkg, out, xmlSerializer);
} }
AndrolibException decodeError = axmlParser.getFirstError(); AndrolibException decodeError = axmlParser.getFirstError();

View File

@ -22,9 +22,9 @@ import brut.androlib.exceptions.RawXmlEncounteredException;
import brut.androlib.res.data.ResResource; import brut.androlib.res.data.ResResource;
import brut.androlib.res.data.value.ResBoolValue; import brut.androlib.res.data.value.ResBoolValue;
import brut.androlib.res.data.value.ResFileValue; import brut.androlib.res.data.value.ResFileValue;
import brut.directory.DirUtil;
import brut.directory.Directory; import brut.directory.Directory;
import brut.directory.DirectoryException; import brut.directory.DirectoryException;
import brut.directory.DirUtil;
import brut.util.BrutIO; import brut.util.BrutIO;
import java.io.*; import java.io.*;

View File

@ -23,8 +23,7 @@ import java.io.*;
public class ResRawStreamDecoder implements ResStreamDecoder { public class ResRawStreamDecoder implements ResStreamDecoder {
@Override @Override
public void decode(InputStream in, OutputStream out) public void decode(InputStream in, OutputStream out) throws AndrolibException {
throws AndrolibException {
try { try {
IOUtils.copy(in, out); IOUtils.copy(in, out);
} catch (IOException ex) { } catch (IOException ex) {

View File

@ -52,16 +52,16 @@ public class DirUtil {
copyToDir(in, out, fileName, fileName); copyToDir(in, out, fileName, fileName);
} }
public static void copyToDir(Directory in, Directory out, String inFile, String outFile) public static void copyToDir(Directory in, Directory out, String inFileName, String outFileName)
throws DirectoryException { throws DirectoryException {
try { try {
if (in.containsDir(inFile)) { if (in.containsDir(inFileName)) {
in.getDir(inFile).copyToDir(out.createDir(outFile)); in.getDir(inFileName).copyToDir(out.createDir(outFileName));
} else { } else {
BrutIO.copyAndClose(in.getFileInput(inFile), out.getFileOutput(outFile)); BrutIO.copyAndClose(in.getFileInput(inFileName), out.getFileOutput(outFileName));
} }
} catch (IOException ex) { } catch (IOException ex) {
throw new DirectoryException("Error copying file: " + inFile, ex); throw new DirectoryException("Error copying file: " + inFileName, ex);
} }
} }
@ -81,28 +81,33 @@ public class DirUtil {
public static void copyToDir(Directory in, File out, String fileName) public static void copyToDir(Directory in, File out, String fileName)
throws DirectoryException { throws DirectoryException {
copyToDir(in, out, fileName, fileName);
}
public static void copyToDir(Directory in, File out, String inFileName, String outFileName)
throws DirectoryException {
try { try {
if (in.containsDir(fileName)) { if (in.containsDir(inFileName)) {
File outDir = new File(out, fileName); File outDir = new File(out, outFileName);
OS.rmdir(outDir); OS.rmdir(outDir);
in.getDir(fileName).copyToDir(outDir); in.getDir(inFileName).copyToDir(outDir);
} else if (in.containsFile(fileName)) { } else if (in.containsFile(inFileName)) {
String validFileName = BrutIO.sanitizePath(out, fileName); outFileName = BrutIO.sanitizePath(out, outFileName);
if (!validFileName.isEmpty()) { if (!outFileName.isEmpty()) {
File outFile = new File(out, validFileName); File outFile = new File(out, outFileName);
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
outFile.getParentFile().mkdirs(); outFile.getParentFile().mkdirs();
BrutIO.copyAndClose(in.getFileInput(fileName), Files.newOutputStream(outFile.toPath())); BrutIO.copyAndClose(in.getFileInput(inFileName), Files.newOutputStream(outFile.toPath()));
} }
} else { } else {
// Skip if directory/file not found // Skip if directory/file not found
} }
} catch (FileSystemException ex) { } catch (FileSystemException ex) {
LOGGER.warning(String.format("Skipping file %s (%s)", fileName, ex.getReason())); LOGGER.warning(String.format("Skipping file %s (%s)", inFileName, ex.getReason()));
} catch (RootUnknownFileException | InvalidUnknownFileException | TraversalUnknownFileException | IOException ex) { } catch (RootUnknownFileException | InvalidUnknownFileException | TraversalUnknownFileException | IOException ex) {
LOGGER.warning(String.format("Skipping file %s (%s)", fileName, ex.getMessage())); LOGGER.warning(String.format("Skipping file %s (%s)", inFileName, ex.getMessage()));
} catch (BrutException ex) { } catch (BrutException ex) {
throw new DirectoryException("Error copying file: " + fileName, ex); throw new DirectoryException("Error copying file: " + inFileName, ex);
} }
} }
} }

View File

@ -26,6 +26,14 @@ import java.io.*;
import java.util.zip.CRC32; import java.util.zip.CRC32;
public class BrutIO { public class BrutIO {
public static byte[] readAndClose(InputStream in) throws IOException {
try {
return IOUtils.toByteArray(in);
} finally {
IOUtils.closeQuietly(in);
}
}
public static void copyAndClose(InputStream in, OutputStream out) throws IOException { public static void copyAndClose(InputStream in, OutputStream out) throws IOException {
try { try {
IOUtils.copy(in, out); IOUtils.copy(in, out);
@ -60,11 +68,11 @@ public class BrutIO {
return modified; return modified;
} }
public static CRC32 calculateCrc(InputStream input) throws IOException { public static CRC32 calculateCrc(InputStream in) throws IOException {
CRC32 crc = new CRC32(); CRC32 crc = new CRC32();
int bytesRead; int bytesRead;
byte[] buffer = new byte[8192]; byte[] buffer = new byte[8192];
while ((bytesRead = input.read(buffer)) != -1) { while ((bytesRead = in.read(buffer)) != -1) {
crc.update(buffer, 0, bytesRead); crc.update(buffer, 0, bytesRead);
} }
return crc; return crc;

View File

@ -24,6 +24,8 @@ import java.io.IOException;
import java.util.logging.Logger; import java.util.logging.Logger;
public class ExtCountingDataInput extends ExtDataInput { public class ExtCountingDataInput extends ExtDataInput {
private static final Logger LOGGER = Logger.getLogger(ExtCountingDataInput.class.getName());
private final CountingInputStream mCountIn; private final CountingInputStream mCountIn;
public ExtCountingDataInput(LittleEndianDataInputStream in) { public ExtCountingDataInput(LittleEndianDataInputStream in) {
@ -67,6 +69,4 @@ public class ExtCountingDataInput extends ExtDataInput {
} }
return array; return array;
} }
private static final Logger LOGGER = Logger.getLogger(ExtCountingDataInput.class.getName());
} }

View File

@ -17,6 +17,7 @@
package brut.util; package brut.util;
import brut.common.BrutException; import brut.common.BrutException;
import brut.util.BrutIO;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import java.io.*; import java.io.*;
@ -42,8 +43,9 @@ public abstract class Jar {
} }
public static File extractToTmp(String resourcePath, String tmpPrefix, Class<?> clazz) throws BrutException { public static File extractToTmp(String resourcePath, String tmpPrefix, Class<?> clazz) throws BrutException {
InputStream in = null;
try { try {
InputStream in = clazz.getResourceAsStream(resourcePath); in = clazz.getResourceAsStream(resourcePath);
if (in == null) { if (in == null) {
throw new FileNotFoundException(resourcePath); throw new FileNotFoundException(resourcePath);
} }
@ -52,14 +54,13 @@ public abstract class Jar {
File fileOut = File.createTempFile(tmpPrefix, suffix + ".tmp"); File fileOut = File.createTempFile(tmpPrefix, suffix + ".tmp");
fileOut.deleteOnExit(); fileOut.deleteOnExit();
OutputStream out = Files.newOutputStream(fileOut.toPath()); BrutIO.copyAndClose(in, Files.newOutputStream(fileOut.toPath()));
IOUtils.copy(in, out);
in.close();
out.close();
return fileOut; return fileOut;
} catch (IOException ex) { } catch (IOException ex) {
throw new BrutException("Could not extract resource: " + resourcePath, ex); throw new BrutException("Could not extract resource: " + resourcePath, ex);
} finally {
IOUtils.closeQuietly(in);
} }
} }
} }

View File

@ -17,7 +17,7 @@
package brut.util; package brut.util;
import brut.common.BrutException; import brut.common.BrutException;
import org.apache.commons.io.IOUtils; import brut.util.BrutIO;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
@ -28,7 +28,6 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Logger; import java.util.logging.Logger;
public class OS { public class OS {
private static final Logger LOGGER = Logger.getLogger(""); private static final Logger LOGGER = Logger.getLogger("");
public static void rmdir(File dir) throws BrutException { public static void rmdir(File dir) throws BrutException {
@ -77,11 +76,7 @@ public class OS {
continue; continue;
} }
try { try {
try (InputStream in = Files.newInputStream(file.toPath())) { BrutIO.copyAndClose(Files.newInputStream(file.toPath()), Files.newOutputStream(destFile.toPath()));
try (OutputStream out = Files.newOutputStream(destFile.toPath())) {
IOUtils.copy(in, out);
}
}
} catch (IOException ex) { } catch (IOException ex) {
throw new BrutException("Could not copy file: " + file, ex); throw new BrutException("Could not copy file: " + file, ex);
} }