/*
** 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.hook.handle;
import android.content.ComponentName;
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.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import com.morgoo.droidplugin.core.Env;
import com.morgoo.droidplugin.core.PluginProcessManager;
import com.morgoo.droidplugin.hook.proxy.IPackageManagerHook;
import com.morgoo.droidplugin.pm.PluginManager;
import com.morgoo.droidplugin.reflect.FieldUtils;
import com.morgoo.droidplugin.stub.ShortcutProxyActivity;
import com.morgoo.helper.Log;
public class PluginCallback implements Handler.Callback {
private static final String TAG = "PluginCallback";
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING = 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
public static final int REQUEST_THUMBNAIL = 117;
public static final int CONFIGURATION_CHANGED = 118;
public static final int CLEAN_UP_CONTEXT = 119;
public static final int GC_WHEN_IDLE = 120;
public static final int BIND_SERVICE = 121;
public static final int UNBIND_SERVICE = 122;
public static final int DUMP_SERVICE = 123;
public static final int LOW_MEMORY = 124;
public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;
public static final int RELAUNCH_ACTIVITY = 126;
public static final int PROFILER_CONTROL = 127;
public static final int CREATE_BACKUP_AGENT = 128;
public static final int DESTROY_BACKUP_AGENT = 129;
public static final int SUICIDE = 130;
public static final int REMOVE_PROVIDER = 131;
public static final int ENABLE_JIT = 132;
public static final int DISPATCH_PACKAGE_BROADCAST = 133;
public static final int SCHEDULE_CRASH = 134;
public static final int DUMP_HEAP = 135;
public static final int DUMP_ACTIVITY = 136;
public static final int SLEEPING = 137;
public static final int SET_CORE_SETTINGS = 138;
public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;
public static final int TRIM_MEMORY = 140;
public static final int DUMP_PROVIDER = 141;
public static final int UNSTABLE_PROVIDER_DIED = 142;
public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143;
public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
public static final int INSTALL_PROVIDER = 145;
public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
public static final int CANCEL_VISIBLE_BEHIND = 147;
public static final int BACKGROUND_VISIBLE_BEHIND_CHANGED = 148;
public static final int ENTER_ANIMATION_COMPLETE = 149;
String codeToString(int code) {
switch (code) {
case LAUNCH_ACTIVITY:
return "LAUNCH_ACTIVITY";
case PAUSE_ACTIVITY:
return "PAUSE_ACTIVITY";
case PAUSE_ACTIVITY_FINISHING:
return "PAUSE_ACTIVITY_FINISHING";
case STOP_ACTIVITY_SHOW:
return "STOP_ACTIVITY_SHOW";
case STOP_ACTIVITY_HIDE:
return "STOP_ACTIVITY_HIDE";
case SHOW_WINDOW:
return "SHOW_WINDOW";
case HIDE_WINDOW:
return "HIDE_WINDOW";
case RESUME_ACTIVITY:
return "RESUME_ACTIVITY";
case SEND_RESULT:
return "SEND_RESULT";
case DESTROY_ACTIVITY:
return "DESTROY_ACTIVITY";
case BIND_APPLICATION:
return "BIND_APPLICATION";
case EXIT_APPLICATION:
return "EXIT_APPLICATION";
case NEW_INTENT:
return "NEW_INTENT";
case RECEIVER:
return "RECEIVER";
case CREATE_SERVICE:
return "CREATE_SERVICE";
case SERVICE_ARGS:
return "SERVICE_ARGS";
case STOP_SERVICE:
return "STOP_SERVICE";
case CONFIGURATION_CHANGED:
return "CONFIGURATION_CHANGED";
case CLEAN_UP_CONTEXT:
return "CLEAN_UP_CONTEXT";
case GC_WHEN_IDLE:
return "GC_WHEN_IDLE";
case BIND_SERVICE:
return "BIND_SERVICE";
case UNBIND_SERVICE:
return "UNBIND_SERVICE";
case DUMP_SERVICE:
return "DUMP_SERVICE";
case LOW_MEMORY:
return "LOW_MEMORY";
case ACTIVITY_CONFIGURATION_CHANGED:
return "ACTIVITY_CONFIGURATION_CHANGED";
case RELAUNCH_ACTIVITY:
return "RELAUNCH_ACTIVITY";
case PROFILER_CONTROL:
return "PROFILER_CONTROL";
case CREATE_BACKUP_AGENT:
return "CREATE_BACKUP_AGENT";
case DESTROY_BACKUP_AGENT:
return "DESTROY_BACKUP_AGENT";
case SUICIDE:
return "SUICIDE";
case REMOVE_PROVIDER:
return "REMOVE_PROVIDER";
case ENABLE_JIT:
return "ENABLE_JIT";
case DISPATCH_PACKAGE_BROADCAST:
return "DISPATCH_PACKAGE_BROADCAST";
case SCHEDULE_CRASH:
return "SCHEDULE_CRASH";
case DUMP_HEAP:
return "DUMP_HEAP";
case DUMP_ACTIVITY:
return "DUMP_ACTIVITY";
case SLEEPING:
return "SLEEPING";
case SET_CORE_SETTINGS:
return "SET_CORE_SETTINGS";
case UPDATE_PACKAGE_COMPATIBILITY_INFO:
return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
case TRIM_MEMORY:
return "TRIM_MEMORY";
case DUMP_PROVIDER:
return "DUMP_PROVIDER";
case UNSTABLE_PROVIDER_DIED:
return "UNSTABLE_PROVIDER_DIED";
case REQUEST_ASSIST_CONTEXT_EXTRAS:
return "REQUEST_ASSIST_CONTEXT_EXTRAS";
case TRANSLUCENT_CONVERSION_COMPLETE:
return "TRANSLUCENT_CONVERSION_COMPLETE";
case INSTALL_PROVIDER:
return "INSTALL_PROVIDER";
case ON_NEW_ACTIVITY_OPTIONS:
return "ON_NEW_ACTIVITY_OPTIONS";
case CANCEL_VISIBLE_BEHIND:
return "CANCEL_VISIBLE_BEHIND";
case BACKGROUND_VISIBLE_BEHIND_CHANGED:
return "BACKGROUND_VISIBLE_BEHIND_CHANGED";
case ENTER_ANIMATION_COMPLETE:
return "ENTER_ANIMATION_COMPLETE";
}
return Integer.toString(code);
}
private Handler mOldHandle = null;
private Handler.Callback mCallback = null;
private Context mHostContext;
private boolean mEnable = false;
public PluginCallback(Context hostContext, Handler oldHandle, Handler.Callback callback) {
mOldHandle = oldHandle;
mCallback = callback;
mHostContext = hostContext;
}
public void setEnable(boolean enable) {
this.mEnable = enable;
}
public boolean isEnable() {
return mEnable;
}
@Override
public boolean handleMessage(Message msg) {
long b = System.currentTimeMillis();
try {
if (!mEnable) {
return false;
}
if (PluginProcessManager.isPluginProcess(mHostContext)) {
if (!PluginManager.getInstance().isConnected()) {
//这里必须要这么做。如果当前进程是插件进程,并且,还没有绑定上插件管理服务,我们则把消息延迟一段时间再处理。
//这样虽然会降低启动速度,但是可以解决在没绑定服务就启动,会导致的一系列时序问题。
Log.i(TAG, "handleMessage not isConnected post and wait,msg=%s", msg);
mOldHandle.sendMessageDelayed(Message.obtain(msg), 5);
//返回true,告诉下面的handle不要处理了。
return true;
}
}
if (msg.what == LAUNCH_ACTIVITY) {
return handleLaunchActivity(msg);
} /*else if (msg.what == INSTALL_PROVIDER) {
return handleInstallProvider(msg);
} else if (msg.what == CREATE_BACKUP_AGENT) {
//TODO 处理CREATE_BACKUP_AGENT
} else if (msg.what == DESTROY_BACKUP_AGENT) {
//TODO 处理DESTROY_BACKUP_AGENT
} else if (msg.what == CREATE_SERVICE) {
// return handleCreateService(msg);
} else if (msg.what == BIND_SERVICE) {
// return handleBindService(msg);
} else if (msg.what == UNBIND_SERVICE) {
// return handleUnbindService(msg);
} else if (msg.what == SERVICE_ARGS) {
// return handleServiceArgs(msg);
}*/
if (mCallback != null) {
return mCallback.handleMessage(msg);
} else {
return false;
}
} finally {
Log.i(TAG, "handleMessage(%s,%s) cost %s ms", msg.what, codeToString(msg.what), (System.currentTimeMillis() - b));
}
}
// private boolean handleServiceArgs(Message msg) {
// // handleServiceArgs((ServiceArgsData)msg.obj);
// try {
// Object obj = msg.obj;
// Intent intent = (Intent) FieldUtils.readField(obj, "args", true);
// if (intent != null) {
// intent.setExtrasClassLoader(getClass().getClassLoader());
// Intent originPluginIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT);
// if (originPluginIntent != null) {
// FieldUtils.writeDeclaredField(msg.obj, "args", originPluginIntent, true);
// Log.i(TAG, "handleServiceArgs OK");
// } else {
// Log.w(TAG, "handleServiceArgs pluginInfo==null");
// }
// }
//
// } catch (Exception e) {
// Log.e(TAG, "handleServiceArgs", e);
// }
// return false;
// }
//
// private boolean handleUnbindService(Message msg) {
// // handleUnbindService((BindServiceData)msg.obj);
// try {
// Object obj = msg.obj;
// Intent intent = (Intent) FieldUtils.readField(obj, "intent", true);
// intent.setExtrasClassLoader(getClass().getClassLoader());
// Intent originPluginIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT);
// if (originPluginIntent != null) {
// FieldUtils.writeDeclaredField(msg.obj, "intent", originPluginIntent, true);
// Log.i(TAG, "handleUnbindService OK");
// } else {
// Log.w(TAG, "handleUnbindService pluginInfo==null");
// }
// } catch (Exception e) {
// Log.e(TAG, "handleUnbindService", e);
// }
// return false;
// }
//
// private boolean handleBindService(Message msg) {
// // handleBindService((BindServiceData)msg.obj);
// //其实这里什么都不用做的。
// try {
// Object obj = msg.obj;
// Intent intent = (Intent) FieldUtils.readField(obj, "intent", true);
// intent.setExtrasClassLoader(getClass().getClassLoader());
// Intent originPluginIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT);
// if (originPluginIntent != null) {
//
//
// FieldUtils.writeDeclaredField(msg.obj, "intent", originPluginIntent, true);
// Log.i(TAG, "handleBindService OK");
// } else {
// Log.w(TAG, "handleBindService pluginInfo==null");
// }
// } catch (Exception e) {
// Log.e(TAG, "handleBindService", e);
// }
// return false;
// }
//
// private boolean handleCreateService(Message msg) {
// // handleCreateService((CreateServiceData)msg.obj);
// try {
// Object obj = msg.obj;
// ServiceInfo info = (ServiceInfo) FieldUtils.readField(obj, "info", true);
// if (info != null) {
// ServiceInfo newServiceInfo = PluginManager.getInstance().getTargetServiceInfo(info);
// if (newServiceInfo != null) {
// FieldUtils.writeDeclaredField(msg.obj, "info", newServiceInfo, true);
// }
// }
// } catch (Exception e) {
// Log.e(TAG, "handleCreateService", e);
// }
// return false;
// }
private boolean handleLaunchActivity(Message msg) {
try {
Object obj = msg.obj;
Intent stubIntent = (Intent) FieldUtils.readField(obj, "intent");
//ActivityInfo activityInfo = (ActivityInfo) FieldUtils.readField(obj, "activityInfo", true);
stubIntent.setExtrasClassLoader(mHostContext.getClassLoader());
Intent targetIntent = stubIntent.getParcelableExtra(Env.EXTRA_TARGET_INTENT);
// 这里多加一个isNotShortcutProxyActivity的判断,因为ShortcutProxyActivity的很特殊,启动它的时候,
// 也会带上一个EXTRA_TARGET_INTENT的数据,就会导致这里误以为是启动插件Activity,所以这里要先做一个判断。
// 之前ShortcutProxyActivity错误复用了key,但是为了兼容,所以这里就先这么判断吧。
if (targetIntent != null && !isShortcutProxyActivity(stubIntent)) {
IPackageManagerHook.fixContextPackageManager(mHostContext);
ComponentName targetComponentName = targetIntent.resolveActivity(mHostContext.getPackageManager());
ActivityInfo targetActivityInfo = PluginManager.getInstance().getActivityInfo(targetComponentName, 0);
if (targetActivityInfo != null) {
if (targetComponentName != null && targetComponentName.getClassName().startsWith(".")) {
targetIntent.setClassName(targetComponentName.getPackageName(), targetComponentName.getPackageName() + targetComponentName.getClassName());
}
ResolveInfo resolveInfo = mHostContext.getPackageManager().resolveActivity(stubIntent, 0);
ActivityInfo stubActivityInfo = resolveInfo != null ? resolveInfo.activityInfo : null;
if (stubActivityInfo != null) {
PluginManager.getInstance().reportMyProcessName(stubActivityInfo.processName, targetActivityInfo.processName, targetActivityInfo.packageName);
}
PluginProcessManager.preLoadApk(mHostContext, targetActivityInfo);
ClassLoader pluginClassLoader = PluginProcessManager.getPluginClassLoader(targetComponentName.getPackageName());
setIntentClassLoader(targetIntent, pluginClassLoader);
setIntentClassLoader(stubIntent, pluginClassLoader);
boolean success = false;
try {
targetIntent.putExtra(Env.EXTRA_TARGET_INFO, targetActivityInfo);
if (stubActivityInfo != null) {
targetIntent.putExtra(Env.EXTRA_STUB_INFO, stubActivityInfo);
}
success = true;
} catch (Exception e) {
Log.e(TAG, "putExtra 1 fail", e);
}
if (!success && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
try {
ClassLoader oldParent = fixedClassLoader(pluginClassLoader);
targetIntent.putExtras(targetIntent.getExtras());
targetIntent.putExtra(Env.EXTRA_TARGET_INFO, targetActivityInfo);
if (stubActivityInfo != null) {
targetIntent.putExtra(Env.EXTRA_STUB_INFO, stubActivityInfo);
}
fixedClassLoader(oldParent);
success = true;
} catch (Exception e) {
Log.e(TAG, "putExtra 2 fail", e);
}
}
if (!success) {
Intent newTargetIntent = new Intent();
newTargetIntent.setComponent(targetIntent.getComponent());
newTargetIntent.putExtra(Env.EXTRA_TARGET_INFO, targetActivityInfo);
if (stubActivityInfo != null) {
newTargetIntent.putExtra(Env.EXTRA_STUB_INFO, stubActivityInfo);
}
FieldUtils.writeDeclaredField(msg.obj, "intent", newTargetIntent);
} else {
FieldUtils.writeDeclaredField(msg.obj, "intent", targetIntent);
}
FieldUtils.writeDeclaredField(msg.obj, "activityInfo", targetActivityInfo);
Log.i(TAG, "handleLaunchActivity OK");
} else {
Log.e(TAG, "handleLaunchActivity oldInfo==null");
}
} else {
Log.e(TAG, "handleLaunchActivity targetIntent==null");
}
} catch (Exception e) {
Log.e(TAG, "handleLaunchActivity FAIL", e);
}
if (mCallback != null) {
return mCallback.handleMessage(msg);
} else {
return false;
}
}
private boolean isShortcutProxyActivity(Intent targetIntent) {
try {
if (PluginManager.ACTION_SHORTCUT_PROXY.equalsIgnoreCase(targetIntent.getAction())) {
return true;
}
PackageManager pm = mHostContext.getPackageManager();
ResolveInfo info = pm.resolveActivity(targetIntent, 0);
if (info != null) {
String name = info.activityInfo.name;
if (name != null && name.startsWith(".")) {
name = info.activityInfo.packageName + info.activityInfo.name;
}
return ShortcutProxyActivity.class.getName().equals(name);
}
return false;
} catch (Exception e) {
return false;
}
}
private ClassLoader fixedClassLoader(ClassLoader newParent) {
ClassLoader nowClassLoader = PluginCallback.class.getClassLoader();
ClassLoader oldParent = nowClassLoader.getParent();
try {
if (newParent != null && newParent != oldParent) {
FieldUtils.writeField(nowClassLoader, "parent", newParent);
}
} catch (Exception e) {
e.printStackTrace();
}
return oldParent;
}
private void setIntentClassLoader(Intent intent, ClassLoader classLoader) {
try {
Bundle mExtras = (Bundle) FieldUtils.readField(intent, "mExtras");
if (mExtras != null) {
mExtras.setClassLoader(classLoader);
} else {
Bundle value = new Bundle();
value.setClassLoader(classLoader);
FieldUtils.writeField(intent, "mExtras", value);
}
} catch (Exception e) {
} finally {
intent.setExtrasClassLoader(classLoader);
}
}
}