/* ** DroidPlugin Project ** ** Copyright(c) 2015 Andy Zhang <zhangyong232@gmail.com> ** ** This file is part of DroidPlugin. ** ** DroidPlugin is free software: you can redistribute it and/or ** modify it under the terms of the GNU Lesser General Public ** License as published by the Free Software Foundation, either ** version 3 of the License, or (at your option) any later version. ** ** DroidPlugin is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** Lesser General Public License for more details. ** ** You should have received a copy of the GNU Lesser General Public ** License along with DroidPlugin. If not, see <http://www.gnu.org/licenses/lgpl.txt> ** **/ package com.morgoo.droidplugin.pm.parser; import android.content.ComponentName; import android.content.Context; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.os.Build; import android.text.TextUtils; import com.morgoo.droidplugin.core.PluginDirHelper; import com.morgoo.droidplugin.reflect.FieldUtils; import com.morgoo.helper.ComponentNameComparator; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.TreeMap; /** * 解析插件apk * <p/> * Created by Andy Zhang(zhangyong232@gmail.com) on 2015/2/13. */ public class PluginPackageParser { private final File mPluginFile; private final PackageParser mParser; private final String mPackageName; private final Context mHostContext; private final PackageInfo mHostPackageInfo; private Map<ComponentName, Object> mActivityObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator()); private Map<ComponentName, Object> mServiceObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator()); private Map<ComponentName, Object> mProviderObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator()); private Map<ComponentName, Object> mReceiversObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator()); private Map<ComponentName, Object> mInstrumentationObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator()); private Map<ComponentName, Object> mPermissionsObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator()); private Map<ComponentName, Object> mPermissionGroupObjCache = new TreeMap<ComponentName, Object>(new ComponentNameComparator()); private ArrayList<String> mRequestedPermissionsCache = new ArrayList<String>(); private Map<ComponentName, List<IntentFilter>> mActivityIntentFilterCache = new TreeMap<ComponentName, List<IntentFilter>>(new ComponentNameComparator()); private Map<ComponentName, List<IntentFilter>> mServiceIntentFilterCache = new TreeMap<ComponentName, List<IntentFilter>>(new ComponentNameComparator()); private Map<ComponentName, List<IntentFilter>> mProviderIntentFilterCache = new TreeMap<ComponentName, List<IntentFilter>>(new ComponentNameComparator()); private Map<ComponentName, List<IntentFilter>> mReceiverIntentFilterCache = new TreeMap<ComponentName, List<IntentFilter>>(new ComponentNameComparator()); private Map<ComponentName, ActivityInfo> mActivityInfoCache = new TreeMap<ComponentName, ActivityInfo>(new ComponentNameComparator()); private Map<ComponentName, ServiceInfo> mServiceInfoCache = new TreeMap<ComponentName, ServiceInfo>(new ComponentNameComparator()); private Map<ComponentName, ProviderInfo> mProviderInfoCache = new TreeMap<ComponentName, ProviderInfo>(new ComponentNameComparator()); private Map<ComponentName, ActivityInfo> mReceiversInfoCache = new TreeMap<ComponentName, ActivityInfo>(new ComponentNameComparator()); private Map<ComponentName, InstrumentationInfo> mInstrumentationInfoCache = new TreeMap<ComponentName, InstrumentationInfo>(new ComponentNameComparator()); private Map<ComponentName, PermissionGroupInfo> mPermissionGroupInfoCache = new TreeMap<ComponentName, PermissionGroupInfo>(new ComponentNameComparator()); private Map<ComponentName, PermissionInfo> mPermissionsInfoCache = new TreeMap<ComponentName, PermissionInfo>(new ComponentNameComparator()); public PluginPackageParser(Context hostContext, File pluginFile) throws Exception { mHostContext = hostContext; mPluginFile = pluginFile; mParser = PackageParser.newPluginParser(hostContext); mParser.parsePackage(pluginFile, 0); mPackageName = mParser.getPackageName(); mHostPackageInfo = mHostContext.getPackageManager().getPackageInfo(mHostContext.getPackageName(), 0); List datas = mParser.getActivities(); for (Object data : datas) { ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(data)); synchronized (mActivityObjCache) { mActivityObjCache.put(componentName, data); } synchronized (mActivityInfoCache) { ActivityInfo value = mParser.generateActivityInfo(data, 0); fixApplicationInfo(value.applicationInfo); if (TextUtils.isEmpty(value.processName)) { value.processName = value.packageName; } mActivityInfoCache.put(componentName, value); } List<IntentFilter> filters = mParser.readIntentFilterFromComponent(data); synchronized (mActivityIntentFilterCache) { mActivityIntentFilterCache.remove(componentName); mActivityIntentFilterCache.put(componentName, new ArrayList<IntentFilter>(filters)); } } datas = mParser.getServices(); for (Object data : datas) { ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(data)); synchronized (mServiceObjCache) { mServiceObjCache.put(componentName, data); } synchronized (mServiceInfoCache) { ServiceInfo value = mParser.generateServiceInfo(data, 0); fixApplicationInfo(value.applicationInfo); if (TextUtils.isEmpty(value.processName)) { value.processName = value.packageName; } mServiceInfoCache.put(componentName, value); } List<IntentFilter> filters = mParser.readIntentFilterFromComponent(data); synchronized (mServiceIntentFilterCache) { mServiceIntentFilterCache.remove(componentName); mServiceIntentFilterCache.put(componentName, new ArrayList<IntentFilter>(filters)); } } datas = mParser.getProviders(); for (Object data : datas) { ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(data)); synchronized (mProviderObjCache) { mProviderObjCache.put(componentName, data); } synchronized (mProviderInfoCache) { ProviderInfo value = mParser.generateProviderInfo(data, 0); fixApplicationInfo(value.applicationInfo); if (TextUtils.isEmpty(value.processName)) { value.processName = value.packageName; } mProviderInfoCache.put(componentName, value); } List<IntentFilter> filters = mParser.readIntentFilterFromComponent(data); synchronized (mProviderIntentFilterCache) { mProviderIntentFilterCache.remove(componentName); mProviderIntentFilterCache.put(componentName, new ArrayList<IntentFilter>(filters)); } } datas = mParser.getReceivers(); for (Object data : datas) { ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(data)); synchronized (mReceiversObjCache) { mReceiversObjCache.put(componentName, data); } synchronized (mReceiversInfoCache) { ActivityInfo value = mParser.generateReceiverInfo(data, 0); fixApplicationInfo(value.applicationInfo); if (TextUtils.isEmpty(value.processName)) { value.processName = value.packageName; } mReceiversInfoCache.put(componentName, value); } List<IntentFilter> filters = mParser.readIntentFilterFromComponent(data); synchronized (mReceiverIntentFilterCache) { mReceiverIntentFilterCache.remove(componentName); mReceiverIntentFilterCache.put(componentName, new ArrayList<IntentFilter>(filters)); } } datas = mParser.getInstrumentations(); for (Object data : datas) { ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(data)); synchronized (mInstrumentationObjCache) { mInstrumentationObjCache.put(componentName, data); } } datas = mParser.getPermissions(); for (Object data : datas) { String cls = mParser.readNameFromComponent(data); if (cls != null) { ComponentName componentName = new ComponentName(mPackageName, cls); synchronized (mPermissionsObjCache) { mPermissionsObjCache.put(componentName, data); } synchronized (mPermissionsInfoCache) { PermissionInfo value = mParser.generatePermissionInfo(data, 0); mPermissionsInfoCache.put(componentName, value); } } } datas = mParser.getPermissionGroups(); for (Object data : datas) { ComponentName componentName = new ComponentName(mPackageName, mParser.readNameFromComponent(data)); synchronized (mPermissionGroupObjCache) { mPermissionGroupObjCache.put(componentName, data); } } List<String> requestedPermissions = mParser.getRequestedPermissions(); if (requestedPermissions != null && requestedPermissions.size() > 0) { synchronized (mRequestedPermissionsCache) { mRequestedPermissionsCache.addAll(requestedPermissions); } } } public File getPluginFile() { return mPluginFile; } public void collectCertificates(int flags) throws Exception { mParser.collectCertificates(flags); } public List<IntentFilter> getActivityIntentFilter(ComponentName className) { synchronized (mActivityIntentFilterCache) { return mActivityIntentFilterCache.get(className); } } public List<IntentFilter> getServiceIntentFilter(ComponentName className) { synchronized (mServiceIntentFilterCache) { return mServiceIntentFilterCache.get(className); } } public List<IntentFilter> getProviderIntentFilter(ComponentName className) { synchronized (mProviderObjCache) { return mProviderIntentFilterCache.get(className); } } public ActivityInfo getActivityInfo(ComponentName className, int flags) throws Exception { Object data; synchronized (mActivityObjCache) { data = mActivityObjCache.get(className); } if (data != null) { ActivityInfo activityInfo = mParser.generateActivityInfo(data, flags); fixApplicationInfo(activityInfo.applicationInfo); if (TextUtils.isEmpty(activityInfo.processName)) { activityInfo.processName = activityInfo.packageName; } return activityInfo; } return null; } public ServiceInfo getServiceInfo(ComponentName className, int flags) throws Exception { Object data; synchronized (mServiceObjCache) { data = mServiceObjCache.get(className); } if (data != null) { ServiceInfo serviceInfo = mParser.generateServiceInfo(data, flags); fixApplicationInfo(serviceInfo.applicationInfo); if (TextUtils.isEmpty(serviceInfo.processName)) { serviceInfo.processName = serviceInfo.packageName; } return serviceInfo; } return null; } public ActivityInfo getReceiverInfo(ComponentName className, int flags) throws Exception { Object data; synchronized (mReceiversObjCache) { data = mReceiversObjCache.get(className); } if (data != null) { ActivityInfo activityInfo = mParser.generateReceiverInfo(data, flags); fixApplicationInfo(activityInfo.applicationInfo); if (TextUtils.isEmpty(activityInfo.processName)) { activityInfo.processName = activityInfo.packageName; } return activityInfo; } return null; } public ProviderInfo getProviderInfo(ComponentName className, int flags) throws Exception { Object data; synchronized (mProviderObjCache) { data = mProviderObjCache.get(className); } if (data != null) { ProviderInfo providerInfo = mParser.generateProviderInfo(data, flags); fixApplicationInfo(providerInfo.applicationInfo); if (TextUtils.isEmpty(providerInfo.processName)) { providerInfo.processName = providerInfo.packageName; } return providerInfo; } return null; } public InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags) throws Exception { Object data; synchronized (mInstrumentationObjCache) { data = mInstrumentationObjCache.get(className); } if (data != null) { return mParser.generateInstrumentationInfo(data, flags); } return null; } public ApplicationInfo getApplicationInfo(int flags) throws Exception { ApplicationInfo applicationInfo = mParser.generateApplicationInfo(flags); fixApplicationInfo(applicationInfo); if (TextUtils.isEmpty(applicationInfo.processName)) { applicationInfo.processName = applicationInfo.packageName; } return applicationInfo; } public PermissionGroupInfo getPermissionGroupInfo(ComponentName className, int flags) throws Exception { Object data; synchronized (mPermissionGroupObjCache) { data = mPermissionGroupObjCache.get(className); } if (data != null) { return mParser.generatePermissionGroupInfo(data, flags); } return null; } public PermissionInfo getPermissionInfo(ComponentName className, int flags) throws Exception { Object data; synchronized (mPermissionsObjCache) { data = mPermissionsObjCache.get(className); } if (data != null) { return mParser.generatePermissionInfo(data, flags); } return null; } public PackageInfo getPackageInfo(int flags) throws Exception { PackageInfo packageInfo = mParser.generatePackageInfo(mHostPackageInfo.gids, flags, mPluginFile.lastModified(), mPluginFile.lastModified(), new HashSet<String>(getRequestedPermissions())); fixPackageInfo(packageInfo); return packageInfo; } public List<ActivityInfo> getActivities() throws Exception { return new ArrayList<ActivityInfo>(mActivityInfoCache.values()); } public List<ServiceInfo> getServices() throws Exception { return new ArrayList<ServiceInfo>(mServiceInfoCache.values()); } public List<ProviderInfo> getProviders() throws Exception { return new ArrayList<ProviderInfo>(mProviderInfoCache.values()); } public List<ActivityInfo> getReceivers() throws Exception { return new ArrayList<ActivityInfo>(mReceiversInfoCache.values()); } public List<PermissionInfo> getPermissions() throws Exception { return new ArrayList<PermissionInfo>(mPermissionsInfoCache.values()); } public List<PermissionGroupInfo> getPermissionGroups() throws Exception { return new ArrayList<PermissionGroupInfo>(mPermissionGroupInfoCache.values()); } public List<InstrumentationInfo> getInstrumentationInfos() { return new ArrayList<InstrumentationInfo>(mInstrumentationInfoCache.values()); } public List<String> getRequestedPermissions() throws Exception { synchronized (mRequestedPermissionsCache) { return new ArrayList<String>(mRequestedPermissionsCache); } } public String getPackageName() throws Exception { return mPackageName; } private ApplicationInfo fixApplicationInfo(ApplicationInfo applicationInfo) { if (applicationInfo.sourceDir == null) { applicationInfo.sourceDir = mPluginFile.getPath(); } if (applicationInfo.publicSourceDir == null) { applicationInfo.publicSourceDir = mPluginFile.getPath(); } if (applicationInfo.dataDir == null) { applicationInfo.dataDir = PluginDirHelper.getPluginDataDir(mHostContext, applicationInfo.packageName); } try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (FieldUtils.readField(applicationInfo, "scanSourceDir", true) == null) { FieldUtils.writeField(applicationInfo, "scanSourceDir", applicationInfo.dataDir, true); } } } catch (Throwable e) { //Do nothing } try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (FieldUtils.readField(applicationInfo, "scanPublicSourceDir", true) == null) { FieldUtils.writeField(applicationInfo, "scanPublicSourceDir", applicationInfo.dataDir, true); } } } catch (Throwable e) { //Do nothing } applicationInfo.uid = mHostPackageInfo.applicationInfo.uid; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { if (applicationInfo.nativeLibraryDir == null) { applicationInfo.nativeLibraryDir = PluginDirHelper.getPluginNativeLibraryDir(mHostContext, applicationInfo.packageName); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (applicationInfo.splitSourceDirs == null) { applicationInfo.splitSourceDirs = new String[]{mPluginFile.getPath()}; } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (applicationInfo.splitPublicSourceDirs == null) { applicationInfo.splitPublicSourceDirs = new String[]{mPluginFile.getPath()}; } } // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // if (applicationInfo.primaryCpuAbi == null) { // applicationInfo.primaryCpuAbi = mHostPackageInfo.applicationInfo.primaryCpuAbi; // } // } // // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // if (applicationInfo.secondaryCpuAbi == null) { // applicationInfo.secondaryCpuAbi = mHostPackageInfo.applicationInfo.secondaryCpuAbi; // } // } if (TextUtils.isEmpty(applicationInfo.processName)) { applicationInfo.processName = applicationInfo.packageName; } return applicationInfo; } private PackageInfo fixPackageInfo(PackageInfo packageInfo) { packageInfo.gids = mHostPackageInfo.gids; fixApplicationInfo(packageInfo.applicationInfo); return packageInfo; } public Map<ActivityInfo, List<IntentFilter>> getReceiverIntentFilter() { synchronized (mReceiverIntentFilterCache) { Map<ActivityInfo, List<IntentFilter>> map = new HashMap<ActivityInfo, List<IntentFilter>>(); for (ComponentName componentName : mReceiverIntentFilterCache.keySet()) { map.put(mReceiversInfoCache.get(componentName), mReceiverIntentFilterCache.get(componentName)); } return map; } } public List<IntentFilter> getReceiverIntentFilter(ActivityInfo info) { synchronized (mReceiverIntentFilterCache) { for (ComponentName componentName : mReceiverIntentFilterCache.keySet()) { if (TextUtils.equals(info.name, mReceiversInfoCache.get(componentName).name)) { return mReceiverIntentFilterCache.get(componentName); } } } return null; } public void writeSignature(Signature[] signatures) throws Exception { if (signatures != null) { mParser.writeSignature(signatures); } } }