Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extract class Framework from AndrolibResources #3105

Merged
merged 1 commit into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 5 additions & 4 deletions brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import brut.androlib.exceptions.InFileNotFoundException;
import brut.androlib.exceptions.OutDirExistsException;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.Framework;
import brut.common.BrutException;
import brut.directory.DirectoryException;
import brut.directory.ExtFile;
Expand Down Expand Up @@ -271,23 +272,23 @@ private static void cmdBuild(CommandLine cli, Config config) {

private static void cmdInstallFramework(CommandLine cli, Config config) throws AndrolibException {
String apkName = getLastArg(cli);
new AndrolibResources(config).installFramework(new File(apkName));
new Framework(config).installFramework(new File(apkName));
}

private static void cmdListFrameworks(CommandLine cli, Config config) throws AndrolibException {
new AndrolibResources(config).listFrameworkDirectory();
new Framework(config).listFrameworkDirectory();
}

private static void cmdPublicizeResources(CommandLine cli, Config config) throws AndrolibException {
String apkName = getLastArg(cli);
new AndrolibResources(config).publicizeResources(new File(apkName));
new Framework(config).publicizeResources(new File(apkName));
}

private static void cmdEmptyFrameworkDirectory(CommandLine cli, Config config) throws AndrolibException {
if (cli.hasOption("f") || cli.hasOption("force")) {
config.forceDeleteFramework = true;
}
new AndrolibResources(config).emptyFrameworkDirectory();
new Framework(config).emptyFrameworkDirectory();
}

private static String getLastArg(CommandLine cli) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import brut.androlib.meta.MetaInfo;
import brut.androlib.meta.UsesFramework;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.Framework;
import brut.androlib.res.data.ResConfigFlags;
import brut.androlib.res.xml.ResXmlPatcher;
import brut.androlib.src.SmaliBuilder;
Expand Down Expand Up @@ -570,11 +571,12 @@ private File[] parseUsesFramework(UsesFramework usesFramework)
return null;
}

Framework framework = new Framework(config);
String tag = usesFramework.tag;
File[] files = new File[ids.size()];
int i = 0;
for (int id : ids) {
files[i++] = mAndRes.getFrameworkApk(id, tag);
files[i++] = framework.getFrameworkApk(id, tag);
}
return files;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ public ResPackage selectPkgWithMostResSpecs(ResPackage[] pkgs) {

public ResPackage loadFrameworkPkg(ResTable resTable, int id)
throws AndrolibException {
File apk = getFrameworkApk(id, config.frameworkTag);
Framework framework = new Framework(config);
File apk = framework.getFrameworkApk(id, config.frameworkTag);

LOGGER.info("Loading resource table from file: " + apk);
mFramework = new ExtFile(apk);
Expand Down Expand Up @@ -819,215 +820,6 @@ private ResPackage[] getResPackagesFromApk(ExtFile apkFile, ResTable resTable, b
}
}

public File getFrameworkApk(int id, String frameTag)
throws AndrolibException {
File dir = getFrameworkDirectory();
File apk;

if (frameTag != null) {
apk = new File(dir, String.valueOf(id) + '-' + frameTag + ".apk");
if (apk.exists()) {
return apk;
}
}

apk = new File(dir, id + ".apk");
if (apk.exists()) {
return apk;
}

if (id == 1) {
try (InputStream in = getAndroidFrameworkResourcesAsStream();
OutputStream out = Files.newOutputStream(apk.toPath())) {
IOUtils.copy(in, out);
return apk;
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}

throw new CantFindFrameworkResException(id);
}

public void emptyFrameworkDirectory() throws AndrolibException {
File dir = getFrameworkDirectory();
File apk;

apk = new File(dir, "1.apk");

if (! apk.exists()) {
LOGGER.warning("Can't empty framework directory, no file found at: " + apk.getAbsolutePath());
} else {
try {
if (apk.exists() && Objects.requireNonNull(dir.listFiles()).length > 1 && ! config.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())) {
if (file.isFile() && file.getName().endsWith(".apk")) {
LOGGER.info("Removing " + file.getName() + " framework file...");
//noinspection ResultOfMethodCallIgnored
file.delete();
}
}
}
} catch (NullPointerException e) {
throw new AndrolibException(e);
}
}
}

