Skip to content

Commit

Permalink
add byte array Apk Parser
Browse files Browse the repository at this point in the history
  • Loading branch information
董刘 committed Jan 25, 2016
1 parent a139d57 commit 0d6bd44
Show file tree
Hide file tree
Showing 6 changed files with 337 additions and 239 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<artifactId>apk-parser</artifactId>
<name>apk-parser</name>
<packaging>jar</packaging>
<version>2.0.15</version>
<version>2.1.0</version>
<url>https://github.com/xiaxiaocao/apk-parser</url>
<developers>
<developer>
Expand Down
255 changes: 255 additions & 0 deletions src/main/java/net/dongliu/apk/parser/AbstractApkParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
package net.dongliu.apk.parser;

import net.dongliu.apk.parser.bean.*;
import net.dongliu.apk.parser.exception.ParserException;
import net.dongliu.apk.parser.parser.*;
import net.dongliu.apk.parser.struct.AndroidConstants;
import net.dongliu.apk.parser.struct.resource.ResourceTable;

import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.cert.CertificateException;
import java.util.*;

/**
* Common Apk Parser methods.
* This Class is not thread-safe
*
* @author Liu Dong
*/
public abstract class AbstractApkParser implements Closeable {
private DexClass[] dexClasses;
private ResourceTable resourceTable;

private String manifestXml;
private ApkMeta apkMeta;
private Set<Locale> locales;
private List<CertificateMeta> certificateMetaList;

private static final Locale DEFAULT_LOCALE = Locale.US;

/**
* default use empty locale
*/
private Locale preferredLocale = DEFAULT_LOCALE;

/**
* return decoded AndroidManifest.xml
*
* @return decoded AndroidManifest.xml
*/
public String getManifestXml() throws IOException {
if (this.manifestXml == null) {
parseManifestXml();
}
return this.manifestXml;
}

/**
* return decoded AndroidManifest.xml
*
* @return decoded AndroidManifest.xml
*/
public ApkMeta getApkMeta() throws IOException {
if (this.apkMeta == null) {
parseApkMeta();
}
return this.apkMeta;
}

/**
* get locales supported from resource file
*
* @return decoded AndroidManifest.xml
* @throws IOException
*/
public Set<Locale> getLocales() throws IOException {
if (this.locales == null) {
parseResourceTable();
}
return this.locales;
}

/**
* get the apk's certificates.
*/
public List<CertificateMeta> getCertificateMetaList() throws IOException,
CertificateException {
if (this.certificateMetaList == null) {
parseCertificate();
}
return this.certificateMetaList;
}

protected abstract byte[] getCertificateData() throws IOException;

private void parseCertificate() throws IOException, CertificateException {

byte[] data = getCertificateData();
if (data == null) {
throw new ParserException("ApkParser certificate not found");
}
CertificateParser parser = new CertificateParser(data);
parser.parse();
this.certificateMetaList = parser.getCertificateMetas();
}

/**
* parse manifest.xml, get apkMeta.
*
* @throws IOException
*/
private void parseApkMeta() throws IOException {
if (this.manifestXml == null) {
parseManifestXml();
}
}

/**
* parse manifest.xml, get manifestXml as xml text.
*
* @throws IOException
*/
private void parseManifestXml() throws IOException {
XmlTranslator xmlTranslator = new XmlTranslator();
ApkMetaTranslator translator = new ApkMetaTranslator();
XmlStreamer xmlStreamer = new CompositeXmlStreamer(xmlTranslator, translator);

byte[] data = getFileData(AndroidConstants.MANIFEST_FILE);
if (data == null) {
throw new ParserException("Manifest file not found");
}
transBinaryXml(data, xmlStreamer);
this.manifestXml = xmlTranslator.getXml();
this.apkMeta = translator.getApkMeta();
}

/**
* read file in apk into bytes
*/
public abstract byte[] getFileData(String path) throws IOException;


/**
* trans binary xml file to text xml file.
*
* @param path the xml file path in apk file
* @return the text. null if file not exists
* @throws IOException
*/
public String transBinaryXml(String path) throws IOException {
byte[] data = getFileData(path);
if (data == null) {
return null;
}
if (this.resourceTable == null) {
parseResourceTable();
}

XmlTranslator xmlTranslator = new XmlTranslator();
transBinaryXml(data, xmlTranslator);
return xmlTranslator.getXml();
}

private void transBinaryXml(byte[] data, XmlStreamer xmlStreamer) throws IOException {
if (this.resourceTable == null) {
parseResourceTable();
}

ByteBuffer buffer = ByteBuffer.wrap(data);
BinaryXmlParser binaryXmlParser = new BinaryXmlParser(buffer, resourceTable);
binaryXmlParser.setLocale(preferredLocale);
binaryXmlParser.setXmlStreamer(xmlStreamer);
binaryXmlParser.parse();
}

/**
* get the apk icon file as bytes.
*
* @return the apk icon data,null if icon not found
* @throws IOException
*/
public Icon getIconFile() throws IOException {
ApkMeta apkMeta = getApkMeta();
String iconPath = apkMeta.getIcon();
if (iconPath == null) {
return null;
}
return new Icon(iconPath, getFileData(iconPath));
}

/**
* get class infos form dex file. currently only class name
*/
public DexClass[] getDexClasses() throws IOException {
if (this.dexClasses == null) {
parseDexFile();
}
return this.dexClasses;
}

private void parseDexFile() throws IOException {
byte[] data = getFileData(AndroidConstants.DEX_FILE);
if (data == null) {
throw new ParserException("Dex file not found");
}
ByteBuffer buffer = ByteBuffer.wrap(data);
DexParser dexParser = new DexParser(buffer);
dexParser.parse();
this.dexClasses = dexParser.getDexClasses();
}

/**
* parse resource table.
*/
private void parseResourceTable() throws IOException {
byte[] data = getFileData(AndroidConstants.RESOURCE_FILE);
if (data == null) {
// if no resource entry has been found, we assume it is not needed by this APK
this.resourceTable = new ResourceTable();
this.locales = Collections.emptySet();
return;
}

this.resourceTable = new ResourceTable();
this.locales = Collections.emptySet();

ByteBuffer buffer = ByteBuffer.wrap(data);
ResourceTableParser resourceTableParser = new ResourceTableParser(buffer);
resourceTableParser.parse();
this.resourceTable = resourceTableParser.getResourceTable();
this.locales = resourceTableParser.getLocales();
}

/**
* check apk sign
*
* @throws IOException
*/
public abstract ApkSignStatus verifyApk() throws IOException;

@Override
public void close() throws IOException {
this.certificateMetaList = null;
this.resourceTable = null;
this.certificateMetaList = null;
}

public Locale getPreferredLocale() {
return preferredLocale;
}


/**
* The locale preferred. Will cause getManifestXml / getApkMeta to return different values.
* The default value is from os default locale setting.
*/
public void setPreferredLocale(Locale preferredLocale) {
if (!Objects.equals(this.preferredLocale, preferredLocale)) {
this.preferredLocale = preferredLocale;
this.manifestXml = null;
this.apkMeta = null;
}
}
}
Loading

0 comments on commit 0d6bd44

Please sign in to comment.