package com.lody.virtual.client.hook.base; import android.text.TextUtils; import com.lody.virtual.client.hook.utils.HookUtils; import com.lody.virtual.helper.utils.VLog; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; /** * @author Lody * <p> * HookHandler uses Java's {@link Proxy} to create a wrapper for existing services. * <p> * When any method is called on the wrapper, it checks if there is any {@link Hook} registered * and enabled for that method. If so, it calls the startUniformer instead of the wrapped implementation. * <p> * The whole thing is managed by a {@link PatchDelegate} subclass */ @SuppressWarnings("unchecked") public class HookDelegate<T> { private static final String TAG = HookDelegate.class.getSimpleName(); private Map<String, Hook> internalHookTable = new HashMap<String, Hook>(); private T mBaseInterface; private T mProxyInterface; public Map<String, Hook> getAllHooks() { return internalHookTable; } public HookDelegate(T baseInterface, Class<?>... proxyInterfaces) { this.mBaseInterface = baseInterface; if (baseInterface != null) { if (proxyInterfaces == null) { proxyInterfaces = HookUtils.getAllInterface(baseInterface.getClass()); } mProxyInterface = (T) Proxy.newProxyInstance(baseInterface.getClass().getClassLoader(), proxyInterfaces, new HookHandler()); } else { VLog.d(TAG, "Unable to build HookDelegate: %s.", getClass().getName()); } } public HookDelegate(T baseInterface) { this(baseInterface, (Class[]) null); } /** * Copy all hooks from the input HookDelegate. * * @param from the HookDelegate we copy from. */ public void copyHooks(HookDelegate from) { this.internalHookTable.putAll(from.getAllHooks()); } /** * Add a Hook * * @param hook add a Hook */ public Hook addHook(Hook hook) { if (hook != null && !TextUtils.isEmpty(hook.getName())) { if (internalHookTable.containsKey(hook.getName())) { VLog.w(TAG, "The Hook(%s, %s) you added has been in existence.", hook.getName(), hook.getClass().getName()); return hook; } internalHookTable.put(hook.getName(), hook); } return hook; } /** * Remove a startUniformer * * @param hookName The name of target Hook * @return The startUniformer you removed */ public Hook removeHook(String hookName) { return internalHookTable.remove(hookName); } /** * Remove a startUniformer * * @param hook target Hook */ public void removeHook(Hook hook) { if (hook != null) { removeHook(hook.getName()); } } /** * 移除全部Hook */ public void removeAllHook() { internalHookTable.clear(); } /** * Get the startUniformer by its name. * * @param name name of the Hook * @param <H> Type of the Hook * @return target startUniformer */ @SuppressWarnings("unchecked") public <H extends Hook> H getHook(String name) { return (H) internalHookTable.get(name); } /** * @return Proxy interface */ public T getProxyInterface() { return mProxyInterface; } /** * @return Origin Interface */ public T getBaseInterface() { return mBaseInterface; } /** * @return count of the hooks */ public int getHookCount() { return internalHookTable.size(); } private class HookHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Hook hook = getHook(method.getName()); try { if (hook != null && hook.isEnable()) { if (hook.beforeCall(mBaseInterface, method, args)) { Object res = hook.call(mBaseInterface, method, args); res = hook.afterCall(mBaseInterface, method, args, res); return res; } } return method.invoke(mBaseInterface, args); } catch (InvocationTargetException e) { Throwable cause = e.getTargetException(); if (cause != null) { throw cause; } throw e; } } } }