Skip to content

Commit

Permalink
New and aapt features for <info>
Browse files Browse the repository at this point in the history
  • Loading branch information
REAndroid committed Sep 26, 2024
1 parent 59a4044 commit 4ae3196
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 14 deletions.
Binary file modified libs/ARSCLib.jar
Binary file not shown.
61 changes: 60 additions & 1 deletion src/main/java/com/reandroid/apkeditor/info/Info.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@
package com.reandroid.apkeditor.info;

import com.reandroid.apk.ApkModule;
import com.reandroid.apk.ResFile;
import com.reandroid.apkeditor.CommandExecutor;
import com.reandroid.apkeditor.Util;
import com.reandroid.app.AndroidManifest;
import com.reandroid.archive.InputSource;
import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.arsc.chunk.xml.ResXmlAttribute;
import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.chunk.xml.ResXmlElement;
import com.reandroid.arsc.coder.EncodeResult;
import com.reandroid.arsc.coder.ReferenceString;
Expand Down Expand Up @@ -94,14 +97,20 @@ private void print(ApkModule apkModule) throws IOException {
printResources(apkModule);
printDex(apkModule);
printSignatures(apkModule);

printXmlTree(apkModule);
printXmlStrings(apkModule);
listFiles(apkModule);
listXmlFiles(apkModule);
}
private void printSourceFile() throws IOException {
InfoOptions options = getOptions();
if(options.outputFile == null){
return;
}
if(options.verbose || !options.resources){
getInfoWriter().writeNameValue("source-file",
InfoWriter infoWriter = getInfoWriter();
infoWriter.writeNameValue("source-file",
options.inputFile.getAbsolutePath());
}
}
Expand Down Expand Up @@ -346,6 +355,56 @@ private void printAppClass(ApkModule apkModule) throws IOException {
getInfoWriter().writeNameValue("application-class", value);
}
}
private void printXmlStrings(ApkModule apkModule) throws IOException {
InfoOptions options = getOptions();
String xmlStrings = options.xmlStrings;
if (xmlStrings == null) {
return;
}
InfoWriter infoWriter = getInfoWriter();
ResXmlDocument document = apkModule.loadResXmlDocument(xmlStrings);
document.setApkFile(null);
document.setPackageBlock(null);
infoWriter.writeStringPool(document.getStringPool());
}
private void printXmlTree(ApkModule apkModule) throws IOException {
InfoOptions options = getOptions();
InfoWriter infoWriter = getInfoWriter();
for (String path : options.xmlTree) {
logMessage("Writing: " + path);
ResXmlDocument document = apkModule.loadResXmlDocument(path);
document.setApkFile(null);
document.setPackageBlock(null);
infoWriter.writeXmlDocument(path, document);
}
}
private void listFiles(ApkModule apkModule) throws IOException {
InfoOptions options = getOptions();
if (!options.listFiles) {
return;
}
InputSource[] inputSources = apkModule.getInputSources();
int count = inputSources.length;
String[] names = new String[count];
for (int i = 0; i < count; i++) {
names[i] = inputSources[i].getAlias();
}
getInfoWriter().writeArray("Files", names);
}
private void listXmlFiles(ApkModule apkModule) throws IOException {
InfoOptions options = getOptions();
if (!options.listXmlFiles) {
return;
}
List<ResFile> resFileList = apkModule.listResFiles();
List<String> names = new ArrayList<>();
for (ResFile resFile : resFileList) {
if(resFile.isBinaryXml()) {
names.add(resFile.getFilePath());
}
}
getInfoWriter().writeArray("CompiledXmlFiles", names.toArray(new String[0]));
}
private String getValueOfName(ResXmlElement element){
ResXmlAttribute attribute = element
.searchAttributeByResourceId(AndroidManifest.ID_name);
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/com/reandroid/apkeditor/info/InfoOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ public class InfoOptions extends OptionsWithFramework {
public boolean signatures = false;
@OptionArg(name = "-signatures-base64", description = "info_signatures_base64", flag = true)
public boolean signatures_base64 = false;
@OptionArg(name = "-xmlstrings", description = "info_xml_strings")
public String xmlStrings;
@OptionArg(name = "-xmltree", description = "info_xml_tree")
public final List<String> xmlTree = new ArrayList<>();
@OptionArg(name = "-list-files", description = "info_list_files", flag = true)
public boolean listFiles = false;
@OptionArg(name = "-list-xml-files", description = "info_list_xml_files", flag = true)
public boolean listXmlFiles = false;

public InfoOptions(){
super();
Expand Down Expand Up @@ -139,8 +147,9 @@ private void initializeDefaults(){
private boolean isDefault() {
boolean flagsChanged = activities || appClass || appIcon || appName || appRoundIcon ||
dex || minSdkVersion || packageName || permissions || targetSdkVersion ||
resources || signatures || signatures_base64 || versionCode || versionName;
resources || signatures || signatures_base64 || versionCode || versionName ||
listFiles || listXmlFiles || xmlStrings != null;

return !flagsChanged && resList.isEmpty() && typeFilterList.isEmpty();
return !flagsChanged && resList.isEmpty() && typeFilterList.isEmpty() && xmlTree.isEmpty();
}
}
19 changes: 12 additions & 7 deletions src/main/java/com/reandroid/apkeditor/info/InfoWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import com.reandroid.archive.block.ApkSignatureBlock;
import com.reandroid.archive.block.CertificateBlock;
import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.coder.ValueCoder;
import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.model.ResourceEntry;
import com.reandroid.arsc.pool.StringPool;
import com.reandroid.dex.model.DexDirectory;
import com.reandroid.dex.model.DexFile;
import com.reandroid.utils.HexUtil;
Expand Down Expand Up @@ -58,6 +60,10 @@ public void writeDexInfo(DexDirectory dexDirectory) throws IOException {
}
}

public void writeStringPool(StringPool<?> stringPool) throws IOException {

}
public abstract void writeXmlDocument(String sourcePath, ResXmlDocument xmlDocument) throws IOException;
public abstract void writeCertificates(List<CertificateBlock> certificateList, boolean base64) throws IOException;
public abstract void writeDexInfo(DexFile dexFile, boolean writeSectionInfo) throws IOException;
public abstract void writeResources(ResourceEntry resourceEntry, boolean writeEntries) throws IOException;
Expand Down Expand Up @@ -86,13 +92,6 @@ static String toString(Object obj){
}
return null;
}
static String getValueAsString(Entry entry){
ResValue resValue = entry.getResValue();
if(resValue == null){
return "";
}
return getValueAsString(resValue);
}
static String getValueAsString(Value value){
ValueType valueType = value.getValueType();
if(valueType == ValueType.STRING){
Expand All @@ -114,6 +113,12 @@ static String toBase64(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}

static void writeSpaces(Writer writer, int amount) throws IOException {
for (int i = 0; i < amount; i ++) {
writer.append(' ');
}
}

static final String TAG_RES_PACKAGES = "resource-packages";
static final String TAG_PUBLIC = "public";
static final String TAG_RESOURCES = "resources";
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/reandroid/apkeditor/info/InfoWriterJson.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.reandroid.archive.block.CertificateBlock;
import com.reandroid.arsc.array.ResValueMapArray;
import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.model.ResourceEntry;
import com.reandroid.arsc.value.Entry;
Expand Down Expand Up @@ -46,6 +47,14 @@ public InfoWriterJson(Writer writer) {
this.mJsonWriter = jsonWriter;
}

@Override
public void writeXmlDocument(String sourcePath, ResXmlDocument xmlDocument) throws IOException {
JSONWriter jsonWriter = mJsonWriter.object();
jsonWriter.key("source_path").value(sourcePath);
jsonWriter.key("document").value(xmlDocument.toJson());
jsonWriter.endObject();
}

@Override
public void writeCertificates(List<CertificateBlock> certificateList, boolean base64) throws IOException {
JSONWriter jsonWriter = mJsonWriter.object()
Expand Down Expand Up @@ -220,6 +229,7 @@ public void writeNameValue(String name, Object value) throws IOException {
.endObject();
getWriter().flush();
}

@Override
public void flush() throws IOException {
Writer writer = getWriter();
Expand Down
147 changes: 143 additions & 4 deletions src/main/java/com/reandroid/apkeditor/info/InfoWriterText.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
import com.reandroid.archive.block.CertificateBlock;
import com.reandroid.arsc.array.ResValueMapArray;
import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.xml.*;
import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.header.StringPoolHeader;
import com.reandroid.arsc.item.StringItem;
import com.reandroid.arsc.model.ResourceEntry;
import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResTableMapEntry;
import com.reandroid.arsc.value.ResValue;
import com.reandroid.arsc.value.ResValueMap;
import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.value.*;
import com.reandroid.common.Namespace;
import com.reandroid.dex.model.DexFile;
import com.reandroid.dex.sections.MapItem;
import com.reandroid.dex.sections.MapList;
Expand All @@ -44,6 +46,140 @@ public InfoWriterText(Writer writer) {
super(writer);
}

@Override
public void writeStringPool(StringPool<?> stringPool) throws IOException {
Writer writer = getWriter();
writer.write("String pool of ");
writer.write(Integer.toString(stringPool.size()));
writer.write(" unique ");
if (stringPool.isUtf8()) {
writer.write("UTF-8 ");
} else {
writer.write("UTF-16 ");
}
StringPoolHeader header = stringPool.getHeaderBlock();
if (!header.isSorted()) {
writer.write("non-");
}
writer.write("sorted strings, ");
writer.write(Integer.toString(stringPool.size()));
writer.write(" entries and ");
writer.write(Integer.toString(stringPool.countStyles()));
writer.write(" styles using ");
writer.write(Integer.toString(header.getChunkSize()));
writer.write(" bytes:");
writer.write("\n");
int size = stringPool.size();
for (int i = 0; i < size; i++ ) {
StringItem item = stringPool.get(i);
writer.write("String #");
writer.write(Integer.toString(i));
writer.write(": ");
writer.write(item.get());
writer.write("\n");
}
}

@Override
public void writeXmlDocument(String sourcePath, ResXmlDocument xmlDocument) throws IOException {
writeNameValue("source-path", sourcePath);
for (ResXmlNode node : xmlDocument) {
if (node instanceof ResXmlElement) {
writeElement((ResXmlElement) node);
} else if (node instanceof ResXmlTextNode) {
writeTextNode(0, (ResXmlTextNode) node);
}
}
Writer writer = getWriter();
writer.flush();
}
private void writeElement(ResXmlElement element) throws IOException {
int indent = element.getDepth() * 2;

int count = element.getNamespaceCount();
for (int i = 0; i < count; i++) {
writeNamespace(indent, element.getNamespaceAt(i));
}
indent = indent + 2;
Writer writer = getWriter();
writeIndent(indent);
writer.write("E: ");
writer.write(element.getName(true));
writer.write(" (line=");
writer.write(Integer.toString(element.getLineNumber()));
writer.write(")");
writer.write("\n");

Iterator<ResXmlAttribute> attributes = element.getAttributes();
while (attributes.hasNext()) {
writeAttribute(indent, attributes.next());
}
flush();
Iterator<ResXmlNode> iterator = element.iterator();
while (iterator.hasNext()) {
ResXmlNode node = iterator.next();
if (node instanceof ResXmlElement) {
writeElement((ResXmlElement) node);
} else if (node instanceof ResXmlTextNode) {
writeTextNode(indent, (ResXmlTextNode) node);
}
}
}
private void writeTextNode(int indent, ResXmlTextNode textNode) throws IOException {
Writer writer = getWriter();
writeIndent(indent + 2);
writer.write("T: \"");
writer.write(textNode.getText());
writer.write("\"");
writer.write("\n");
}
private void writeNamespace(int indent, ResXmlNamespace namespace) throws IOException {
Writer writer = getWriter();
writeIndent(indent);
writer.write("N: ");
writer.write(namespace.getPrefix());
writer.write("=");
writer.write(namespace.getUri());
writer.write("\n");
}
private void writeAttribute(int indent, ResXmlAttribute attribute) throws IOException {
Writer writer = getWriter();
writeIndent(indent + 2);
writer.write("A: ");
Namespace namespace = attribute.getNamespace();
if (namespace != null) {
writer.write(namespace.getPrefix());
writer.append(':');
}
writer.write(attribute.getName());
int id = attribute.getNameId();
if (id != 0) {
writer.append('(');
writer.write(HexUtil.toHex8(id));
writer.append(')');
}
writer.append('=');
ValueType valueType = attribute.getValueType();
if (valueType == ValueType.STRING) {
writer.append('"');
writer.write(attribute.getDataAsPoolString().getXml());
writer.append('"');
writer.write(" (Raw: \"");
writer.write(attribute.getValueString());
writer.write("\")");
} else if (valueType == ValueType.BOOLEAN) {
writer.append('"');
writer.write(attribute.getValueAsBoolean() ? "true" : "false");
writer.append('"');
} else {
writer.write("(type ");
writer.write(HexUtil.toHex(valueType.getByte() & 0xff, 1));
writer.write(")");
writer.write(HexUtil.toHex(attribute.getData(), 1));
}
writer.write("\n");
}

@Override
public void writeCertificates(List<CertificateBlock> certificateList, boolean base64) throws IOException {
Writer writer = getWriter();
Expand Down Expand Up @@ -296,6 +432,9 @@ public void flush() throws IOException {
}


private void writeIndent(int amount) throws IOException {
writeSpaces(getWriter(), amount);
}
private void writeWithTab(Writer writer, String tab, String value) throws IOException {
String[] splits = StringsUtil.split(value, '\n');
for(String line : splits){
Expand Down
Loading

0 comments on commit 4ae3196

Please sign in to comment.