public void listFrameworkDirectory() throws AndrolibException {
File dir = getFrameworkDirectory();
if (dir == null) {
LOGGER.severe("No framework directory found. Nothing to list.");
return;
}

for (File file : Objects.requireNonNull(dir.listFiles())) {
if (file.isFile() && file.getName().endsWith(".apk")) {
LOGGER.info(file.getName());
}
}
}

public void installFramework(File frameFile) throws AndrolibException {
installFramework(frameFile, config.frameworkTag);
}

public void installFramework(File frameFile, String tag)
throws AndrolibException {
InputStream in = null;
ZipOutputStream out = null;
try {
ZipFile zip = new ZipFile(frameFile);
ZipEntry entry = zip.getEntry("resources.arsc");

if (entry == null) {
throw new AndrolibException("Can't find resources.arsc file");
}

in = zip.getInputStream(entry);
byte[] data = IOUtils.toByteArray(in);

ARSCData arsc = ARSCDecoder.decode(new ByteArrayInputStream(data), true, true);
publicizeResources(data, arsc.getFlagsOffsets());

File outFile = new File(getFrameworkDirectory(), arsc
.getOnePackage().getId()
+ (tag == null ? "" : '-' + tag)
+ ".apk");

out = new ZipOutputStream(Files.newOutputStream(outFile.toPath()));
out.setMethod(ZipOutputStream.STORED);
CRC32 crc = new CRC32();
crc.update(data);
entry = new ZipEntry("resources.arsc");
entry.setSize(data.length);
entry.setMethod(ZipOutputStream.STORED);
entry.setCrc(crc.getValue());
out.putNextEntry(entry);
out.write(data);
out.closeEntry();

//Write fake AndroidManifest.xml file to support original aapt
entry = zip.getEntry("AndroidManifest.xml");
if (entry != null) {
in = zip.getInputStream(entry);
byte[] manifest = IOUtils.toByteArray(in);
CRC32 manifestCrc = new CRC32();
manifestCrc.update(manifest);
entry.setSize(manifest.length);
entry.setCompressedSize(-1);
entry.setCrc(manifestCrc.getValue());
out.putNextEntry(entry);
out.write(manifest);
out.closeEntry();
}

zip.close();
LOGGER.info("Framework installed to: " + outFile);
} catch (IOException ex) {
throw new AndrolibException(ex);
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
}
}

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())) {
//noinspection ResultOfMethodCallIgnored
in.read(data);
publicizeResources(data);
out.write(data);
} catch (IOException ex){
throw new AndrolibException(ex);
}
}

public void publicizeResources(byte[] arsc) throws AndrolibException {
publicizeResources(arsc, ARSCDecoder.decode(new ByteArrayInputStream(arsc), true, true).getFlagsOffsets());
}

public void publicizeResources(byte[] arsc, FlagsOffset[] flagsOffsets) {
for (FlagsOffset flags : flagsOffsets) {
int offset = flags.offset + 3;
int end = offset + 4 * flags.count;
while (offset < end) {
arsc[offset] |= (byte) 0x40;
offset += 4;
}
}
}

public File getFrameworkDirectory() throws AndrolibException {
if (mFrameworkDirectory != null) {
return mFrameworkDirectory;
}

String path;

// use default framework path or specified on the command line
path = config.frameworkDirectory;

File dir = new File(path);

if (!dir.isDirectory() && dir.isFile()) {
throw new AndrolibException("--frame-path is set to a file, not a directory.");
}

if (dir.getParentFile() != null && dir.getParentFile().isFile()) {
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);
}
throw new AndrolibException(String.format(
"Can't create directory: (%s). Pass a writable path with --frame-path {DIR}. ", dir
));
}
}

if (config.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, " +
"please utilize --frame-path if the default storage directory is unavailable");

dir = new File(System.getProperty("java.io.tmpdir"));
}
}

mFrameworkDirectory = dir;
return dir;
}

private File getAaptBinaryFile() throws AndrolibException {
try {
Expand All @@ -1044,10 +836,6 @@ private int getAaptVersion() {
return config.isAapt2() ? 2 : 1;
}

public InputStream getAndroidFrameworkResourcesAsStream() {
return Jar.class.getResourceAsStream("/brut/androlib/android-framework.jar");
}

public void close() throws IOException {
if (mFramework != null) {
mFramework.close();
Expand Down