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.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
import javax.xml.parsers.DocumentBuilder; import org.apache.commons.io.FileUtils;
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.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import org.apache.commons.io.FileUtils.*;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
@ -152,10 +141,11 @@ public class Androlib {
return false; return false;
} }
public void decodeUnknownFiles(ExtFile apkFile, File outDir) public void decodeUnknownFiles(ExtFile apkFile, File outDir, ResTable resTable)
throws AndrolibException { throws AndrolibException {
LOGGER.info("Copying unknown files/dir..."); LOGGER.info("Copying unknown files/dir...");
File unknownOut = new File(outDir, UNK_DIRNAME); File unknownOut = new File(outDir, UNK_DIRNAME);
ZipEntry invZipFile;
// have to use container of ZipFile to help identify compression type // have to use container of ZipFile to help identify compression type
// with regular looping of apkFile for easy copy // with regular looping of apkFile for easy copy
@ -163,23 +153,27 @@ public class Androlib {
Directory unk = apkFile.getDirectory(); Directory unk = apkFile.getDirectory();
ZipFile apkZipFile = new ZipFile(apkFile.getAbsolutePath()); ZipFile apkZipFile = new ZipFile(apkFile.getAbsolutePath());
// loop all items in container, ignoring any that are pre-defined by aapt // loop all items in container recursively, ignoring any that are pre-defined by aapt
Set<String> files = unk.getFiles(); Set<String> files = unk.getFiles(true);
for (String file : files) { for (String file : files) {
if (!isAPKFileNames(file)) { if (!isAPKFileNames(file)) {
// copy file out of archive into special "unknown" folder
// to be re-included on build
unk.copyToDir(unknownOut,file); 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) { catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
@ -187,8 +181,6 @@ public class Androlib {
catch (IOException ex) { catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
public void writeOriginalFiles(ExtFile apkFile, File outDir) public void writeOriginalFiles(ExtFile apkFile, File outDir)
@ -287,6 +279,10 @@ public class Androlib {
buildLib(appDir, flags); buildLib(appDir, flags);
buildCopyOriginalFiles(appDir, flags); buildCopyOriginalFiles(appDir, flags);
buildApk(appDir, outFile, 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) public void buildSources(File appDir, HashMap<String, Boolean> flags)
@ -533,53 +529,31 @@ public class Androlib {
} }
} }
public void remove_manifest_versions(String filePath) public void buildUnknownFiles(File appDir, File outFile, Map<String, Object> meta)
throws AndrolibException { throws AndrolibException {
File f = new File(filePath); // confirm we have unknown files to inject
if (meta.containsKey("unknownFiles")) {
if (f.exists()) { LOGGER.info("Copying unknown files/dir...");
// remove versionCode and versionName
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(filePath.toString());
Node manifest = doc.getFirstChild(); Map<String, String> files = (Map<String, String>)meta.get("unknownFiles");
// load attr try {
NamedNodeMap attr = manifest.getAttributes(); ZipFile apkZipFile = new ZipFile(outFile.getAbsolutePath());
Node vCode = attr.getNamedItem("android:versionCode");
Node vName = attr.getNamedItem("android:versionName");
// remove versionCode // loop through files inside
if (vCode != null) { for (Map.Entry<String,String> entry : files.entrySet()) {
attr.removeNamedItem("android:versionCode");
// 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, public void buildApk(File appDir, File outApk,
HashMap<String, Boolean> flags) throws AndrolibException { HashMap<String, Boolean> flags) throws AndrolibException {

View File

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

View File

@ -47,7 +47,6 @@ import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.xml.sax.SAXException; 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) public void decode(ResTable resTable, ExtFile apkFile, File outDir)
throws AndrolibException { throws AndrolibException {
Duo<ResFileDecoder, AXmlResourceParser> duo = getResFileDecoder(); Duo<ResFileDecoder, AXmlResourceParser> duo = getResFileDecoder();
@ -263,9 +310,13 @@ final public class AndrolibResources {
fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out, fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out,
"AndroidManifest.xml"); "AndroidManifest.xml");
// fix package if needed // fix package (Android 4.2)
adjust_package_manifest(resTable, outDir.getAbsolutePath() 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")) { if (inApk.containsDir("res")) {
in = inApk.getDir("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> mSdkInfo = new LinkedHashMap<String, String>();
private Map<String, String> mPackageInfo = 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> mVersionInfo = new LinkedHashMap<String, String>();
private Map<String, String> mUnknownFiles = new LinkedHashMap<String, String>();
public ResTable() { public ResTable() {
mAndRes = null; mAndRes = null;
@ -137,6 +138,10 @@ public class ResTable {
mPackageInfo.put(key, value); mPackageInfo.put(key, value);
} }
public void addUnknownFileInfo(String file, String value) {
mUnknownFiles.put(file,value);
}
public Map<String, String> getPackageInfo() { public Map<String, String> getPackageInfo() {
return mPackageInfo; return mPackageInfo;
} }
@ -149,6 +154,10 @@ public class ResTable {
return mSdkInfo; return mSdkInfo;
} }
public Map<String, String> getUnknownFiles() {
return mUnknownFiles;
}
public boolean isPackageInfoValueSet(String key) { public boolean isPackageInfoValueSet(String key) {
return (mPackageInfo.containsKey(key)); return (mPackageInfo.containsKey(key));
} }