package org.bbs.felix.util; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Iterator; import org.bbs.felix.util.PackageParser.PackageInfoX.ActivityInfoX; import org.bbs.felix.util.PackageParser.PackageInfoX.ApplicationInfoX; import org.bbs.felix.util.PackageParser.PackageInfoX.IntentInfoX; import org.bbs.felix.util.PackageParser.PackageInfoX.UsesSdkX; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.annotation.SuppressLint; import android.content.Context; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; import android.content.res.AssetManager; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.WindowManager; @SuppressLint("NewApi") public class PackageParser { private static final String ATTR_META_DATA = "meta-data"; private static final String ATTR_BANNER = "banner"; private static final String ATTR_LOGO = "logo"; private static final String TAG = PackageParser.class.getSimpleName(); private static final String TAG_USES_SDK = "uses-sdk"; private static final String ATTR_RESOURCE = "resource"; private static final String ATTR_VALUE = "value"; private static final String ATTR_ICON = "icon"; private static final String ATTR_THEME = "theme"; private static final String ATTR_LABEL = "label"; private static final String TAG_CATEGORY = "category"; private static final String TAG_INTENT_FILTER = "intent-filter"; private static final String TAG_ACTION = "action"; private static final String TAG_ACTIVITY = "activity"; private static final String ATTR_VERSION_NAME = "versionName"; private static final String ANDROID_NS = "http://schemas.android.com/apk/res/android"; private static final String ATTR_VERSION_CODE = "versionCode"; private static final String TAG_APPLICATION = "application"; private static final String ATTR_NAME = "name"; private static final String ATTR_PACKAGE = "package"; private static final String TAG_MANIFEST = "manifest"; public static PackageInfoX parseAPk(Context context, String apkFile) { PackageInfoX info = new PackageInfoX(); info.mApkPath = apkFile; AssetManager assets; XmlResourceParser parser = null; try { assets = AssetManager.class.getConstructor(null).newInstance(null); Method method = assets.getClass().getMethod("addAssetPath", new Class[] { String.class }); int cookie = (Integer) method.invoke(assets, apkFile); parser = assets .openXmlResourceParser(cookie, "AndroidManifest.xml"); parseApk(parser, info); resolveParsedApk(info); // parser = assets.openXmlResourceParser(cookie, "AndroidManifest.xml"); // dumpParser(parser); // info.dump(); return info; } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block // e.printStackTrace(); // } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } private static void resolveParsedApk(PackageInfoX info) { ApplicationInfo appInfo = info.applicationInfo; appInfo.packageName = info.packageName; if (!TextUtils.isEmpty(appInfo.name)) { if (!appInfo.name.contains(".")) { appInfo.name = appInfo.packageName + "." + appInfo.name; } else if (appInfo.name.startsWith(".")) { appInfo.name = appInfo.packageName + "" + appInfo.name; } } appInfo.className = appInfo.name; if (info.mUsesSdk != null) { UsesSdkX sdk = info.mUsesSdk; if (sdk.mMaxSdkVersion > 0) { appInfo.targetSdkVersion = sdk.mTargetSdkVersion; } } if (info.activities != null && info.activities.length > 0) { for (ActivityInfo a : info.activities) { ActivityInfoX aX = (ActivityInfoX) a; if (aX.theme == 0 && appInfo.theme > 0) { aX.theme = appInfo.theme; } aX.mApkPath = info.mApkPath; aX.labelRes = aX.labelRes != 0 ? aX.labelRes : appInfo.labelRes; aX.nonLocalizedLabel = !TextUtils.isEmpty(aX.nonLocalizedLabel) ? aX.nonLocalizedLabel : appInfo.nonLocalizedLabel; aX.packageName = appInfo.packageName; if (!TextUtils.isEmpty(aX.name) && (aX.name.startsWith(".") || !aX.name.contains("."))) { String D = !aX.name.contains(".") ? "." : ""; aX.name = appInfo.packageName + D + aX.name; } } } } private static void parseApk(XmlResourceParser parser, PackageInfoX info) { int eventType; try { eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { String tag = parser.getName(); if (eventType == XmlPullParser.START_DOCUMENT) { } else if (eventType == XmlPullParser.START_TAG) { if (TAG_MANIFEST.equals(tag)) { parserPackage(parser, info); } } else if (eventType == XmlPullParser.END_TAG) { } else if (eventType == XmlPullParser.TEXT) { } final int attCount = parser.getAttributeCount(); for (int i = 0; i < attCount; i++) { String attName = parser.getAttributeName(i); String attValue = parser.getAttributeValue(i); } eventType = parser.next(); } } catch (XmlPullParserException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static void parserPackage(XmlResourceParser parser, PackageInfoX info) throws XmlPullParserException, IOException { // parse attr final int attCount = parser.getAttributeCount(); for (int i = 0; i < attCount; i++) { String attName = parser.getAttributeName(i); String attValue = parser.getAttributeValue(i); if (ATTR_PACKAGE.equals(attName)) { info.packageName = attValue; } else if (ATTR_VERSION_NAME.equals(attName)) { info.versionName = attName; } else if (ATTR_VERSION_CODE.equals(attName)) { info.versionCode = Integer.parseInt(attValue); } } // parse sub-element int type; int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); if (TAG_APPLICATION.equals(tagName)) { parserApplication(parser, info); } else if (TAG_USES_SDK.equals(tagName)) { parserUsesSdk(parser, info); } } } private static void parserUsesSdk(XmlResourceParser parser, PackageInfoX info) {// parse attr UsesSdkX sdk = new UsesSdkX(); final int attCount = parser.getAttributeCount(); for (int i = 0; i < attCount; i++) { String attName = parser.getAttributeName(i); String attValue = parser.getAttributeValue(i); if ("minSdkVersion".equals(attName)) { sdk.mMinSdkVersion = Integer.parseInt(attValue); } else if ("maxSdkVersion".equals(attName)) { sdk.mMaxSdkVersion = Integer.parseInt(attValue); } else if ("targetSdkVersion".equals(attName)) { sdk.mTargetSdkVersion = Integer.parseInt(attValue); } } if (sdk.mMaxSdkVersion == 0) { sdk.mMaxSdkVersion = sdk.mTargetSdkVersion; } info.mUsesSdk = sdk; } private static void parserApplication(XmlResourceParser parser, PackageInfoX info) throws XmlPullParserException, IOException { info.applicationInfo = new ApplicationInfoX(); ApplicationInfoX app = (ApplicationInfoX) info.applicationInfo; // parse attr final int attCount = parser.getAttributeCount(); for (int i = 0; i < attCount; i++) { String attName = parser.getAttributeName(i); String attValue = parser.getAttributeValue(i); if (ATTR_THEME.equals(attName)) { app.theme = Integer.parseInt(attValue.substring(1)); } } parsePackageItem(parser, app); // parse sub-element int type; int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); if (TAG_ACTIVITY.equals(tagName)) { parserActivity(parser, info); } else if (ATTR_META_DATA.equals(tagName)) { if (info.applicationInfo == null){ info.applicationInfo = new ApplicationInfoX(); } if (info.applicationInfo.metaData == null) { info.applicationInfo.metaData = new Bundle(); } parserMetaData(parser, info.applicationInfo.metaData); } } } private static void parsePackageItem(XmlResourceParser parser, PackageItemInfo info) { boolean hasLabel = false; final int attCount = parser.getAttributeCount(); for (int i = 0; i < attCount; i++) { String attName = parser.getAttributeName(i); String attValue = parser.getAttributeValue(i); if (ATTR_NAME.equals(attName)) { String cName = attValue; info.name = cName; } else if (ATTR_LABEL.equals(attName)) { hasLabel = true; if (attValue.startsWith("@")) { info.labelRes = Integer.parseInt(attValue.substring(1)); } else { info.nonLocalizedLabel = attValue; } } else if (ATTR_ICON.equals(attName)) { if (attValue.startsWith("@")) { info.icon = Integer.parseInt(attValue.substring(1)); } } else if (ATTR_LOGO.equals(attName)) { if (attValue.startsWith("@")) { info.logo = Integer.parseInt(attValue.substring(1)); } }else if (ATTR_BANNER.equals(attName)) { if (attValue.startsWith("@")) { info.logo = Integer.parseInt(attValue.substring(1)); } } } } private static void parserMetaData(XmlResourceParser parser, Bundle metaData) { String key = null; String value = null; final int attCount = parser.getAttributeCount(); for (int i = 0; i < attCount; i++) { String attName = parser.getAttributeName(i); String attValue = parser.getAttributeValue(i); if (ATTR_NAME.equals(attName)) { key = attValue; } else if (ATTR_VALUE.equals(attName)) { value = attValue; } else if (ATTR_RESOURCE.equals(attName)) { if (attValue.startsWith("@")) { value = attValue; } } } metaData.putString(key, value); } private static void parserActivity(XmlResourceParser parser, PackageInfoX info) throws XmlPullParserException, IOException { ActivityInfoX a = new ActivityInfoX(); a.mApkPath = info.mApkPath; a.applicationInfo = info.applicationInfo; // parse attr final int attCount = parser.getAttributeCount(); boolean hasLabel = false; for (int i = 0; i < attCount; i++) { String attName = parser.getAttributeName(i); String attValue = parser.getAttributeValue(i); if (ATTR_THEME.equals(attName)) { a.theme = Integer.parseInt(attValue.substring(1)); } } parsePackageItem(parser, a); // a.mPackageClassName = info.applicationInfo != null ? info.applicationInfo.className : ""; // a.applicationInfo = info.applicationInfo; // // if (a.theme == 0) { // a.theme = info.applicationInfo.theme; // } // parse sub-element int type; int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); if (TAG_INTENT_FILTER.equals(tagName)) { parserIntentFilter(parser, info, a); } else if (ATTR_META_DATA.equals(tagName)) { if (a.metaData == null) { a.metaData = new Bundle(); } parserMetaData(parser, a.metaData); } } if (info.activities == null) { info.activities = new ActivityInfoX[1]; info.activities[0] = a; } else { int len = info.activities.length; ActivityInfoX[] as = new ActivityInfoX[len + 1]; System.arraycopy(info.activities, 0, as, 0, len); as[len] = a; info.activities = as; } } private static void parserIntentFilter(XmlResourceParser parser, PackageInfoX info, ActivityInfoX a) throws XmlPullParserException, IOException { IntentInfoX i = new IntentInfoX(); // parse attr // parse sub-element int type; int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); if (TAG_ACTION.equals(tagName)) { parserAction(parser, info, i); } else if (TAG_CATEGORY.equals(tagName)) { parseCategory(parser, info, i); } } if (a.mIntents == null) { a.mIntents = new IntentInfoX[1]; a.mIntents[0] = i; } else { int len = a.mIntents.length; IntentInfoX[] as = new IntentInfoX[len + 1]; System.arraycopy(a.mIntents, 0, as, 0, len); as[len] = i; a.mIntents = as; } } private static void parseCategory(XmlResourceParser parser, PackageInfoX info, IntentInfoX intentInfo) { // parse attr final int attCount = parser.getAttributeCount(); for (int i = 0; i < attCount; i++) { String attName = parser.getAttributeName(i); String attValue = parser.getAttributeValue(i); if (ATTR_NAME.equals(attName)) { String category = attValue; intentInfo.addCategory(category); } } } private static void parserAction(XmlResourceParser parser, PackageInfoX info, IntentInfoX intentInfo) { // parse attr final int attCount = parser.getAttributeCount(); for (int i = 0; i < attCount; i++) { String attName = parser.getAttributeName(i); String attValue = parser.getAttributeValue(i); if (ATTR_NAME.equals(attName)) { String action = attValue; intentInfo.addAction(action); } } } private static void dumpParser(XmlResourceParser parser) { int depth = 0; int eventType; try { eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_DOCUMENT) { Log.d(TAG, makePrefix(depth) + ""); } else if (eventType == XmlPullParser.START_TAG) { depth++; Log.d(TAG, makePrefix(depth) + "" + parser.getName()); } else if (eventType == XmlPullParser.END_TAG) { Log.d(TAG, makePrefix(depth) + "" + parser.getName()); depth--; } else if (eventType == XmlPullParser.TEXT) { Log.d(TAG, makePrefix(depth) + "" + parser.getText()); } final int attCount = parser.getAttributeCount(); for (int i = 0; i < attCount; i++) { String attName = parser.getAttributeName(i); String attValue = parser.getAttributeValue(i); Log.d(TAG, makePrefix(depth + 1) + "" + i + " " + attName + " : " + attValue); } eventType = parser.next(); } } catch (XmlPullParserException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static String makePrefix(int depth) { StringBuffer b = new StringBuffer(); for (int i = 0; i < depth; i++) { b.append(" "); } return b.toString(); } /** * all member MUST has a 'm" prefix. * * @author bysong * */ public static class PackageInfoX extends PackageInfo { public static final int DUMP_APPLICATION = 1 << 0; public static final int DUMP_ACTIVITY = 1 << 1; public static final int DUMP_USES_SDK = 1 << 2; public static final int DUMP_ALL = 0xFFFF; public String mApkPath; public UsesSdkX mUsesSdk; public static void dump(int level, Bundle metaData) { if (metaData != null) { Log.d(TAG, makePrefix(level) + "metaData: "); level++; Iterator<String> it = metaData.keySet().iterator(); while (it.hasNext()) { String key = it.next(); Log.d(TAG, makePrefix(level) + key + ": " + metaData.getString(key)); } } } public static class ApplicationInfoX extends ApplicationInfo { public void dump(int level) { Log.d(TAG, makePrefix(level) + "appliction: "); level++; Log.d(TAG, makePrefix(level) + "packageName: " + packageName); Log.d(TAG, makePrefix(level) + "theme : " + theme); PackageInfoX.dump(level, metaData); } } public static class ActivityInfoX extends ActivityInfo implements Parcelable { public IntentInfoX[] mIntents; public String mApkPath; public int describeContents() { return 0; } public void dump(int level) { Log.d(TAG, makePrefix(level) + "activity:"); Log.d(TAG, makePrefix(level + 1) + "name : " + name); Log.d(TAG, makePrefix(level + 1) + "icon : " + icon); Log.d(TAG, makePrefix(level + 1) + "theme: " + theme); PackageInfoX.dump(level + 1, metaData); } public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeParcelableArray(mIntents, flags); } public static final Parcelable.Creator<ActivityInfoX> CREATOR = new Parcelable.Creator<ActivityInfoX>() { public ActivityInfoX createFromParcel(Parcel in) { return new ActivityInfoX(in); } public ActivityInfoX[] newArray(int size) { return new ActivityInfoX[size]; } }; public ActivityInfoX(){ } private ActivityInfoX(Parcel in) { // super(in); // mData = in.readInt(); } } public static class IntentInfoX extends IntentFilter { public String[] mActions; } public static class UsesSdkX { public int mMinSdkVersion; public int mTargetSdkVersion; public int mMaxSdkVersion; public void dump(int level) { Log.d(TAG, makePrefix(level) + "mUseSdk: "); Log.d(TAG, makePrefix(level++) + "mMinSdkVersion: " + mMinSdkVersion); Log.d(TAG, makePrefix(level++) + "mTargetSdkVersion: " + mTargetSdkVersion); Log.d(TAG, makePrefix(level++) + "mMaxSdkVersion: " + mMaxSdkVersion); } } public void dump(){ Log.d(TAG, "dump manefest info:"); dump(0); } public void dump(int level) { Log.d(TAG, makePrefix(level) + "mApkLocation: " + mApkPath); if (mUsesSdk != null) { mUsesSdk.dump(level + 1); } if (applicationInfo != null) { ((ApplicationInfoX)applicationInfo).dump(level + 1); } if (activities != null && activities.length > 0) { Log.d(TAG, makePrefix(level) + "activities: "); for (ActivityInfo a : activities) { ((ActivityInfoX)a).dump(level + 1); } } } } }