parser.axml.ManifestParser.java Source code

Java tutorial

Introduction

Here is the source code for parser.axml.ManifestParser.java

Source

/*
 * Copyright 2008 Android4ME
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package parser.axml;

import org.apache.commons.io.IOUtils;
import org.xmlpull.v1.XmlPullParser;
import parser.arsc.ARSCParser;
import parser.axml.res.AXMLParser;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * ? AndroidManifest.xml 
 */
public class ManifestParser {
    private static final float RADIX_MULTS[] = { 0.00390625F, 3.051758E-005F, 1.192093E-007F, 4.656613E-010F };

    private static final String DIMENSION_UNITS[] = { "px", "dip", "sp", "pt", "in", "mm", "", "" };

    private static final String FRACTION_UNITS[] = { "%", "%p", "", "", "", "", "", "" };

    private static final int TYPE_REFERENCE = 1, TYPE_ATTRIBUTE = 2, TYPE_STRING = 3, TYPE_FLOAT = 4,
            TYPE_DIMENSION = 5, TYPE_FRACTION = 6, TYPE_FIRST_INT = 16, TYPE_INT_HEX = 17, TYPE_INT_BOOLEAN = 18,
            TYPE_FIRST_COLOR_INT = 28, TYPE_LAST_COLOR_INT = 31, TYPE_LAST_INT = 31, COMPLEX_UNIT_MASK = 15;

    private byte[] m_arsc;

    private StringBuilder m_xml;
    /**
     * ,????
     */
    private boolean m_noback = false;

    /**
     * ????
     *
     * @param prefix 
     * @return ???
     */
    private static String getNamespacePrefix(String prefix) {
        if (prefix == null || prefix.length() == 0) {
            return "";
        }

        if (prefix.contains("http://schemas.android.com/apk/res/android")) {
            return "android:";
        }

        return prefix + ":";
    }

    /**
     * ??
     *
     * @param id id
     * @return ?
     */
    private static String getPackage(int id) {
        if (id >>> 24 == 1) {
            return "android:";
        }
        return "";
    }

    /////////////////////////////////// ILLEGAL STUFF, DON'T LOOK :)

    public static float complexToFloat(int complex) {
        return (float) (complex & 0xFFFFFF00) * RADIX_MULTS[(complex >> 4) & 3];
    }

