package com.tws.plugin.manager;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import tws.component.log.TwsLog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.text.TextUtils;
import android.util.Base64;
import com.tws.plugin.content.PluginDescriptor;
import com.tws.plugin.core.PluginLoader;
import com.tws.plugin.util.ProcessUtil;
/**
* 插件组件动态绑定到宿主的虚拟stub组件
*/
class PluginStubBinding {
private static final String TAG = "rick_Print:PluginStubBinding";
private static String KEY_SERVICE_MAP_PREFERENCES_NAME = "plugins.serviceMapping";
private static String KEY_SERVICE_MAP_MAP_PREFERENCES_NAME = "plugins.serviceMapping.map";
private static String KEY_MP_SERVICE_MAP_PREFERENCES_NAME = "plugins.mp.serviceMapping";
private static String KEY_MP_SERVICE_MAP_MAP_PREFERENCES_NAME = "plugins.mp.serviceMapping.map";
/**
* key:stub Activity Name value:plugin Activity Name
*/
private static HashMap<String, String> singleTaskActivityMapping = new HashMap<String, String>();
private static HashMap<String, String> singleTopActivityMapping = new HashMap<String, String>();
private static HashMap<String, String> singleInstanceActivityMapping = new HashMap<String, String>();
private static String standardActivity = null;
private static String receiver = null;
/**
* key:stub Service Name value:plugin Service Name
*/
private static HashMap<String, String> serviceMapping = new HashMap<String, String>();
private static HashMap<String, String> mpServiceMapping = new HashMap<String, String>();
private static Set<String> mExcatStubSet;
private static boolean isPoolInited = false;
private static String buildDefaultAction() {
return PluginLoader.getApplication().getPackageName() + ".STUB_DEFAULT";
}
private static String buildMpDefaultAction() {
return PluginLoader.getApplication().getPackageName() + ".MP_STUB_DEFAULT";
}
private static String buildExactAction() {
return PluginLoader.getApplication().getPackageName() + ".STUB_EXACT";
}
private static void initPool() {
if (!ProcessUtil.isPluginProcess()) {
throw new IllegalAccessError("此类只能在插件所在进程使用");
}
if (isPoolInited) {
return;
}
loadStubActivity();
loadStubService();
loadMpStubService();
loadStubExactly();
loadStubReceiver();
isPoolInited = true;
}
private static void loadStubActivity() {
Intent launchModeIntent = new Intent();
launchModeIntent.setAction(buildDefaultAction());
launchModeIntent.setPackage(PluginLoader.getApplication().getPackageName());
List<ResolveInfo> list = PluginLoader.getApplication().getPackageManager()
.queryIntentActivities(launchModeIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (list != null && list.size() > 0) {
for (ResolveInfo resolveInfo : list) {
if (resolveInfo.activityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
singleTaskActivityMapping.put(resolveInfo.activityInfo.name, null);
} else if (resolveInfo.activityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
singleTopActivityMapping.put(resolveInfo.activityInfo.name, null);
} else if (resolveInfo.activityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
singleInstanceActivityMapping.put(resolveInfo.activityInfo.name, null);
} else if (resolveInfo.activityInfo.launchMode == ActivityInfo.LAUNCH_MULTIPLE) {
standardActivity = resolveInfo.activityInfo.name;
}
}
}
}
private static synchronized void loadStubService() {
Intent launchModeIntent = new Intent();
launchModeIntent.setAction(buildDefaultAction());
launchModeIntent.setPackage(PluginLoader.getApplication().getPackageName());
List<ResolveInfo> list = PluginLoader.getApplication().getPackageManager()
.queryIntentServices(launchModeIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (list != null && list.size() > 0) {
for (ResolveInfo resolveInfo : list) {
serviceMapping.put(resolveInfo.serviceInfo.name, null);
}
HashMap<String, String> mapping = restore(false);
if (mapping != null) {
Iterator<String> iter = mapping.keySet().iterator();
String key;
while (iter.hasNext()) {
key = iter.next();
if (serviceMapping.containsKey(key)) {
serviceMapping.put(key, mapping.get(key));
} else {
// 我去这个还真的在当前版本被删除了
}
}
// serviceMapping.putAll(mapping);
}
// 只有service需要固化
save(serviceMapping, false);
}
}
private static synchronized void loadMpStubService() {
Intent launchModeIntent = new Intent();
launchModeIntent.setAction(buildMpDefaultAction());
launchModeIntent.setPackage(PluginLoader.getApplication().getPackageName());
List<ResolveInfo> list = PluginLoader.getApplication().getPackageManager()
.queryIntentServices(launchModeIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (list != null && list.size() > 0) {
for (ResolveInfo resolveInfo : list) {
mpServiceMapping.put(resolveInfo.serviceInfo.name, null);
}
HashMap<String, String> mapping = restore(true);
if (mapping != null) {
Iterator<String> iter = mapping.keySet().iterator();
String key;
while (iter.hasNext()) {
key = iter.next();
if (mpServiceMapping.containsKey(key)) {
mpServiceMapping.put(key, mapping.get(key));
} else {
// 我去这个还真的在当前版本被删除了
}
}
// mpServiceMapping.putAll(mapping);
}
// 只有service需要固化
save(mpServiceMapping, true);
}
}
private static void loadStubExactly() {
Intent exactStub = new Intent();
exactStub.setAction(buildExactAction());
exactStub.setPackage(PluginLoader.getApplication().getPackageName());
// 精确匹配的activity
List<ResolveInfo> resolveInfos = PluginLoader.getApplication().getPackageManager()
.queryIntentActivities(exactStub, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfos != null && resolveInfos.size() > 0) {
if (mExcatStubSet == null) {
mExcatStubSet = new HashSet<String>();
}
for (ResolveInfo info : resolveInfos) {
mExcatStubSet.add(info.activityInfo.name);
}
}
// 精确匹配的service
resolveInfos = PluginLoader.getApplication().getPackageManager()
.queryIntentServices(exactStub, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfos != null && resolveInfos.size() > 0) {
if (mExcatStubSet == null) {
mExcatStubSet = new HashSet<String>();
}
for (ResolveInfo info : resolveInfos) {
mExcatStubSet.add(info.serviceInfo.name);
}
}
}
private static void loadStubReceiver() {
Intent exactStub = new Intent();
exactStub.setAction(buildDefaultAction());
exactStub.setPackage(PluginLoader.getApplication().getPackageName());
List<ResolveInfo> resolveInfos = PluginLoader.getApplication().getPackageManager()
.queryBroadcastReceivers(exactStub, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfos != null && resolveInfos.size() > 0) {
receiver = resolveInfos.get(0).activityInfo.name;
}
}
public static String bindStubReceiver() {
initPool();
return receiver;
}
public static synchronized String bindStubActivity(String pluginActivityClassName, int launchMode) {
initPool();
if (isExact(pluginActivityClassName, PluginDescriptor.ACTIVITY)) {
return pluginActivityClassName;
}
HashMap<String, String> bindingMapping = null;
if (launchMode == ActivityInfo.LAUNCH_MULTIPLE) {
return standardActivity;
} else if (launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
bindingMapping = singleTaskActivityMapping;
} else if (launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
bindingMapping = singleTopActivityMapping;
} else if (launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
bindingMapping = singleInstanceActivityMapping;
}
if (bindingMapping != null) {
Iterator<Map.Entry<String, String>> itr = bindingMapping.entrySet().iterator();
String idleStubActivityName = null;
while (itr.hasNext()) {
Map.Entry<String, String> entry = itr.next();
if (entry.getValue() == null) {
if (idleStubActivityName == null) {
idleStubActivityName = entry.getKey();
// 这里找到空闲的stubactivity以后,还需继续遍历,用来检查是否pluginActivityClassName已经绑定过了
}
} else if (pluginActivityClassName.equals(entry.getValue())) {
// 已绑定过,直接返回
return entry.getKey();
}
}
// 没有绑定到StubActivity,而且还有空余的stubActivity,进行绑定
if (idleStubActivityName != null) {
bindingMapping.put(idleStubActivityName, pluginActivityClassName);
return idleStubActivityName;
}
}
return standardActivity;
}
public static boolean isExact(String name, int type) {
initPool();
if (mExcatStubSet != null && mExcatStubSet.size() > 0) {
return mExcatStubSet.contains(name);
}
return false;
}
public static synchronized void unBindLaunchModeStubActivity(String stubActivityName, String pluginActivityName) {
TwsLog.d(TAG, "call unBindLaunchModeStubActivity:" + stubActivityName + " pluginActivityName is "
+ pluginActivityName);
if (pluginActivityName.equals(singleTaskActivityMapping.get(stubActivityName))) {
TwsLog.d(TAG, "equals singleTaskActivityMapping");
singleTaskActivityMapping.put(stubActivityName, null);
} else if (pluginActivityName.equals(singleInstanceActivityMapping.get(stubActivityName))) {
TwsLog.d(TAG, "equals singleInstanceActivityMapping");
singleInstanceActivityMapping.put(stubActivityName, null);
} else {
TwsLog.d(TAG, "对于standard和singleTop的launchmode,不做处理。");
}
}
public static synchronized String getBindedPluginServiceName(String stubServiceName) {
initPool();
if (isExact(stubServiceName, PluginDescriptor.SERVICE)) {
return stubServiceName;
}
Iterator<Map.Entry<String, String>> itr = serviceMapping.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, String> entry = itr.next();
if (entry.getKey().equals(stubServiceName)) {
return entry.getValue();
}
}
// 没找到尝试MP里面
Iterator<Map.Entry<String, String>> mpItr = mpServiceMapping.entrySet().iterator();
while (mpItr.hasNext()) {
Map.Entry<String, String> entry = mpItr.next();
if (entry.getKey().equals(stubServiceName)) {
return entry.getValue();
}
}
return null;
}
public static synchronized String bindStubService(String pluginServiceClassName, String process) {
initPool();
if (isExact(pluginServiceClassName, PluginDescriptor.SERVICE)) {
return pluginServiceClassName;
}
final boolean isMp = !TextUtils.isEmpty(process);
Iterator<Map.Entry<String, String>> itr = isMp ? mpServiceMapping.entrySet().iterator() : serviceMapping
.entrySet().iterator();
String idleStubServiceName = null;
while (itr.hasNext()) {
Map.Entry<String, String> entry = itr.next();
if (entry.getValue() == null) {
if (idleStubServiceName == null) {
idleStubServiceName = entry.getKey();
// 这里找到空闲的idleStubServiceName以后,还需继续遍历,用来检查是否pluginServiceClassName已经绑定过了
}
} else if (pluginServiceClassName.equals(entry.getValue())) {
// 已经绑定过,直接返回
TwsLog.d(TAG, "已经绑定过:" + entry.getKey() + " pluginServiceClassName is " + pluginServiceClassName);
return entry.getKey();
}
}
// 没有绑定到StubService,而且还有空余的StubService,进行绑定
if (idleStubServiceName != null) {
TwsLog.d(TAG, "添加绑定:" + idleStubServiceName + " pluginServiceClassName is " + pluginServiceClassName);
if (isMp) {
mpServiceMapping.put(idleStubServiceName, pluginServiceClassName);
// 对serviceMapping持久化是因为如果service处于运行状态时app发生了crash,系统会自动恢复之前的service,此时插件映射信息查不到的话会再次crash
save(mpServiceMapping, true);
} else {
serviceMapping.put(idleStubServiceName, pluginServiceClassName);
// 对serviceMapping持久化是因为如果service处于运行状态时app发生了crash,系统会自动恢复之前的service,此时插件映射信息查不到的话会再次crash
save(serviceMapping, false);
}
return idleStubServiceName;
}
// 绑定失败
return null;
}
public static synchronized void unBindStubService(String pluginServiceName) {
Iterator<Map.Entry<String, String>> itr = serviceMapping.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, String> entry = itr.next();
if (pluginServiceName.equals(entry.getValue())) {
// 如果存在绑定关系,解绑
TwsLog.d(TAG, "回收绑定 Key:" + entry.getKey() + " Value:" + entry.getValue());
serviceMapping.put(entry.getKey(), null);
save(serviceMapping, false);
break;
}
}
Iterator<Map.Entry<String, String>> mpItr = mpServiceMapping.entrySet().iterator();
while (mpItr.hasNext()) {
Map.Entry<String, String> entry = mpItr.next();
if (pluginServiceName.equals(entry.getValue())) {
// 如果存在绑定关系,解绑
TwsLog.d(TAG, "回收绑定 Key:" + entry.getKey() + " Value:" + entry.getValue());
mpServiceMapping.put(entry.getKey(), null);
save(mpServiceMapping, false);
break;
}
}
}
public static String dumpServieInfo() {
return serviceMapping.toString();
}
private static boolean save(HashMap<String, String> mapping, boolean isMp) {
ObjectOutputStream objectOutputStream = null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(mapping);
objectOutputStream.flush();
byte[] data = byteArrayOutputStream.toByteArray();
String list = Base64.encodeToString(data, Base64.DEFAULT);
if (isMp) {
PluginLoader.getApplication()
.getSharedPreferences(KEY_MP_SERVICE_MAP_PREFERENCES_NAME, Context.MODE_PRIVATE).edit()
.putString(KEY_MP_SERVICE_MAP_MAP_PREFERENCES_NAME, list).commit();
} else {
PluginLoader.getApplication()
.getSharedPreferences(KEY_SERVICE_MAP_PREFERENCES_NAME, Context.MODE_PRIVATE).edit()
.putString(KEY_SERVICE_MAP_MAP_PREFERENCES_NAME, list).commit();
}
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (objectOutputStream != null) {
try {
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (byteArrayOutputStream != null) {
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
private static HashMap<String, String> restore(boolean isMp) {
String list = isMp ? PluginLoader.getApplication()
.getSharedPreferences(KEY_MP_SERVICE_MAP_PREFERENCES_NAME, Context.MODE_PRIVATE)
.getString(KEY_MP_SERVICE_MAP_MAP_PREFERENCES_NAME, "") : PluginLoader.getApplication()
.getSharedPreferences(KEY_SERVICE_MAP_PREFERENCES_NAME, Context.MODE_PRIVATE)
.getString(KEY_SERVICE_MAP_MAP_PREFERENCES_NAME, "");
Serializable object = null;
if (!TextUtils.isEmpty(list)) {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.decode(list, Base64.DEFAULT));
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(byteArrayInputStream);
object = (Serializable) objectInputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (byteArrayInputStream != null) {
try {
byteArrayInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
if (object != null) {
HashMap<String, String> mapping = (HashMap<String, String>) object;
return mapping;
}
return null;
}
public static boolean isStub(String className) {
initPool();
return isExact(className, PluginDescriptor.ACTIVITY) || className.equals(standardActivity)
|| singleTaskActivityMapping.containsKey(className) || singleTopActivityMapping.containsKey(className)
|| singleInstanceActivityMapping.containsKey(className) || serviceMapping.containsKey(className)
|| mpServiceMapping.containsKey(className) || className.equals(receiver);
}
}