All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.dongliu.apk.parser.ApkParser Maven / Gradle / Ivy

package net.dongliu.apk.parser;

import net.dongliu.apk.parser.bean.ApkMeta;
import net.dongliu.apk.parser.bean.CertificateMeta;
import net.dongliu.apk.parser.bean.DexClass;
import net.dongliu.apk.parser.bean.Locale;
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 org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.util.*;

/**
 * ApkParser and result holder.
 * This class is not thread-safe.
 *
 * @author dongliu
 */
public class ApkParser implements Closeable {

    private DexClass[] dexClasses;
    private ResourceTable resourceTable;

    private Map manifestXmlMap;
    private Map apkMetaMap;
    private Set locales;
    private List certificateMetas;
    private final ZipFile zf;

    private Locale preferredLocale;

    public ApkParser(File apkFile) throws IOException {
        this.zf = new ZipFile(apkFile);
        this.preferredLocale = Locale.any;
        this.manifestXmlMap = new HashMap();
        this.apkMetaMap = new HashMap();
    }

    /**
     * return decoded AndroidManifest.xml
     *
     * @return decoded AndroidManifest.xml
     */
    public String getManifestXml() throws IOException {
        if (!manifestXmlMap.containsKey(preferredLocale)) {
            parseManifestXml();
        }
        return manifestXmlMap.get(preferredLocale);
    }

    /**
     * return decoded AndroidManifest.xml
     *
     * @return decoded AndroidManifest.xml
     */
    public ApkMeta getApkMeta() throws IOException {
        if (!apkMetaMap.containsKey(preferredLocale)) {
            parseManifestXml();
        }
        return apkMetaMap.get(preferredLocale);
    }

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

    /**
     * get the apk's certificates.
     */
    public List getCertificateMetas() throws IOException,
            CertificateEncodingException {
        if (this.certificateMetas == null) {
            parseCertificate();
        }
        return this.certificateMetas;
    }

    private void parseCertificate() throws IOException, CertificateEncodingException {
        ZipArchiveEntry entry = null;
        Enumeration enu = zf.getEntries();
        while (enu.hasMoreElements()) {
            entry = enu.nextElement();
            if (entry.isDirectory()) {
                continue;
            }
            if (entry.getName().toUpperCase().endsWith(".RSA")
                    || entry.getName().toUpperCase().endsWith(".DSA")) {
                break;
            }
        }
        if (entry == null) {
            throw new ParserException("ApkParser certificate not found");
        }
        CetificateParser parser = new CetificateParser(zf.getInputStream(entry));
        parser.parse();
        this.certificateMetas = parser.getCertificateMetas();
    }

    /**
     * parse manifest.xml, get apkMeta and manifestXml text.
     *
     * @throws IOException
     */
    private void parseManifestXml() throws IOException {
        ZipArchiveEntry manifestEntry = null;
        Enumeration enu = zf.getEntries();
        while (enu.hasMoreElements()) {
            ZipArchiveEntry entry = enu.nextElement();
            if (entry.isDirectory()) {
                continue;
            }
            if (entry.getName().equals(AndroidConstants.MANIFEST)) {
                manifestEntry = entry;
            }

        }

        if (manifestEntry == null) {
            throw new ParserException("manifest xml file not found");
        }

        if (this.resourceTable == null) {
            parseResourceTable();
        }

        BinaryXmlParser binaryXmlParser = new BinaryXmlParser(zf.getInputStream(manifestEntry),
                manifestEntry.getSize(),
                resourceTable);
        binaryXmlParser.setLocale(preferredLocale);
        XmlTranslator xmlTranslator = new XmlTranslator();
        ApkMetaConstructor apkMetaConstructor = new ApkMetaConstructor();
        CompositeXmlStreamer xmlStreamer = new CompositeXmlStreamer(xmlTranslator,
                apkMetaConstructor);
        binaryXmlParser.setXmlStreamer(xmlStreamer);
        binaryXmlParser.parse();
        manifestXmlMap.put(preferredLocale, xmlTranslator.getXml());
        apkMetaMap.put(preferredLocale, apkMetaConstructor.getApkMeta());
    }

    /**
     * parse resource table.
     */
    private void parseResourceTable() throws IOException {
        ZipArchiveEntry resourceEntry = null;
        Enumeration enu = zf.getEntries();
        while (enu.hasMoreElements()) {
            ZipArchiveEntry entry = enu.nextElement();
            if (entry.isDirectory()) {
                continue;
            }
            if (entry.getName().equals(AndroidConstants.RESOURCE)) {
                resourceEntry = entry;
            }
        }
        if (resourceEntry == null) {
            throw new ParserException("resource table not found");
        }
        ResourceTableParser resourceTableParser = new ResourceTableParser(
                zf.getInputStream(resourceEntry));
        resourceTableParser.parse();
        this.resourceTable = resourceTableParser.getResourceTable();
        this.locales = resourceTableParser.getLocales();
    }

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

    private void parseDexFile() throws IOException {
        ZipArchiveEntry resourceEntry = null;
        Enumeration enu = zf.getEntries();
        while (enu.hasMoreElements()) {
            ZipArchiveEntry entry = enu.nextElement();
            if (entry.isDirectory()) {
                continue;
            }
            if (entry.getName().equals(AndroidConstants.DEX)) {
                resourceEntry = entry;
            }
        }
        if (resourceEntry == null) {
            throw new ParserException("resource table not found");
        }
        DexParser dexParser = new DexParser(zf.getInputStream(resourceEntry));
        dexParser.parse();
        this.dexClasses = dexParser.getDexClasses();
    }

    @Override
    public void close() throws IOException {
        this.certificateMetas = null;
        this.apkMetaMap = null;
        this.manifestXmlMap = null;
        this.resourceTable = null;
        this.certificateMetas = null;
        zf.close();
    }

    public Locale getPreferredLocale() {
        return preferredLocale;
    }

    /**
     * The locale prefrerred.
     * Will cause getManifestXml / getApkMeta to return diffrent values.
     */
    public void setPreferredLocale(Locale preferredLocale) {
        this.preferredLocale = preferredLocale;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy