/*
** 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;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.Binder;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.RemoteException;
import android.text.TextUtils;
import com.morgoo.droidplugin.am.BaseActivityManagerService;
import com.morgoo.droidplugin.am.MyActivityManagerService;
import com.morgoo.droidplugin.core.PluginClassLoader;
import com.morgoo.droidplugin.core.PluginDirHelper;
import com.morgoo.droidplugin.pm.parser.IntentMatcher;
import com.morgoo.droidplugin.pm.parser.PluginPackageParser;
import com.morgoo.helper.Log;
import com.morgoo.helper.Utils;
import com.morgoo.helper.compat.NativeLibraryHelperCompat;
import com.morgoo.helper.compat.PackageManagerCompat;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 此服务模仿系统的PackageManagerService,提供对插件简单的管理服务。
* Created by Andy Zhang(zhangyong232@gmail.com) on 2015/2/12.
*/
public class IPluginManagerImpl extends IPluginManager.Stub {
private static final String TAG = IPluginManagerImpl.class.getSimpleName();
private Map<String, PluginPackageParser> mPluginCache = Collections.synchronizedMap(new HashMap<String, PluginPackageParser>(20));
private Context mContext;
private AtomicBoolean mHasLoadedOk = new AtomicBoolean(false);
private final Object mLock = new Object();
private BaseActivityManagerService mActivityManagerService;
private Set<String> mHostRequestedPermission = new HashSet<String>(10);
private Map<String, Signature[]> mSignatureCache = new HashMap<String, Signature[]>();
public IPluginManagerImpl(Context context) {
mContext = context;
mActivityManagerService = new MyActivityManagerService(mContext);
}
public void onCreate() {
new Thread() {
@Override
public void run() {
onCreateInner();
}
}.start();
}
private void onCreateInner() {
loadAllPlugin(mContext);
loadHostRequestedPermission();
try {
mHasLoadedOk.set(true);
synchronized (mLock) {
mLock.notifyAll();
}
} catch (Exception e) {
}
}
private void loadHostRequestedPermission() {
try {
mHostRequestedPermission.clear();
PackageManager pm = mContext.getPackageManager();
PackageInfo pms = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_PERMISSIONS);
if (pms != null && pms.requestedPermissions != null && pms.requestedPermissions.length > 0) {
for (String requestedPermission : pms.requestedPermissions) {
mHostRequestedPermission.add(requestedPermission);
}
}
} catch (Exception e) {
}
}
private void loadAllPlugin(Context context) {
long b = System.currentTimeMillis();
ArrayList<File> apkfiles = null;
try {
apkfiles = new ArrayList<File>();
File baseDir = new File(PluginDirHelper.getBaseDir(context));
File[] dirs = baseDir.listFiles();
for (File dir : dirs) {
if (dir.isDirectory()) {
File file = new File(dir, "apk/base-1.apk");
if (file.exists()) {
apkfiles.add(file);
}
}
}
} catch (Exception e) {
Log.e(TAG, "scan a apk file error", e);
}
Log.i(TAG, "Search apk cost %s ms", (System.currentTimeMillis() - b));
b = System.currentTimeMillis();
if (apkfiles != null && apkfiles.size() > 0) {
for (File pluginFile : apkfiles) {
long b1 = System.currentTimeMillis();
try {
PluginPackageParser pluginPackageParser = new PluginPackageParser(mContext, pluginFile);
Signature[] signatures = readSignatures(pluginPackageParser.getPackageName());
if (signatures == null || signatures.length <= 0) {
pluginPackageParser.collectCertificates(0);
PackageInfo info = pluginPackageParser.getPackageInfo(PackageManager.GET_SIGNATURES);
saveSignatures(info);
} else {
mSignatureCache.put(pluginPackageParser.getPackageName(), signatures);
pluginPackageParser.writeSignature(signatures);
}
if (!mPluginCache.containsKey(pluginPackageParser.getPackageName())) {
mPluginCache.put(pluginPackageParser.getPackageName(), pluginPackageParser);
}
} catch (Throwable e) {
Log.e(TAG, "parse a apk file error %s", e, pluginFile.getPath());
} finally {
Log.i(TAG, "Parse %s apk cost %s ms", pluginFile.getPath(), (System.currentTimeMillis() - b1));
}
}
}
Log.i(TAG, "Parse all apk cost %s ms", (System.currentTimeMillis() - b));
b = System.currentTimeMillis();
try {
mActivityManagerService.onCreate(IPluginManagerImpl.this);
} catch (Throwable e) {
Log.e(TAG, "mActivityManagerService.onCreate", e);
}
Log.i(TAG, "ActivityManagerService.onCreate %s ms", (System.currentTimeMillis() - b));
}
private void enforcePluginFileExists() throws RemoteException {
List<String> removedPkg = new ArrayList<>();
for (String pkg : mPluginCache.keySet()) {
PluginPackageParser parser = mPluginCache.get(pkg);
File pluginFile = parser.getPluginFile();
if (pluginFile != null && pluginFile.exists()) {
//DO NOTHING
} else {
removedPkg.add(pkg);
}
}
for (String pkg : removedPkg) {
deletePackage(pkg, 0);
}
}
@Override
public boolean waitForReady() {
waitForReadyInner();
return true;
}
private void waitForReadyInner() {
if (!mHasLoadedOk.get()) {
synchronized (mLock) {
try {
mLock.wait();
} catch (InterruptedException e) {
}
}
}
}
private void handleException(Exception e) throws RemoteException {
RemoteException remoteException;
if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
remoteException = new RemoteException(e.getMessage());
remoteException.initCause(e);
remoteException.setStackTrace(e.getStackTrace());
} else {
remoteException = new RemoteException();
remoteException.initCause(e);
remoteException.setStackTrace(e.getStackTrace());
}
throw remoteException;
}
@Override
public PackageInfo getPackageInfo(String packageName, int flags) throws RemoteException {
waitForReadyInner();
try {
String pkg = getAndCheckCallingPkg(packageName);
if (pkg != null && !TextUtils.equals(packageName, mContext.getPackageName())) {
enforcePluginFileExists();
PluginPackageParser parser = mPluginCache.get(pkg);
if (parser != null) {
PackageInfo packageInfo = parser.getPackageInfo(flags);
if (packageInfo != null && (flags & PackageManager.GET_SIGNATURES) != 0 && packageInfo.signatures == null) {
packageInfo.signatures = mSignatureCache.get(packageName);
}
return packageInfo;
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public boolean isPluginPackage(String packageName) throws RemoteException {
waitForReadyInner();
enforcePluginFileExists();
return mPluginCache.containsKey(packageName);
}
@Override
public ActivityInfo getActivityInfo(ComponentName className, int flags) throws RemoteException {
waitForReadyInner();
try {
String pkg = getAndCheckCallingPkg(className.getPackageName());
if (pkg != null) {
enforcePluginFileExists();
PluginPackageParser parser = mPluginCache.get(className.getPackageName());
if (parser != null) {
return parser.getActivityInfo(className, flags);
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public ActivityInfo getReceiverInfo(ComponentName className, int flags) throws RemoteException {
waitForReadyInner();
try {
String pkg = getAndCheckCallingPkg(className.getPackageName());
if (pkg != null) {
enforcePluginFileExists();
PluginPackageParser parser = mPluginCache.get(className.getPackageName());
if (parser != null) {
return parser.getReceiverInfo(className, flags);
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public ServiceInfo getServiceInfo(ComponentName className, int flags) throws RemoteException {
waitForReadyInner();
try {
String pkg = getAndCheckCallingPkg(className.getPackageName());
if (pkg != null) {
enforcePluginFileExists();
PluginPackageParser parser = mPluginCache.get(className.getPackageName());
if (parser != null) {
return parser.getServiceInfo(className, flags);
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public ProviderInfo getProviderInfo(ComponentName className, int flags) throws RemoteException {
waitForReadyInner();
try {
String pkg = getAndCheckCallingPkg(className.getPackageName());
if (pkg != null) {
enforcePluginFileExists();
PluginPackageParser parser = mPluginCache.get(className.getPackageName());
if (parser != null) {
return parser.getProviderInfo(className, flags);
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
private boolean shouldNotBlockOtherInfo() {
return true;
// int pid = Binder.getCallingPid();
// if (pid == android.os.Process.myPid()) {
// return true;
// } else {
// List<String> pkgs = mActivityManagerService.getPackageNamesByPid(pid);
// if (pkgs != null && pkgs.size() > 0 && !pkgs.contains(mContext.getPackageName())) {
// return false;
// } else {
// return true;
// }
// }
}
private String getAndCheckCallingPkg(String pkg) {
return pkg;
// if (shouldNotBlockOtherInfo()) {
// return pkg;
// } else {
// if (!pkgInPid(Binder.getCallingPid(), pkg)) {
// return null;
// } else {
// return pkg;
// }
// }
}
private boolean pkgInPid(int pid, String pkg) {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(pid);
if (pkgs != null && pkgs.size() > 0) {
return pkgs.contains(pkg);
} else {
return true;
}
}
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
if (shouldNotBlockOtherInfo()) {
List<ResolveInfo> infos = IntentMatcher.resolveIntent(mContext, mPluginCache, intent, resolvedType, flags);
if (infos != null && infos.size() > 0) {
return IntentMatcher.findBest(infos);
}
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
List<ResolveInfo> infos = new ArrayList<ResolveInfo>();
for (String pkg : pkgs) {
intent.setPackage(pkg);
List<ResolveInfo> list = IntentMatcher.resolveIntent(mContext, mPluginCache, intent, resolvedType, flags);
infos.addAll(list);
}
if (infos != null && infos.size() > 0) {
return IntentMatcher.findBest(infos);
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
if (shouldNotBlockOtherInfo()) {
return IntentMatcher.resolveActivityIntent(mContext, mPluginCache, intent, resolvedType, flags);
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
List<ResolveInfo> infos = new ArrayList<ResolveInfo>();
for (String pkg : pkgs) {
intent.setPackage(pkg);
List<ResolveInfo> list = IntentMatcher.resolveActivityIntent(mContext, mPluginCache, intent, resolvedType, flags);
infos.addAll(list);
}
if (infos != null && infos.size() > 0) {
return infos;
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
if (shouldNotBlockOtherInfo()) {
return IntentMatcher.resolveReceiverIntent(mContext, mPluginCache, intent, resolvedType, flags);
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
List<ResolveInfo> infos = new ArrayList<ResolveInfo>();
for (String pkg : pkgs) {
intent.setPackage(pkg);
List<ResolveInfo> list = IntentMatcher.resolveReceiverIntent(mContext, mPluginCache, intent, resolvedType, flags);
infos.addAll(list);
}
if (infos != null && infos.size() > 0) {
return infos;
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
if (shouldNotBlockOtherInfo()) {
List<ResolveInfo> infos = IntentMatcher.resolveServiceIntent(mContext, mPluginCache, intent, resolvedType, flags);
if (infos != null && infos.size() > 0) {
return IntentMatcher.findBest(infos);
}
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
List<ResolveInfo> infos = new ArrayList<ResolveInfo>();
for (String pkg : pkgs) {
intent.setPackage(pkg);
List<ResolveInfo> list = IntentMatcher.resolveServiceIntent(mContext, mPluginCache, intent, resolvedType, flags);
infos.addAll(list);
}
if (infos != null && infos.size() > 0) {
return IntentMatcher.findBest(infos);
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
if (shouldNotBlockOtherInfo()) {
return IntentMatcher.resolveServiceIntent(mContext, mPluginCache, intent, resolvedType, flags);
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
List<ResolveInfo> infos = new ArrayList<ResolveInfo>();
for (String pkg : pkgs) {
intent.setPackage(pkg);
List<ResolveInfo> list = IntentMatcher.resolveServiceIntent(mContext, mPluginCache, intent, resolvedType, flags);
infos.addAll(list);
}
if (infos != null && infos.size() > 0) {
return infos;
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public List<ResolveInfo> queryIntentContentProviders(Intent intent, String resolvedType, int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
if (shouldNotBlockOtherInfo()) {
return IntentMatcher.resolveProviderIntent(mContext, mPluginCache, intent, resolvedType, flags);
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
List<ResolveInfo> infos = new ArrayList<ResolveInfo>();
for (String pkg : pkgs) {
intent.setPackage(pkg);
List<ResolveInfo> list = IntentMatcher.resolveProviderIntent(mContext, mPluginCache, intent, resolvedType, flags);
infos.addAll(list);
}
if (infos != null && infos.size() > 0) {
return infos;
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public List<PackageInfo> getInstalledPackages(int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
List<PackageInfo> infos = new ArrayList<PackageInfo>(mPluginCache.size());
if (shouldNotBlockOtherInfo()) {
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
infos.add(pluginPackageParser.getPackageInfo(flags));
}
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
if (pkgs.contains(pluginPackageParser.getPackageName())) {
infos.add(pluginPackageParser.getPackageInfo(flags));
}
}
}
return infos;
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public List<ApplicationInfo> getInstalledApplications(int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
List<ApplicationInfo> infos = new ArrayList<ApplicationInfo>(mPluginCache.size());
if (shouldNotBlockOtherInfo()) {
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
infos.add(pluginPackageParser.getApplicationInfo(flags));
}
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
if (pkgs.contains(pluginPackageParser.getPackageName())) {
infos.add(pluginPackageParser.getApplicationInfo(flags));
}
}
}
return infos;
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public PermissionInfo getPermissionInfo(String name, int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
if (shouldNotBlockOtherInfo()) {
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
List<PermissionInfo> permissionInfos = pluginPackageParser.getPermissions();
for (PermissionInfo permissionInfo : permissionInfos) {
if (TextUtils.equals(permissionInfo.name, name)) {
return permissionInfo;
}
}
}
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
List<PermissionInfo> permissionInfos = pluginPackageParser.getPermissions();
for (PermissionInfo permissionInfo : permissionInfos) {
if (TextUtils.equals(permissionInfo.name, name) && pkgs.contains(permissionInfo.packageName)) {
return permissionInfo;
}
}
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
List<PermissionInfo> list = new ArrayList<PermissionInfo>();
if (shouldNotBlockOtherInfo()) {
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
List<PermissionInfo> permissionInfos = pluginPackageParser.getPermissions();
for (PermissionInfo permissionInfo : permissionInfos) {
if (TextUtils.equals(permissionInfo.group, group) && !list.contains(permissionInfo)) {
list.add(permissionInfo);
}
}
}
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
List<PermissionInfo> permissionInfos = pluginPackageParser.getPermissions();
for (PermissionInfo permissionInfo : permissionInfos) {
if (pkgs.contains(permissionInfo.packageName) && TextUtils.equals(permissionInfo.group, group) && !list.contains(permissionInfo)) {
list.add(permissionInfo);
}
}
}
}
return list;
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
if (shouldNotBlockOtherInfo()) {
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
List<PermissionGroupInfo> permissionGroupInfos = pluginPackageParser.getPermissionGroups();
for (PermissionGroupInfo permissionGroupInfo : permissionGroupInfos) {
if (TextUtils.equals(permissionGroupInfo.name, name)) {
return permissionGroupInfo;
}
}
}
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
List<PermissionGroupInfo> permissionGroupInfos = pluginPackageParser.getPermissionGroups();
for (PermissionGroupInfo permissionGroupInfo : permissionGroupInfos) {
if (TextUtils.equals(permissionGroupInfo.name, name) && pkgs.contains(permissionGroupInfo.packageName)) {
return permissionGroupInfo;
}
}
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public List<PermissionGroupInfo> getAllPermissionGroups(int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
List<PermissionGroupInfo> list = new ArrayList<PermissionGroupInfo>();
if (shouldNotBlockOtherInfo()) {
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
List<PermissionGroupInfo> permissionGroupInfos = pluginPackageParser.getPermissionGroups();
for (PermissionGroupInfo permissionGroupInfo : permissionGroupInfos) {
if (!list.contains(permissionGroupInfo)) {
list.add(permissionGroupInfo);
}
}
}
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
List<PermissionGroupInfo> permissionGroupInfos = pluginPackageParser.getPermissionGroups();
for (PermissionGroupInfo permissionGroupInfo : permissionGroupInfos) {
if (!list.contains(permissionGroupInfo) && pkgs
.contains(permissionGroupInfo.packageName)) {
list.add(permissionGroupInfo);
}
}
}
}
return list;
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public ProviderInfo resolveContentProvider(String name, int flags) throws RemoteException {
waitForReadyInner();
try {
enforcePluginFileExists();
if (shouldNotBlockOtherInfo()) {
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
List<ProviderInfo> providerInfos = pluginPackageParser.getProviders();
for (ProviderInfo providerInfo : providerInfos) {
if (TextUtils.equals(providerInfo.authority, name)) {
return providerInfo;
}
}
}
} else {
List<String> pkgs = mActivityManagerService.getPackageNamesByPid(Binder.getCallingPid());
for (PluginPackageParser pluginPackageParser : mPluginCache.values()) {
List<ProviderInfo> providerInfos = pluginPackageParser.getProviders();
for (ProviderInfo providerInfo : providerInfos) {
if (TextUtils.equals(providerInfo.authority, name) && pkgs.contains(providerInfo.packageName)) {
return providerInfo;
}
}
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) throws RemoteException {
boolean success = false;
try {
if (TextUtils.isEmpty(packageName)) {
return;
}
PluginPackageParser parser = mPluginCache.get(packageName);
if (parser == null) {
return;
}
ApplicationInfo applicationInfo = parser.getApplicationInfo(0);
Utils.deleteDir(new File(applicationInfo.dataDir, "caches").getName());
success = true;
} catch (Exception e) {
handleException(e);
} finally {
if (observer != null) {
observer.onRemoveCompleted(packageName, success);
}
}
}
@Override
public void clearApplicationUserData(String packageName, IPackageDataObserver observer) throws RemoteException {
boolean success = false;
try {
if (TextUtils.isEmpty(packageName)) {
return;
}
PluginPackageParser parser = mPluginCache.get(packageName);
if (parser == null) {
return;
}
ApplicationInfo applicationInfo = parser.getApplicationInfo(0);
Utils.deleteDir(applicationInfo.dataDir);
success = true;
} catch (Exception e) {
handleException(e);
} finally {
if (observer != null) {
observer.onRemoveCompleted(packageName, success);
}
}
}
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags) throws RemoteException {
waitForReadyInner();
try {
if (TextUtils.equals(packageName, mContext.getPackageName())) {
return null;
}
PluginPackageParser parser = mPluginCache.get(packageName);
if (parser != null) {
return parser.getApplicationInfo(flags);
}
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public int installPackage(String filepath, int flags) throws RemoteException {
//install plugin
String apkfile = null;
try {
PackageManager pm = mContext.getPackageManager();
PackageInfo info = pm.getPackageArchiveInfo(filepath, 0);
if (info == null) {
return PackageManagerCompat.INSTALL_FAILED_INVALID_APK;
}
apkfile = PluginDirHelper.getPluginApkFile(mContext, info.packageName);
if ((flags & PackageManagerCompat.INSTALL_REPLACE_EXISTING) != 0) {
forceStopPackage(info.packageName);
if (mPluginCache.containsKey(info.packageName)) {
deleteApplicationCacheFiles(info.packageName, null);
}
new File(apkfile).delete();
Utils.copyFile(filepath, apkfile);
PluginPackageParser parser = new PluginPackageParser(mContext, new File(apkfile));
parser.collectCertificates(0);
PackageInfo pkgInfo = parser.getPackageInfo(PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES);
if (pkgInfo != null && pkgInfo.requestedPermissions != null && pkgInfo.requestedPermissions.length > 0) {
for (String requestedPermission : pkgInfo.requestedPermissions) {
boolean b = false;
try {
b = pm.getPermissionInfo(requestedPermission, 0) != null;
} catch (NameNotFoundException e) {
}
if (!mHostRequestedPermission.contains(requestedPermission) && b) {
Log.e(TAG, "No Permission %s", requestedPermission);
new File(apkfile).delete();
return PluginManager.INSTALL_FAILED_NO_REQUESTEDPERMISSION;
}
}
}
saveSignatures(pkgInfo);
// if (pkgInfo.reqFeatures != null && pkgInfo.reqFeatures.length > 0) {
// for (FeatureInfo reqFeature : pkgInfo.reqFeatures) {
// Log.e(TAG, "reqFeature name=%s,flags=%s,glesVersion=%s", reqFeature.name, reqFeature.flags, reqFeature.getGlEsVersion());
// }
// }
if (copyNativeLibs(mContext, apkfile, parser.getApplicationInfo(0)) < 0) {
new File(apkfile).delete();
return PackageManagerCompat.INSTALL_FAILED_NOT_SUPPORT_ABI;
}
dexOpt(mContext, apkfile, parser);
mPluginCache.put(parser.getPackageName(), parser);
mActivityManagerService.onPkgInstalled(mPluginCache, parser, parser.getPackageName());
sendInstalledBroadcast(info.packageName);
return PackageManagerCompat.INSTALL_SUCCEEDED;
} else {
if (mPluginCache.containsKey(info.packageName)) {
return PackageManagerCompat.INSTALL_FAILED_ALREADY_EXISTS;
} else {
forceStopPackage(info.packageName);
new File(apkfile).delete();
Utils.copyFile(filepath, apkfile);
PluginPackageParser parser = new PluginPackageParser(mContext, new File(apkfile));
parser.collectCertificates(0);
PackageInfo pkgInfo = parser.getPackageInfo(PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES);
if (pkgInfo != null && pkgInfo.requestedPermissions != null && pkgInfo.requestedPermissions.length > 0) {
for (String requestedPermission : pkgInfo.requestedPermissions) {
boolean b = false;
try {
b = pm.getPermissionInfo(requestedPermission, 0) != null;
} catch (NameNotFoundException e) {
}
if (!mHostRequestedPermission.contains(requestedPermission) && b) {
Log.e(TAG, "No Permission %s", requestedPermission);
new File(apkfile).delete();
return PluginManager.INSTALL_FAILED_NO_REQUESTEDPERMISSION;
}
}
}
saveSignatures(pkgInfo);
// if (pkgInfo.reqFeatures != null && pkgInfo.reqFeatures.length > 0) {
// for (FeatureInfo reqFeature : pkgInfo.reqFeatures) {
// Log.e(TAG, "reqFeature name=%s,flags=%s,glesVersion=%s", reqFeature.name, reqFeature.flags, reqFeature.getGlEsVersion());
// }
// }
if (copyNativeLibs(mContext, apkfile, parser.getApplicationInfo(0)) < 0) {
new File(apkfile).delete();
return PackageManagerCompat.INSTALL_FAILED_NOT_SUPPORT_ABI;
}
dexOpt(mContext, apkfile, parser);
mPluginCache.put(parser.getPackageName(), parser);
mActivityManagerService.onPkgInstalled(mPluginCache, parser, parser.getPackageName());
sendInstalledBroadcast(info.packageName);
return PackageManagerCompat.INSTALL_SUCCEEDED;
}
}
} catch (Exception e) {
if (apkfile != null) {
new File(apkfile).delete();
}
handleException(e);
return PackageManagerCompat.INSTALL_FAILED_INTERNAL_ERROR;
}
}
private void dexOpt(Context hostContext, String apkfile, PluginPackageParser parser) throws Exception {
String packageName = parser.getPackageName();
String optimizedDirectory = PluginDirHelper.getPluginDalvikCacheDir(hostContext, packageName);
String libraryPath = PluginDirHelper.getPluginNativeLibraryDir(hostContext, packageName);
ClassLoader classloader = new PluginClassLoader(apkfile, optimizedDirectory, libraryPath,hostContext.getClassLoader().getParent());
// DexFile dexFile = DexFile.loadDex(apkfile, PluginDirHelper.getPluginDalvikCacheFile(mContext, parser.getPackageName()), 0);
// Log.e(TAG, "dexFile=%s,1=%s,2=%s", dexFile, DexFile.isDexOptNeeded(apkfile), DexFile.isDexOptNeeded(PluginDirHelper.getPluginDalvikCacheFile(mContext, parser.getPackageName())));
}
private void saveSignatures(PackageInfo pkgInfo) {
if (pkgInfo != null && pkgInfo.signatures != null) {
int i = 0;
for (Signature signature : pkgInfo.signatures) {
File file = new File(PluginDirHelper.getPluginSignatureFile(mContext, pkgInfo.packageName, i));
try {
Utils.writeToFile(file, signature.toByteArray());
Log.i(TAG, "Save %s signature of %s,md5=%s", pkgInfo.packageName, i, Utils.md5(signature.toByteArray()));
} catch (Exception e) {
e.printStackTrace();
Log.w(TAG, "Save signatures fail", e);
file.delete();
Utils.deleteDir(PluginDirHelper.getPluginSignatureDir(mContext, pkgInfo.packageName));
break;
}
i++;
}
}
}
private Signature[] readSignatures(String packageName) {
List<String> fils = PluginDirHelper.getPluginSignatureFiles(mContext, packageName);
List<Signature> signatures = new ArrayList<Signature>(fils.size());
int i = 0;
for (String file : fils) {
try {
byte[] data = Utils.readFromFile(new File(file));
if (data != null) {
Signature sin = new Signature(data);
signatures.add(sin);
Log.i(TAG, "Read %s signature of %s,md5=%s", packageName, i, Utils.md5(sin.toByteArray()));
} else {
Log.i(TAG, "Read %s signature of %s FAIL", packageName, i);
return null;
}
i++;
} catch (Exception e) {
Log.i(TAG, "Read %s signature of %s FAIL", e, packageName, i);
return null;
}
}
return signatures.toArray(new Signature[signatures.size()]);
}
private void sendInstalledBroadcast(String packageName) {
Intent intent = new Intent(PluginManager.ACTION_PACKAGE_ADDED);
intent.setData(Uri.parse("package://" + packageName));
mContext.sendBroadcast(intent);
}
private void sendUninstalledBroadcast(String packageName) {
Intent intent = new Intent(PluginManager.ACTION_PACKAGE_REMOVED);
intent.setData(Uri.parse("package://" + packageName));
mContext.sendBroadcast(intent);
}
private int copyNativeLibs(Context context, String apkfile, ApplicationInfo applicationInfo) throws Exception {
String nativeLibraryDir = PluginDirHelper.getPluginNativeLibraryDir(context, applicationInfo.packageName);
return NativeLibraryHelperCompat.copyNativeBinaries(new File(apkfile), new File(nativeLibraryDir));
}
@Override
public int deletePackage(String packageName, int flags) throws RemoteException {
try {
if (mPluginCache.containsKey(packageName)) {
forceStopPackage(packageName);
PluginPackageParser parser;
synchronized (mPluginCache) {
parser = mPluginCache.remove(packageName);
}
Utils.deleteDir(PluginDirHelper.makePluginBaseDir(mContext, packageName));
mActivityManagerService.onPkgDeleted(mPluginCache, parser, packageName);
mSignatureCache.remove(packageName);
sendUninstalledBroadcast(packageName);
return PackageManagerCompat.DELETE_SUCCEEDED;
}
} catch (Exception e) {
handleException(e);
}
return PackageManagerCompat.DELETE_FAILED_INTERNAL_ERROR;
}
@Override
public List<ActivityInfo> getReceivers(String packageName, int flags) throws RemoteException {
try {
String pkg = getAndCheckCallingPkg(packageName);
if (pkg != null) {
PluginPackageParser parser = mPluginCache.get(packageName);
if (parser != null) {
return new ArrayList<ActivityInfo>(parser.getReceivers());
}
}
} catch (Exception e) {
RemoteException remoteException = new RemoteException();
remoteException.setStackTrace(e.getStackTrace());
throw remoteException;
}
return new ArrayList<ActivityInfo>(0);
}
@Override
public List<IntentFilter> getReceiverIntentFilter(ActivityInfo info) throws RemoteException {
try {
String pkg = getAndCheckCallingPkg(info.packageName);
if (pkg != null) {
PluginPackageParser parser = mPluginCache.get(info.packageName);
if (parser != null) {
List<IntentFilter> filters = parser.getReceiverIntentFilter(info);
if (filters != null && filters.size() > 0) {
return new ArrayList<IntentFilter>(filters);
}
}
}
return new ArrayList<IntentFilter>(0);
} catch (Exception e) {
RemoteException remoteException = new RemoteException();
remoteException.setStackTrace(e.getStackTrace());
throw remoteException;
}
}
@Override
public int checkSignatures(String pkg1, String pkg2) throws RemoteException {
PackageManager pm = mContext.getPackageManager();
Signature[] signatures1 = new Signature[0];
try {
signatures1 = getSignature(pkg1, pm);
} catch (NameNotFoundException e) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
Signature[] signatures2 = new Signature[0];
try {
signatures2 = getSignature(pkg2, pm);
} catch (NameNotFoundException e) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
boolean pkg1Signed = signatures1 != null && signatures1.length > 0;
boolean pkg2Signed = signatures2 != null && signatures2.length > 0;
if (!pkg1Signed && !pkg2Signed) {
return PackageManager.SIGNATURE_NEITHER_SIGNED;
} else if (!pkg1Signed && pkg2Signed) {
return PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
} else if (pkg1Signed && !pkg2Signed) {
return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
} else {
if (signatures1.length == signatures2.length) {
for (int i = 0; i < signatures1.length; i++) {
Signature s1 = signatures1[i];
Signature s2 = signatures2[i];
if (!Arrays.equals(s1.toByteArray(), s2.toByteArray())) {
return PackageManager.SIGNATURE_NO_MATCH;
}
}
return PackageManager.SIGNATURE_MATCH;
} else {
return PackageManager.SIGNATURE_NO_MATCH;
}
}
}
private Signature[] getSignature(String pkg, PackageManager pm) throws RemoteException, NameNotFoundException {
PackageInfo info = getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
if (info == null) {
info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
}
if (info == null) {
throw new NameNotFoundException();
}
return info.signatures;
}
//////////////////////////////////////
//
// THIS API FOR ACTIVITY MANAGER
//
//////////////////////////////////////
@Override
public ActivityInfo selectStubActivityInfo(ActivityInfo pluginInfo) throws RemoteException {
return mActivityManagerService.selectStubActivityInfo(Binder.getCallingPid(), Binder.getCallingUid(), pluginInfo);
}
@Override
public ActivityInfo selectStubActivityInfoByIntent(Intent intent) throws RemoteException {
ActivityInfo ai = null;
if (intent.getComponent() != null) {
ai = getActivityInfo(intent.getComponent(), 0);
} else {
ResolveInfo resolveInfo = resolveIntent(intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), 0);
if (resolveInfo != null && resolveInfo.activityInfo != null) {
ai = resolveInfo.activityInfo;
}
}
if (ai != null) {
return selectStubActivityInfo(ai);
}
return null;
}
@Override
public ServiceInfo selectStubServiceInfo(ServiceInfo targetInfo) throws RemoteException {
return mActivityManagerService.selectStubServiceInfo(Binder.getCallingPid(), Binder.getCallingUid(), targetInfo);
}
@Override
public ServiceInfo selectStubServiceInfoByIntent(Intent intent) throws RemoteException {
ServiceInfo ai = null;
if (intent.getComponent() != null) {
ai = getServiceInfo(intent.getComponent(), 0);
} else {
ResolveInfo resolveInfo = resolveIntent(intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), 0);
if (resolveInfo.serviceInfo != null) {
ai = resolveInfo.serviceInfo;
}
}
if (ai != null) {
return selectStubServiceInfo(ai);
}
return null;
}
@Override
public ServiceInfo getTargetServiceInfo(ServiceInfo targetInfo) throws RemoteException {
return mActivityManagerService.getTargetServiceInfo(Binder.getCallingPid(), Binder.getCallingUid(), targetInfo);
}
@Override
public ProviderInfo selectStubProviderInfo(String name) throws RemoteException {
ProviderInfo targetInfo = resolveContentProvider(name, 0);
return mActivityManagerService.selectStubProviderInfo(Binder.getCallingPid(), Binder.getCallingUid(), targetInfo);
}
@Override
public List<String> getPackageNameByPid(int pid) throws RemoteException {
List<String> packageNameByProcessName = mActivityManagerService.getPackageNamesByPid(pid);
if (packageNameByProcessName != null) {
return new ArrayList<String>(packageNameByProcessName);
} else {
return null;
}
}
@Override
public String getProcessNameByPid(int pid) throws RemoteException {
return mActivityManagerService.getProcessNameByPid(pid);
}
@Override
public boolean killBackgroundProcesses(String pluginPackageName) throws RemoteException {
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> infos = am.getRunningAppProcesses();
boolean success = false;
for (RunningAppProcessInfo info : infos) {
if (info.pkgList != null) {
String[] pkgListCopy = Arrays.copyOf(info.pkgList, info.pkgList.length);
Arrays.sort(pkgListCopy);
if (Arrays.binarySearch(pkgListCopy, pluginPackageName) >= 0 && info.pid != android.os.Process.myPid()) {
Log.i(TAG, "killBackgroundProcesses(%s),pkgList=%s,pid=%s", pluginPackageName, Arrays.toString(info.pkgList), info.pid);
android.os.Process.killProcess(info.pid);
success = true;
}
}
}
return success;
}
@Override
public boolean killApplicationProcess(String pluginPackageName) throws RemoteException {
return killBackgroundProcesses(pluginPackageName);
}
@Override
public boolean forceStopPackage(String pluginPackageName) throws RemoteException {
return killBackgroundProcesses(pluginPackageName);
}
@Override
public boolean registerApplicationCallback(IApplicationCallback callback) throws RemoteException {
return mActivityManagerService.registerApplicationCallback(Binder.getCallingPid(), Binder.getCallingUid(), callback);
}
@Override
public boolean unregisterApplicationCallback(IApplicationCallback callback) throws RemoteException {
return mActivityManagerService.unregisterApplicationCallback(Binder.getCallingPid(), Binder.getCallingUid(), callback);
}
@Override
public void onActivityCreated(ActivityInfo stubInfo, ActivityInfo targetInfo) throws RemoteException {
mActivityManagerService.onActivityCreated(Binder.getCallingPid(), Binder.getCallingUid(), stubInfo, targetInfo);
}
@Override
public void onActivityDestory(ActivityInfo stubInfo, ActivityInfo targetInfo) throws RemoteException {
mActivityManagerService.onActivityDestory(Binder.getCallingPid(), Binder.getCallingUid(), stubInfo, targetInfo);
}
@Override
public void onServiceCreated(ServiceInfo stubInfo, ServiceInfo targetInfo) throws RemoteException {
mActivityManagerService.onServiceCreated(Binder.getCallingPid(), Binder.getCallingUid(), stubInfo, targetInfo);
}
@Override
public void onServiceDestory(ServiceInfo stubInfo, ServiceInfo targetInfo) throws RemoteException {
mActivityManagerService.onServiceDestory(Binder.getCallingPid(), Binder.getCallingUid(), stubInfo, targetInfo);
}
@Override
public void onProviderCreated(ProviderInfo stubInfo, ProviderInfo targetInfo) throws RemoteException {
mActivityManagerService.onProviderCreated(Binder.getCallingPid(), Binder.getCallingUid(), stubInfo, targetInfo);
}
@Override
public void reportMyProcessName(String stubProcessName, String targetProcessName, String targetPkg) throws RemoteException {
mActivityManagerService.onReportMyProcessName(Binder.getCallingPid(), Binder.getCallingUid(), stubProcessName, targetProcessName, targetPkg);
}
public void onDestroy() {
mActivityManagerService.onDestory();
}
@Override
public void onActivtyOnNewIntent(ActivityInfo stubInfo, ActivityInfo targetInfo, Intent intent) throws RemoteException {
mActivityManagerService.onActivtyOnNewIntent(Binder.getCallingPid(), Binder.getCallingUid(), stubInfo, targetInfo, intent);
}
@Override
public int getMyPid() {
return android.os.Process.myPid();
}
}