/*
** 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;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import com.morgoo.droidplugin.core.PluginProcessManager;
import com.morgoo.droidplugin.pm.PluginManager;
import com.morgoo.droidplugin.reflect.FieldUtils;
import com.morgoo.droidplugin.reflect.MethodUtils;
import com.morgoo.helper.Log;
import com.morgoo.helper.compat.ActivityThreadCompat;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
/**
* Created by Andy Zhang(zhangyong232@gmail.com) on 2015/5/18.
*/
public class PluginHelper implements ServiceConnection {
private static final String TAG = PluginHelper.class.getSimpleName();
private static PluginHelper sInstance = null;
private PluginHelper() {
}
public static final PluginHelper getInstance() {
if (sInstance == null) {
sInstance = new PluginHelper();
}
return sInstance;
}
public void applicationOnCreate(final Context baseContext) {
mContext = baseContext;
initPlugin(baseContext);
}
private Context mContext;
private void initPlugin(Context baseContext) {
long b = System.currentTimeMillis();
try {
try {
fixMiUiLbeSecurity();
} catch (Throwable e) {
Log.e(TAG, "fixMiUiLbeSecurity has error", e);
}
try {
PluginPatchManager.getInstance().init(baseContext);
PluginProcessManager.installHook(baseContext);
} catch (Throwable e) {
Log.e(TAG, "installHook has error", e);
}
try {
if (PluginProcessManager.isPluginProcess(baseContext)) {
PluginProcessManager.setHookEnable(true);
} else {
PluginProcessManager.setHookEnable(false);
}
} catch (Throwable e) {
Log.e(TAG, "setHookEnable has error", e);
}
try {
PluginManager.getInstance().addServiceConnection(PluginHelper.this);
PluginManager.getInstance().init(baseContext);
} catch (Throwable e) {
Log.e(TAG, "installHook has error", e);
}
} finally {
Log.i(TAG, "Init plugin in process cost %s ms", (System.currentTimeMillis() - b));
}
}
//解决小米JLB22.0 4.1.1系统自带的小米安全中心(lbe.security.miui)广告拦截组件导致的插件白屏问题
private void fixMiUiLbeSecurity() throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//卸载掉LBE安全的ApplicationLoaders.mLoaders钩子
Class ApplicationLoaders = Class.forName("android.app.ApplicationLoaders");
Object applicationLoaders = MethodUtils.invokeStaticMethod(ApplicationLoaders, "getDefault");
Object mLoaders = FieldUtils.readField(applicationLoaders, "mLoaders", true);
if (mLoaders instanceof HashMap) {
HashMap oldValue = ((HashMap) mLoaders);
if ("com.lbe.security.client.ClientContainer$MonitoredLoaderMap".equals(mLoaders.getClass().getName())) {
HashMap value = new HashMap();
value.putAll(oldValue);
FieldUtils.writeField(applicationLoaders, "mLoaders", value, true);
}
}
//卸载掉LBE安全的ActivityThread.mPackages钩子
Object currentActivityThread = ActivityThreadCompat.currentActivityThread();
Object mPackages = FieldUtils.readField(currentActivityThread, "mPackages", true);
if (mPackages instanceof HashMap) {
HashMap oldValue = ((HashMap) mPackages);
if ("com.lbe.security.client.ClientContainer$MonitoredPackageMap".equals(mPackages.getClass().getName())) {
HashMap value = new HashMap();
value.putAll(oldValue);
FieldUtils.writeField(currentActivityThread, "mPackages", value, true);
}
}
//当前已经处在主线程消息队列中的所有消息,找出lbe消息并remove之
if (Looper.getMainLooper() == Looper.myLooper()) {
final MessageQueue queue = Looper.myQueue();
try {
Object mMessages = FieldUtils.readField(queue, "mMessages", true);
if (mMessages instanceof Message) {
findLbeMessageAndRemoveIt((Message) mMessages);
}
Log.e(TAG, "getMainLooper MessageQueue.IdleHandler:" + mMessages);
} catch (Exception e) {
Log.e(TAG, "fixMiUiLbeSecurity:error on remove lbe message", e);
}
}
}
//由于消息队列是一个单向链表,我们递归处理。
//递归当前已经处在主线程消息队列中的所有消息,找出lbe消息并remove之
private void findLbeMessageAndRemoveIt(Message message) {
if (message == null) {
return;
}
Runnable callback = message.getCallback();
if (message.what == 0 && callback != null) {
if (callback.getClass().getName().indexOf("com.lbe.security.client") >= 0) {
message.getTarget().removeCallbacks(callback);
}
}
try {
Object nextObj = FieldUtils.readField(message, "next", true);
if (nextObj != null) {
Message next = (Message) nextObj;
findLbeMessageAndRemoveIt(next);
}
} catch (Exception e) {
Log.e(TAG, "findLbeMessageAndRemoveIt:error on remove lbe message", e);
}
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
PluginProcessManager.setHookEnable(true, true);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
public void applicationAttachBaseContext(Context baseContext) {
MyCrashHandler.getInstance().register(baseContext);
}
}