support for decoding unknown files, @todo add support for building unknown files

This commit is contained in:
Connor Tumbleson 2013-03-31 17:13:10 -05:00
parent 2e44e3a856
commit 0ca74eca67
4 changed files with 143 additions and 101 deletions

View File

@ -33,23 +33,12 @@ import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.apache.commons.io.FileUtils;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.apache.commons.io.FileUtils.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
@ -152,10 +141,11 @@ public class Androlib {
return false;
}
public void decodeUnknownFiles(ExtFile apkFile, File outDir)
public void decodeUnknownFiles(ExtFile apkFile, File outDir, ResTable resTable)
throws AndrolibException {
LOGGER.info("Copying unknown files/dir...");
File unknownOut = new File(outDir, UNK_DIRNAME);
ZipEntry invZipFile;
// have to use container of ZipFile to help identify compression type
// with regular looping of apkFile for easy copy
@ -163,23 +153,27 @@ public class Androlib {
Directory unk = apkFile.getDirectory();
ZipFile apkZipFile = new ZipFile(apkFile.getAbsolutePath());
// loop all items in container, ignoring any that are pre-defined by aapt
Set<String> files = unk.getFiles();
// loop all items in container recursively, ignoring any that are pre-defined by aapt
Set<String> files = unk.getFiles(true);
for (String file : files) {
if (!isAPKFileNames(file)) {
// copy file out of archive into special "unknown" folder
// to be re-included on build
unk.copyToDir(unknownOut,file);
System.out.println(apkZipFile.getEntry(file).getMethod());
try {
invZipFile = apkZipFile.getEntry(file.toString());
// lets record the name of the file, and its compression type
// so that we may re-include it the same way
if (invZipFile != null) {
resTable.addUnknownFileInfo(invZipFile.getName(), String.valueOf(invZipFile.getMethod()));
}
} catch (NullPointerException ignored) {
}
}
}
// for folders now.
Map<String, Directory> dirs = unk.getDirs();
for (String dir : dirs.keySet()) {
if (!isAPKFileNames(dir)) {
unk.copyToDir(unknownOut,dir);
System.out.println(apkZipFile.getEntry(dir).getMethod());
}
// @todo add ability to loop through dir and pull those methods
}
}
catch (DirectoryException ex) {
throw new AndrolibException(ex);
@ -187,8 +181,6 @@ public class Androlib {
catch (IOException ex) {
throw new AndrolibException(ex);
}
}
public void writeOriginalFiles(ExtFile apkFile, File outDir)
@ -287,6 +279,10 @@ public class Androlib {
buildLib(appDir, flags);
buildCopyOriginalFiles(appDir, flags);
buildApk(appDir, outFile, flags);
// we must go after the Apk is built, and copy the files in via Zip
// this is because Aapt won't add files it doesn't know (ex unknown files)
buildUnknownFiles(appDir,outFile,meta);
}
public void buildSources(File appDir, HashMap<String, Boolean> flags)
@ -533,53 +529,31 @@ public class Androlib {
}
}
public void remove_manifest_versions(String filePath)
throws AndrolibException {
public void buildUnknownFiles(File appDir, File outFile, Map<String, Object> meta)
throws AndrolibException {
File f = new File(filePath);
if (f.exists()) {
// remove versionCode and versionName
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(filePath.toString());
// confirm we have unknown files to inject
if (meta.containsKey("unknownFiles")) {
LOGGER.info("Copying unknown files/dir...");
Node manifest = doc.getFirstChild();
Map<String, String> files = (Map<String, String>)meta.get("unknownFiles");
// load attr
NamedNodeMap attr = manifest.getAttributes();
Node vCode = attr.getNamedItem("android:versionCode");
Node vName = attr.getNamedItem("android:versionName");
try {
ZipFile apkZipFile = new ZipFile(outFile.getAbsolutePath());
// remove versionCode
if (vCode != null) {
attr.removeNamedItem("android:versionCode");
// loop through files inside
for (Map.Entry<String,String> entry : files.entrySet()) {
// check if file exists
if (new File(appDir,entry.getKey()).isFile()) {
// apkZipFile.
}
}
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
if (vName != null) {
attr.removeNamedItem("android:versionName");
}
// save manifest
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(filePath));
transformer.transform(source, result);
} catch (ParserConfigurationException ex) {
throw new AndrolibException(ex);
} catch (SAXException ex) {
throw new AndrolibException(ex);
} catch (IOException ex) {
throw new AndrolibException(ex);
} catch (TransformerConfigurationException ex) {
throw new AndrolibException(ex);
} catch (TransformerException ex) {
throw new AndrolibException(ex);
}
}
}
public void buildApk(File appDir, File outApk,
HashMap<String, Boolean> flags) throws AndrolibException {

View File

@ -133,11 +133,8 @@ public class ApkDecoder {
}
mAndrolib.decodeRawFiles(mApkFile, outDir);
mAndrolib.decodeUnknownFiles(mApkFile, outDir);
mAndrolib.decodeUnknownFiles(mApkFile, outDir, mResTable);
mAndrolib.writeOriginalFiles(mApkFile, outDir);
// remove version names in favour of aapt injection
mAndrolib.remove_manifest_versions(outDir.getAbsolutePath() + "/AndroidManifest.xml");
writeMetaFile();
}
@ -251,6 +248,7 @@ public class ApkDecoder {
putPackageInfo(meta);
putVersionInfo(meta);
putCompressionInfo(meta);
putUnknownInfo(meta);
//meta.put("packageId", getResTable().getPackageInfo().get("cur_package_id"));
}
@ -281,32 +279,42 @@ public class ApkDecoder {
meta.put("usesFramework", uses);
}
private void putSdkInfo(Map<String, Object> meta) throws AndrolibException {
Map<String, String> info = getResTable().getSdkInfo();
if (info.size() > 0) {
meta.put("sdkInfo", info);
}
}
private void putPackageInfo(Map<String, Object> meta)
throws AndrolibException {
Map<String, String> info = getResTable().getPackageInfo();
if (info.size() > 0) {
meta.put("packageInfo", info);
}
}
private void putVersionInfo(Map<String, Object> meta) throws AndrolibException {
Map<String, String> info = getResTable().getVersionInfo();
if (info.size() > 0) {
meta.put("versionInfo", info);
private void putSdkInfo(Map<String, Object> meta)
throws AndrolibException {
Map<String, String> info = getResTable().getSdkInfo();
if (info.size() > 0) {
meta.put("sdkInfo", info);
}
}
}
private void putCompressionInfo(Map<String, Object> meta)
throws AndrolibException {
meta.put("compressionType", getCompressionType());
}
private void putPackageInfo(Map<String, Object> meta)
throws AndrolibException {
Map<String, String> info = getResTable().getPackageInfo();
if (info.size() > 0) {
meta.put("packageInfo", info);
}
}
private void putVersionInfo(Map<String, Object> meta)
throws AndrolibException {
Map<String, String> info = getResTable().getVersionInfo();
if (info.size() > 0) {
meta.put("versionInfo", info);
}
}
private void putUnknownInfo(Map<String, Object> meta)
throws AndrolibException {
Map<String,String> info = getResTable().getUnknownFiles();
if (info.size() > 0) {
meta.put("unknownFiles", info);
}
}
private void putCompressionInfo(Map<String, Object> meta)
throws AndrolibException {
meta.put("compressionType", getCompressionType());
}
private boolean getCompressionType() {
return mCompressResources;

View File

@ -47,7 +47,6 @@ import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.commons.io.IOUtils;
import org.xml.sax.SAXException;
@ -244,6 +243,54 @@ final public class AndrolibResources {
}
}
public void remove_manifest_versions(String filePath)
throws AndrolibException {
File f = new File(filePath);
if (f.exists()) {
// remove versionCode and versionName
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(filePath.toString());
Node manifest = doc.getFirstChild();
// load attr
NamedNodeMap attr = manifest.getAttributes();
Node vCode = attr.getNamedItem("android:versionCode");
Node vName = attr.getNamedItem("android:versionName");
// remove versionCode
if (vCode != null) {
attr.removeNamedItem("android:versionCode");
}
if (vName != null) {
attr.removeNamedItem("android:versionName");
}
// save manifest
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(filePath));
transformer.transform(source, result);
} catch (ParserConfigurationException ex) {
throw new AndrolibException(ex);
} catch (SAXException ex) {
throw new AndrolibException(ex);
} catch (IOException ex) {
throw new AndrolibException(ex);
} catch (TransformerConfigurationException ex) {
throw new AndrolibException(ex);
} catch (TransformerException ex) {
throw new AndrolibException(ex);
}
}
}
public void decode(ResTable resTable, ExtFile apkFile, File outDir)
throws AndrolibException {
Duo<ResFileDecoder, AXmlResourceParser> duo = getResFileDecoder();
@ -263,9 +310,13 @@ final public class AndrolibResources {
fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out,
"AndroidManifest.xml");
// fix package if needed
// fix package (Android 4.2)
adjust_package_manifest(resTable, outDir.getAbsolutePath()
+ "/AndroidManifest.xml");
+ File.separator + "AndroidManifest.xml");
// Remove versionName / versionCode (aapt API 16)
remove_manifest_versions(outDir.getAbsolutePath()
+ File.separator + "/AndroidManifest.xml");
if (inApk.containsDir("res")) {
in = inApk.getDir("res");

View File

@ -38,6 +38,7 @@ public class ResTable {
private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>();
private Map<String, String> mPackageInfo = new LinkedHashMap<String, String>();
private Map<String, String> mVersionInfo = new LinkedHashMap<String, String>();
private Map<String, String> mUnknownFiles = new LinkedHashMap<String, String>();
public ResTable() {
mAndRes = null;
@ -137,6 +138,10 @@ public class ResTable {
mPackageInfo.put(key, value);
}
public void addUnknownFileInfo(String file, String value) {
mUnknownFiles.put(file,value);
}
public Map<String, String> getPackageInfo() {
return mPackageInfo;
}
@ -149,6 +154,10 @@ public class ResTable {
return mSdkInfo;
}
public Map<String, String> getUnknownFiles() {
return mUnknownFiles;
}
public boolean isPackageInfoValueSet(String key) {
return (mPackageInfo.containsKey(key));
}