    /**
     * ? AndroidManifest.xml  <code>ManifestInfo </code>.
     *
     * @param pFile apk 
     * @return ??????? null
     */
    public ManifestInfo parse(File pFile) throws IOException {
        ManifestInfo manifestInfo = new ManifestInfo();
        m_noback = false;

        ZipFile zipFile;
        InputStream aXMLInputStream = null;
        InputStream arscInputStream = null;
        try {
            zipFile = new ZipFile(pFile);
            ZipEntry zipEntry = zipFile.getEntry("AndroidManifest.xml");

            if (zipEntry != null) {
                aXMLInputStream = zipFile.getInputStream(zipEntry);
            } else {
                manifestInfo = null;
            }

            zipEntry = zipFile.getEntry("resources.arsc");
            if (zipEntry != null) {
                arscInputStream = zipFile.getInputStream(zipEntry);
            }

            if (arscInputStream != null) {
                m_arsc = IOUtils.toByteArray(arscInputStream);
            }
        } catch (IOException e) {
            throw new IOException(e.getMessage());
        }

        if (aXMLInputStream != null) {
            try {
                parseManifest(aXMLInputStream, manifestInfo);
                manifestInfo.back = !m_noback;
                manifestInfo.xml = (m_xml == null) ? null : m_xml.toString();
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }

        try {
            zipFile.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (aXMLInputStream != null) {
            try {
                aXMLInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (arscInputStream != null) {
            try {
                arscInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return manifestInfo;
    }

    public ManifestInfo parse(InputStream amInputStream, byte[] arscBytes) throws IOException {
        ManifestInfo manifestInfo = new ManifestInfo();
        m_noback = false;
        m_arsc = arscBytes;

        if (amInputStream != null) {
            try {
                parseManifest(amInputStream, manifestInfo);
                manifestInfo.back = !m_noback;
                manifestInfo.xml = (m_xml == null) ? null : m_xml.toString();
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }

        return manifestInfo;
    }

    /**
     * ? AndroidManifest.xml
     *
     * @param amInputStream AndroidManifest.xml ?
     * @param manifestInfo  ManifestInfo
     * @throws IOException
     */
    private void parseManifest(InputStream amInputStream, ManifestInfo manifestInfo) throws IOException {

        final HashSet<String> permissionsSet = new HashSet<>();
        final HashMap<String, ArrayList<String>> activities = new HashMap<>();
        final ArrayList<String> services = new ArrayList<>();
        final HashMap<String, ArrayList<String>> receivers = new HashMap<>();
        String key = null;
        final byte FLAG_ACTIVITY = 0;
        final byte FLAG_RECEIVER = 1;
        byte flag = 2;

        final AXMLParser parser = new AXMLParser(amInputStream);
        final String indentStep = "\t";
        StringBuilder indent = new StringBuilder(10);
        m_xml = new StringBuilder(100);
        while (true) {
            final int type = parser.next();
            if (type == XmlPullParser.END_DOCUMENT) {
                break;
            }

            final String tagName = parser.getName();
            if (type == XmlPullParser.END_TAG && tagName.equals("manifest")) {
                break;
            }

            switch (type) {
            case XmlPullParser.START_DOCUMENT:
                m_xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
                break;
            case AXMLParser.CHUNK_XML_START_TAG:
                //                    final String tagName = parser.getName();
                m_xml.append(indent).append("<").append(tagName);
                indent.append(indentStep);

                switch (tagName) {
                case "manifest":
                    parseTagManifest(parser, manifestInfo);
                    break;
                case "application":
                    parseTagApplication(parser, manifestInfo);
                    break;
                case "uses-permission":
                    parseTagPermission(parser, permissionsSet);
                    break;
                case "activity":
                case "activity-alias":
                    key = parseTagActivity(parser);
                    activities.put(key, new ArrayList<String>());
                    flag = FLAG_ACTIVITY;
                    break;
                case "service":
                    parseTagService(parser, services);
                    break;
                case "receiver":
                    key = parseTagReceiver(parser);
                    receivers.put(key, new ArrayList<String>());
                    flag = FLAG_RECEIVER;
                    break;
                case "action":
                    if (flag == FLAG_ACTIVITY) {
                        parseTagAction(parser, activities.get(key));
                    } else if (flag == FLAG_RECEIVER) {
                        parseTagAction(parser, receivers.get(key));
                    }
                    break;
                case "category":
                    if (flag == FLAG_ACTIVITY) {
                        parseTagCategory(parser, activities.get(key));
                    } else if (flag == FLAG_RECEIVER) {
                        parseTagCategory(parser, receivers.get(key));
                    }
                    break;
                }

                //xml
                for (int i = 0; i != parser.getAttributeCount(); ++i) {
                    final String v = makeAttributeValue(parser, i);
                    final String attr = getNamespacePrefix(parser.getAttributePrefix(i))
                            + parser.getAttributeName(i);
                    //?????
                    if (v.equals("android.intent.category.LAUNCHER"))
                        m_noback = true;

                    m_xml.append(" ").append(attr).append("=\"").append(v).append("\"");
                }
                m_xml.append(">\n");
                break;
            case AXMLParser.CHUNK_XML_END_TAG:
                indent.setLength(indent.length() - indentStep.length());
                m_xml.append(indent).append("</").append(parser.getName()).append(">\n");
                break;
            case AXMLParser.CHUNK_XML_TEXT:
                break;
            }
        }

        ArrayList<String> requestedPermissions = new ArrayList<>(permissionsSet);
        Collections.sort(requestedPermissions);
        manifestInfo.requestedPermissions = requestedPermissions;
        manifestInfo.services = services;
        manifestInfo.receivers = receivers;
        manifestInfo.activities = activities;
    }

    private String parseTagActivity(AXMLParser parser) {
        StringBuilder value = new StringBuilder();
        for (int i = 0; i != parser.getAttributeCount(); ++i) {
            String v = makeAttributeValue(parser, i);
            final String attr = getNamespacePrefix(parser.getAttributeNamespace(i)) + parser.getAttributeName(i);
            if ("android:name".equals(attr)) {
                return v;
            } else if ("".equals(attr)) {
                value.append(v).append("-");
            }
        }

        return value.append(" [NOT Default AXML!]").toString();
    }

    /**
     * ? manifest ? versionName, versionCode, packageName
     */
    private void parseTagManifest(AXMLParser parser, ManifestInfo manifestInfo) {
        m_xml.append(" xmlns:android=\"http://schemas.android.com/apk/res/android\"");
        for (int i = 0; i != parser.getAttributeCount(); ++i) {
            final String attributeValue = makeAttributeValue(parser, i);
            final String attribute = getNamespacePrefix(parser.getAttributePrefix(i)) + parser.getAttributeName(i);
            switch (attribute) {
            case "android:versionName":
                manifestInfo.versionName = attributeValue;
                break;
            case "package":
                manifestInfo.packageName = attributeValue;
                break;
            case "android:versionCode":
                manifestInfo.versionCode = attributeValue;
                break;
            }
        }
    }

    /**
     * ? application ?label(appName)
     */
    private void parseTagApplication(AXMLParser parser, ManifestInfo pkgInfo) {
        for (int i = 0; i != parser.getAttributeCount(); ++i) {
            final String v = makeAttributeValue(parser, i);
            final String attr = getNamespacePrefix(parser.getAttributePrefix(i)) + parser.getAttributeName(i);
            if ("android:label".equals(attr)) {
                pkgInfo.label = v;
                break;
            }
        }
    }

    /**
     * ???
     */
    private void parseTagPermission(AXMLParser parser, Collection<String> requestedPermissions) {
        for (int i = 0; i != parser.getAttributeCount(); ++i) {
            String attributeName = parser.getAttributeName(i);
            if (attributeName.contains("name")) {
                String v = makeAttributeValue(parser, i);
                if (v.startsWith("android.permission.")) {
                    v = v.substring(19);
                }
                requestedPermissions.add(v);
            } else if (attributeName.equals("")) {
                String v = makeAttributeValue(parser, i);
                if (v.startsWith("android.permission.")) {
                    v = v.substring(19);
                }
                requestedPermissions.add(v);
            }
        }

    }

    /**
     * ? service 
     *
     * @param parser ?
     * @param services ?
     */
    private void parseTagService(AXMLParser parser, ArrayList<String> services) {
        StringBuilder nullValue = new StringBuilder();
        for (int i = 0; i != parser.getAttributeCount(); ++i) {
            String value = makeAttributeValue(parser, i);
            final String attr = getNamespacePrefix(parser.getAttributeNamespace(i)) + parser.getAttributeName(i);
            if ("android:name".equals(attr)) {
                // <service android:name="">
                services.add(value);
            } else if ("".equals(attr)) {
                // <service ="service_name">
                nullValue.append(value).append(" - ");
                services.add(nullValue.append(" [NOT Default AXML!]").toString());
            }
        }

        //        services.add(nullValue.append(" [NOT Default AXML!]").toString());
    }

    /**
     * ? receiver 
     *
     * @param parser ?
     */
    private String parseTagReceiver(AXMLParser parser) {
        StringBuilder nullValue = new StringBuilder();
        for (int i = 0; i != parser.getAttributeCount(); ++i) {
            String v = makeAttributeValue(parser, i);
            final String attr = getNamespacePrefix(parser.getAttributeNamespace(i)) + parser.getAttributeName(i);
            if ("android:name".equals(attr)) {
                return v;
            } else if ("".equals(attr)) {
                nullValue.append(v).append(" - ");
            }
        }

        return nullValue.append("[NOT Default AXML!]").toString();
    }

    private void parseTagAction(AXMLParser parser, ArrayList<String> act) {
        for (int i = 0; i != parser.getAttributeCount(); ++i) {
            String v = makeAttributeValue(parser, i);
            final String attr = getNamespacePrefix(parser.getAttributeNamespace(i)) + parser.getAttributeName(i);
            if ("android:name".equals(attr)) {
                act.add(v);
            }
        }
    }

    private void parseTagCategory(AXMLParser parser, ArrayList<String> intent) {
        for (int i = 0; i != parser.getAttributeCount(); ++i) {
            final String v = makeAttributeValue(parser, i);
            final String attr = getNamespacePrefix(parser.getAttributeNamespace(i)) + parser.getAttributeName(i);
            if ("android:name".equals(attr)) {
                intent.add(v);
            }
        }
    }

    //?
    private String makeAttributeValue(AXMLParser parser, int index) {
        final int type = parser.getAttributeValueType(index);
        final int data = parser.getAttributeValue(index);

        if (type == TYPE_STRING) {
            return parser.getAttributeValueString(index);
        }
        if (type == TYPE_ATTRIBUTE) {
            return String.format("?%s%08X", getPackage(data), data);
        }
        if (type == TYPE_REFERENCE) {
            if (data >>> 24 == 1 || m_arsc == null)
                return String.format("@%s%08X", getPackage(data), data);

            String name = new ARSCParser().parser(m_arsc, data);
            if (name != null)
                return name;
            else
                return String.format("@%08X", data);
        }
        if (type == TYPE_FLOAT) {
            return String.valueOf(Float.intBitsToFloat(data));
        }
        if (type == TYPE_INT_HEX) {
            return String.format("0x%08X", data);
        }
        if (type == TYPE_INT_BOOLEAN) {
            return data != 0 ? "true" : "false";
        }
        if (type == TYPE_DIMENSION) {
            return Float.toString(complexToFloat(data)) + DIMENSION_UNITS[data & COMPLEX_UNIT_MASK];
        }
        if (type == TYPE_FRACTION) {
            return Float.toString(complexToFloat(data)) + FRACTION_UNITS[data & COMPLEX_UNIT_MASK];
        }
        if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) {
            return String.format("#%08X", data);
        }
        if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
            return String.valueOf(data);
        }
        return String.format("<0x%X, type 0x%02X>", data, type);

    }

}