forked from hsiafan/apk-parser
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
董刘
committed
Jan 25, 2016
1 parent
a139d57
commit 0d6bd44
Showing
6 changed files
with
337 additions
and
239 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
255 changes: 255 additions & 0 deletions
255
src/main/java/net/dongliu/apk/parser/AbstractApkParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
Oops, something went wrong.